跳至主要內容

使用者模擬 (User impersonation)

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

使用者模擬允許像 Sarah 這樣的授權 (Authorization) 使用者,能夠在系統內暫時以其他使用者(如 Alex)的身分行動。這個強大的功能對於疑難排解、客戶支援和執行管理任務非常有價值。

運作方式?

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

  1. Sarah 透過 TechCorp 的後端伺服器請求模擬
  2. TechCorp 的伺服器向 Logto 的 Management API 取得主體 (Subject) 權杖
  3. Sarah 的應用程式將此主體 (Subject) 權杖交換為存取權杖 (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) 權杖

TechCorp 的伺服器在驗證 Sarah 的請求後,會呼叫 Logto 的 Management API 以取得主體 (Subject) 權杖。

請求(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) 權杖回傳給 Sarah 的應用程式。

回應(TechCorp 伺服器 → Sarah 的應用程式)

{
"subjectToken": "sub_7h32jf8sK3j2",
"expiresIn": 600
}

步驟 3:交換主體 (Subject) 權杖取得存取權杖 (Access token)

先決條件:

在使用權杖交換 (token exchange) 授權類型前,你需要為你的應用程式啟用此功能:

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

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

現在,Sarah 的應用程式將此主體 (Subject) 權杖交換為代表 Alex 的存取權杖 (Access token),並指定該權杖將用於哪個資源。

請求(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) 權杖
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) 權杖取得存取權杖 (Access token)
// 傳統 Web 或 M2M 應用程式使用 Basic auth 與 client secret
// SPA 或原生應用程式則在 body 中帶入 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 {
// 公開型用戶端:body 中帶入 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('Failed to perform impersonation:', error);
}
}

// 執行模擬
void performImpersonation();
備註:
  1. 主體 (Subject) 權杖為短效且僅限一次使用。
  2. 模擬存取權杖 (Impersonation access token) 不會附帶 重新整理權杖 (Refresh token)。若權杖過期,Sarah 需重複此流程以協助 Alex。
  3. TechCorp 的後端伺服器必須實作適當的授權 (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 允許你自訂模擬權杖的宣告 (Token claims)。這對於在模擬過程中加入額外情境或中繼資料(如模擬原因或支援工單)很有幫助。

當 TechCorp 的伺服器向 Logto Management API 請求主體 (Subject) 權杖時,可以包含 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) 宣告 (Claims),TechCorp 可在系統中輕鬆稽核與理解模擬行為的情境資訊。

備註:

在為權杖新增自訂宣告 (Claims) 時請謹慎,避免包含敏感資訊,以免權杖遭攔截或外洩時造成安全風險。JWT 雖經簽章但未加密,任何取得權杖的人都能看到宣告內容。

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