跳至主要內容

使用者模擬 (User impersonation)

想像一下,TechCorp 的支援工程師 Sarah 收到來自客戶 Alex 的緊急工單,Alex 無法存取關鍵資源。為了有效診斷並解決問題,Sarah 需要看到與 Alex 在系統中完全相同的畫面。這時,Logto 的使用者模擬 (User impersonation) 功能就派上用場了。

使用者模擬允許像 Sarah 這樣的授權 (Authorization) 使用者,能夠在系統內暫時以其他使用者(如 Alex)的身分行動。這項強大功能對於疑難排解、客戶支援及執行管理工作極為有用。

運作方式?

模擬流程包含三個主要步驟:

  1. Sarah 透過 TechCorp 的後端伺服器請求模擬
  2. TechCorp 的伺服器向 Logto 的 Management API 取得 subject token
  3. 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,後端應進行適當的授權 (Authorization) 檢查,確保 Sarah 具備模擬 Alex 的必要權限 (Permission)。

步驟 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) 授權類型前,你需要為你的應用程式啟用此功能:

  1. 前往 主控台 > 應用程式 並選擇你的應用程式。
  2. 在應用程式設定中,找到「權杖交換 (Token exchange)」區段。
  3. 啟用「允許權杖交換 (Allow token exchange)」開關。

出於安全考量,權杖交換預設為停用狀態。如果你未啟用,將會收到「此應用程式不允許權杖交換 (token exchange is not allowed for this application)」的錯誤訊息。

現在,Sarah 的應用程式會以此 subject token 兌換代表 Alex 的存取權杖 (Access token),並指定該權杖將用於的資源 (Resource)。

請求(Sarah 的應用程式 → Logto 權杖端點)

對於傳統 Web 應用程式或帶有 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 的客戶資料 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 // 傳統 Web 或機器對機器應用程式需填寫
): 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)
// 傳統 Web 或 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) {
// 機密型用戶端:使用 Basic auth
headers['Authorization'] =
`Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`;
} else {
// 公開型用戶端:主體帶入 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 {
// 傳統 Web 或 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 的模擬存取權杖 (Impersonation access token):', accessToken);
} catch (error) {
console.error('執行模擬失敗:', error);
}
}

// 執行模擬
void performImpersonation();
備註:
  1. subject token 為短效且僅能使用一次。
  2. 模擬存取權杖 (Impersonation access token) 不會附帶 重新整理權杖 (Refresh token)。若權杖過期且問題尚未解決,Sarah 需重複此流程。
  3. TechCorp 的後端伺服器必須實作適當的授權 (Authorization) 檢查,確保僅有授權 (Authorization) 支援人員(如 Sarah)能請求模擬。

act 宣告 (Claim)

使用權杖交換流程進行模擬時,發出的存取權杖 (Access token) 可包含額外的 act(actor)宣告 (Claim)。此宣告代表「行動方」的身分——在本例中即執行模擬的 Sarah。

若要包含 act 宣告,Sarah 的應用程式需在權杖交換請求中提供 actor_token,此權杖需為帶有 openid 權限範圍 (Scope) 的有效存取權杖 (Access token)。以下為請求範例:

傳統 Web 或機器對機器應用程式:

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 宣告 (Claim) 明確指出 Sarah(sarah789)正以 Alex(alex123)的身分行動。act 宣告 (Claim) 對於稽核與追蹤模擬行為非常有用。

自訂權杖宣告 (Customizing token claims)

Logto 支援自訂模擬權杖 (Impersonation token) 的宣告 (Claim)。這對於在模擬過程中加入額外情境或中繼資料(如模擬原因或支援工單)非常有用。

當 TechCorp 的伺服器向 Logto Management API 請求 subject token 時,可帶入 context 物件:

{
"userId": "alex123",
"context": {
"ticketId": "TECH-1234",
"reason": "Resource access issue",
"supportEngineerId": "sarah789"
}
}

context 可在 getCustomJwtClaims() 函式中用來為最終存取權杖 (Access token) 加入特定宣告 (Claim)。範例如下:

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) 宣告 (Claim),TechCorp 可在系統中更容易稽核與理解模擬行為的情境資訊。

備註:

自訂權杖宣告 (Claim) 時請謹慎,避免加入敏感資訊,以免權杖遭攔截或洩漏時造成安全風險。JWT 雖經簽章但未加密,任何取得權杖者皆可檢視內容。

什麼是資安與身分管理中的模擬 (Impersonation)?AI agent 如何運用?