使用者模擬 (User impersonation)
想像一下,TechCorp 的支援工程師 Sarah 收到來自客戶 Alex 的緊急工單,Alex 無法存取關鍵資源。為了有效診斷並解決問題,Sarah 需要看到與 Alex 在系統中完全相同的畫面。這時,Logto 的使用者模擬功能就派上用場了。
使用者模擬允許像 Sarah 這樣的授權使用者,能在系統內暫時以其他使用者(如 Alex)的身分行動。這項強大功能對於疑難排解、客戶支援與執行管理任務極為有用。
運作方式?
模擬流程包含三個主要步驟:
- Sarah 透過 TechCorp 的後端伺服器請求模擬
- TechCorp 的伺服器向 Logto 的 Management API 取得 subject token
- Sarah 的應用程式將此 subject token 兌換為存取權杖 (access token)
以下說明 Sarah 如何利用此功能協助 Alex。
步驟 1:請求模擬
首先,Sarah 的支援應用程式需向 TechCorp 的後端伺服器提出模擬請求。
請求(Sarah 的應用程式 → TechCorp 伺服器)
POST /api/request-impersonation HTTP/1.1
Host: api.techcorp.com
Authorization: Bearer <Sarah's_access_token>
Content-Type: application/json
{
"userId": "alex123",
"reason": "Investigating resource access issue",
"ticketId": "TECH-1234"
}
在此 API 中,後端應進行適當的授權檢查,確保 Sarah 具備模擬 Alex 的必要權限。
步驟 2:取得 subject token
TechCorp 的伺服器在驗證 Sarah 的請求後,會呼叫 Logto 的 Management API 以取得 subject token。
請求(TechCorp 伺服器 → Logto Management API)
POST /api/subject-tokens HTTP/1.1
Host: techcorp.logto.app
Authorization: Bearer <TechCorp_m2m_access_token>
Content-Type: application/json
{
"userId": "alex123",
"context": {
"ticketId": "TECH-1234",
"reason": "Resource access issue",
"supportEngineerId": "sarah789"
}
}
回應(Logto → TechCorp 伺服器)
{
"subjectToken": "sub_7h32jf8sK3j2",
"expiresIn": 600
}
TechCorp 的伺服器接著將此 subject token 回傳給 Sarah 的應用程式。
回應(TechCorp 伺服器 → Sarah 的應用程式)
{
"subjectToken": "sub_7h32jf8sK3j2",
"expiresIn": 600
}
步驟 3:以 subject token 兌換存取權杖 (access token)
在使用權杖交換 (token exchange) 授權類型前,你需要為你的應用程式啟用此功能:
- 前往 主控台 > 應用程式 並選擇你的應用程式。
- 在應用程式設定中,找到「權杖交換 (Token exchange)」區段。
- 啟用「允許權杖交換 (Allow token exchange)」開關。
出於安全考量,權杖交換預設為停用狀態。如果你未啟用,將會收到「此應用程式不允許權杖交換 (token exchange is not allowed for this application)」的錯誤訊息。
現在,Sarah 的應用程式會將此 subject token 兌換為代表 Alex 的存取權杖 (access token),並指定權杖將用於的資源。
請求(Sarah 的應用程式 → Logto 權杖端點)
對於傳統網頁應用程式或帶有 app secret 的機器對機器應用程式,請在 Authorization 標頭中包含憑證:
POST /oidc/token HTTP/1.1
Host: techcorp.logto.app
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&scope=resource:read
&subject_token=alx_7h32jf8sK3j2
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&resource=https://api.techcorp.com/customer-data
對於單頁應用程式(SPA)或無 app secret 的原生應用程式,請在請求主體中包含 client_id:
POST /oidc/token HTTP/1.1
Host: techcorp.logto.app
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&client_id=techcorp_support_app
&scope=resource:read
&subject_token=alx_7h32jf8sK3j2
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&resource=https://api.techcorp.com/customer-data
回應(Logto → Sarah 的應用程式)
{
"access_token": "eyJhbG...<truncated>",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "resource:read"
}
回傳的 access_token 會綁定於指定資源,確保僅能用於 TechCorp 的 customer data API。
範例用法
以下展示 Sarah 如何在 Node.js 支援應用程式中使用此功能:
interface ImpersonationResponse {
subjectToken: string;
expiresIn: number;
}
interface TokenExchangeResponse {
access_token: string;
issued_token_type: string;
token_type: string;
expires_in: number;
scope: string;
}
async function impersonateUser(
userId: string,
clientId: string,
ticketId: string,
resource: string,
clientSecret?: string // 傳統網頁或機器對機器應用程式需填寫
): Promise<string> {
try {
// 步驟 1 & 2:請求模擬並取得 subject token
const impersonationResponse = await fetch(
'https://api.techcorp.com/api/request-impersonation',
{
method: 'POST',
headers: {
Authorization: "Bearer <Sarah's_access_token>",
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId,
reason: 'Investigating resource access issue',
ticketId,
}),
}
);
if (!impersonationResponse.ok) {
throw new Error(`HTTP error occurred. Status: ${impersonationResponse.status}`);
}
const { subjectToken } = (await impersonationResponse.json()) as ImpersonationResponse;
// 步驟 3:以 subject token 兌換存取權杖 (access token)
// 傳統網頁或 M2M 應用程式請用 Basic auth 並帶入 client secret
// SPA 或原生應用程式請在主體帶入 client_id
const headers: Record<string, string> = {
'Content-Type': 'application/x-www-form-urlencoded',
};
const tokenExchangeBody = new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
scope: 'openid profile resource.read',
subject_token: subjectToken,
subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
resource: resource,
});
if (clientSecret) {
// 機密型 client:使用 Basic auth
headers['Authorization'] =
`Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`;
} else {
// 公開型 client:主體帶入 client_id
tokenExchangeBody.append('client_id', clientId);
}
const tokenExchangeResponse = await fetch('https://techcorp.logto.app/oidc/token', {
method: 'POST',
headers,
body: tokenExchangeBody,
});
if (!tokenExchangeResponse.ok) {
throw new Error(`HTTP error! status: ${tokenExchangeResponse.status}`);
}
const tokenData = (await tokenExchangeResponse.json()) as TokenExchangeResponse;
return tokenData.access_token;
} catch (error) {
console.error('Impersonation failed:', error);
throw error;
}
}
// Sarah 使用此函式模擬 Alex
async function performImpersonation(): Promise<void> {
try {
// 傳統網頁或 M2M 應用程式需傳入 client secret
const accessToken = await impersonateUser(
'alex123',
'techcorp_support_app',
'TECH-1234',
'https://api.techcorp.com/customer-data',
'your-client-secret' // SPA 或原生應用程式可省略
);
console.log('Alex 的模擬存取權杖 (access token):', accessToken);
} catch (error) {
console.error('執行模擬失敗:', error);
}
}
// 執行模擬
void performImpersonation();
- subject token 為短效且僅能使用一次。
- 模擬存取權杖 (access token) 不會附帶 重新整理權杖 (refresh token)。若權杖過期,Sarah 需重複此流程以協助 Alex。
- TechCorp 的後端伺服器必須實作適當的授權檢查,確保僅有授權的支援人員(如 Sarah)能請求模擬。
act 宣告 (claim)
當使用權杖交換流程進行模擬時,發出的存取權杖 (access token) 可包含額外的 act(actor)宣告 (claim)。此宣告代表「執行方」的身分——在本例中即執行模擬的 Sarah。
若要包含 act 宣告,Sarah 的應用程式需在權杖交換請求中提供 actor_token,此權杖應為帶有 openid 權限範圍 (scope) 的有效存取權杖 (access token)。以下為請求範例:
傳統網頁或機器對機器應用程式:
POST /oidc/token HTTP/1.1
Host: techcorp.logto.app
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&scope=resource:read
&subject_token=alx_7h32jf8sK3j2
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=sarah_access_token
&actor_token_type=urn:ietf:params:oauth:token-type:access_token
&resource=https://api.techcorp.com/customer-data
SPA 或原生應用程式則在主體帶入 client_id:
POST /oidc/token HTTP/1.1
Host: techcorp.logto.app
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&client_id=techcorp_support_app
&scope=resource:read
&subject_token=alx_7h32jf8sK3j2
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=sarah_access_token
&actor_token_type=urn:ietf:params:oauth:token-type:access_token
&resource=https://api.techcorp.com/customer-data
若有提供 actor_token,產生的存取權杖 (access token) 會包含如下的 act 宣告 (claim):
{
"aud": "https://api.techcorp.com",
"iss": "https://techcorp.logto.app",
"exp": 1443904177,
"sub": "alex123",
"act": {
"sub": "sarah789"
}
}
此 act 宣告明確指出 Sarah(sarah789)正以 Alex(alex123)的身分行動。act 宣告有助於稽核與追蹤模擬操作。
自訂權杖宣告 (Customizing token claims)
Logto 允許你自訂模擬權杖的宣告 (claims)。這對於在模擬流程中加入額外情境或中繼資料(如模擬原因或支援工單)非常有用。
當 TechCorp 的伺服器向 Logto Management API 請求 subject token 時,可包含 context 物件:
{
"userId": "alex123",
"context": {
"ticketId": "TECH-1234",
"reason": "Resource access issue",
"supportEngineerId": "sarah789"
}
}
此 context 可於 getCustomJwtClaims() 函式中使用,將特定宣告加入最終存取權杖 (access token)。範例如下:
const getCustomJwtClaims = async ({ token, context, environmentVariables }) => {
if (context.grant?.type === 'urn:ietf:params:oauth:grant-type:token-exchange') {
const { ticketId, reason, supportEngineerId } = context.grant.subjectTokenContext;
return {
impersonation_context: {
ticket_id: ticketId,
reason: reason,
support_engineer: supportEngineerId,
},
};
}
return {};
};
Sarah 取得的最終存取權杖 (access token) 可能如下:
{
"sub": "alex123",
"aud": "https://api.techcorp.com/customer-data",
"impersonation_context": {
"ticket_id": "TECH-1234",
"reason": "Resource access issue",
"support_engineer": "sarah789"
}
// ... 其他標準宣告 (claims)
}
透過這種方式自訂存取權杖 (access token) 宣告,TechCorp 可在系統中輕鬆稽核與理解模擬操作的情境資訊。
為權杖新增自訂宣告 (claims) 時請謹慎,避免包含敏感資訊,以免權杖遭攔截或洩漏時造成安全風險。JWT 雖經簽章但未加密,任何取得權杖者皆可檢視內容。
相關資源
什麼是資安與身分管理中的模擬 (impersonation)?AI agent 如何運用?