Pular para o conteúdo principal

Imitação de usuário

Imagine Sarah, uma engenheira de suporte na TechCorp, recebe um ticket urgente de Alex, um cliente que não consegue acessar um recurso crítico. Para diagnosticar e resolver o problema de forma eficiente, Sarah precisa ver exatamente o que Alex vê no sistema. É aqui que o recurso de imitação de usuário do Logto é útil.

A imitação de usuário permite que usuários autorizados como Sarah atuem temporariamente em nome de outros usuários como Alex dentro do sistema. Esse recurso poderoso é inestimável para solução de problemas, fornecimento de suporte ao cliente e execução de tarefas administrativas.

Como funciona?

O processo de imitação envolve três etapas principais:

  1. Sarah solicita a imitação através do servidor backend da TechCorp
  2. O servidor da TechCorp obtém um token de sujeito da Logto Management API
  3. O aplicativo de Sarah troca esse token de sujeito por um token de acesso

Vamos ver como Sarah pode usar esse recurso para ajudar Alex.

Etapa 1: Solicitando imitação

Primeiro, o aplicativo de suporte de Sarah precisa solicitar a imitação ao servidor backend da TechCorp.

Solicitação (Aplicativo de Sarah para o servidor da 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": "Investigando problema de acesso ao recurso",
"ticketId": "TECH-1234"
}

Nesta API, o backend deve realizar verificações de autorização adequadas para garantir que Sarah tenha as permissões necessárias para imitar Alex.

Etapa 2: Obtendo um token de sujeito

O servidor da TechCorp, ao validar a solicitação de Sarah, chamará a Management API do Logto para obter um token de sujeito.

Solicitação (Servidor da TechCorp para a 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": "Problema de acesso ao recurso",
"supportEngineerId": "sarah789"
}
}

Resposta (Logto para o servidor da TechCorp)

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

O servidor da TechCorp deve então retornar esse token de sujeito para o aplicativo de Sarah.

Resposta (Servidor da TechCorp para o aplicativo de Sarah)

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

Etapa 3: Trocando o token de sujeito por um token de acesso

Agora, o aplicativo de Sarah troca esse token de sujeito por um token de acesso representando Alex, especificando o recurso onde o token será usado.

Solicitação (Aplicativo de Sarah para o endpoint de token do Logto)

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

Resposta (Logto para o aplicativo de 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"
}

O access_token retornado será vinculado ao recurso especificado, garantindo que ele só possa ser usado com a API de dados do cliente da TechCorp.

Exemplo de uso

Aqui está como Sarah pode usar isso em um aplicativo de suporte 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
): Promise<string> {
try {
// Etapa 1 e 2: Solicitar imitação e obter token de sujeito
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: 'Investigando problema de acesso ao recurso',
ticketId,
}),
}
);

if (!impersonationResponse.ok) {
throw new Error(`Erro HTTP ocorreu. Status: ${impersonationResponse.status}`);
}

const { subjectToken } = (await impersonationResponse.json()) as ImpersonationResponse;

// Etapa 3: Trocar token de sujeito por token de acesso
const tokenExchangeBody = new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
client_id: clientId,
scope: 'openid profile resource.read',
subject_token: subjectToken,
subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
resource: resource,
});

const tokenExchangeResponse = await fetch('https://techcorp.logto.app/oidc/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: tokenExchangeBody,
});

if (!tokenExchangeResponse.ok) {
throw new Error(`Erro HTTP! status: ${tokenExchangeResponse.status}`);
}

const tokenData = (await tokenExchangeResponse.json()) as TokenExchangeResponse;
return tokenData.access_token;
} catch (error) {
console.error('Falha na imitação:', error);
throw error;
}
}

// Sarah usa esta função para imitar Alex
async function performImpersonation(): Promise<void> {
try {
const accessToken = await impersonateUser(
'alex123',
'techcorp_support_app',
'TECH-1234',
'https://api.techcorp.com/customer-data'
);
console.log('Token de acesso de imitação para Alex:', accessToken);
} catch (error) {
console.error('Falha ao realizar imitação:', error);
}
}

// Executar a imitação
void performImpersonation()
nota
  1. O token de sujeito é de curta duração e para uso único.
  2. O token de acesso de imitação não vem com um token de atualização. Sarah precisará repetir esse processo se o token expirar antes de resolver o problema de Alex.
  3. O servidor backend da TechCorp deve implementar verificações de autorização adequadas para garantir que apenas a equipe de suporte autorizada, como Sarah, possa solicitar imitação.

Reivindicação act

Ao usar o fluxo de troca de token para imitação, o token de acesso emitido pode incluir uma reivindicação adicional act (ator). Essa reivindicação representa a identidade da "parte atuante" - em nosso exemplo, Sarah, que está realizando a imitação.

Para incluir a reivindicação act, o aplicativo de Sarah precisa fornecer um actor_token na solicitação de troca de token. Esse token deve ser um token de acesso válido para Sarah com o escopo openid. Veja como incluí-lo na solicitação de troca de token:

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

Se um actor_token for fornecido, o token de acesso resultante conterá uma reivindicação act assim:

{
"aud": "https://api.techcorp.com",
"iss": "https://techcorp.logto.app",
"exp": 1443904177,
"sub": "alex123",
"act": {
"sub": "sarah789"
}
}

Essa reivindicação act indica claramente que Sarah (sarah789) está agindo em nome de Alex (alex123). A reivindicação act pode ser útil para auditoria e rastreamento de ações de imitação.

Personalizando reivindicações de token

Logto permite que você personalize as reivindicações de token para tokens de imitação. Isso pode ser útil para adicionar contexto ou metadados adicionais ao processo de imitação, como o motivo da imitação ou o ticket de suporte associado.

Quando o servidor da TechCorp solicita um token de sujeito da Logto Management API, ele pode incluir um objeto context:

{
"userId": "alex123",
"context": {
"ticketId": "TECH-1234",
"reason": "Problema de acesso ao recurso",
"supportEngineerId": "sarah789"
}
}

Esse contexto pode então ser usado em uma função getCustomJwtClaims() para adicionar reivindicações específicas ao token de acesso final. Aqui está um exemplo de como isso pode ser implementado:

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 {};
};

O token de acesso resultante que Sarah recebe pode parecer assim:

{
"sub": "alex123",
"aud": "https://api.techcorp.com/customer-data",
"impersonation_context": {
"ticket_id": "TECH-1234",
"reason": "Problema de acesso ao recurso",
"support_engineer": "sarah789"
}
// ... outras reivindicações padrão
}

Ao personalizar as reivindicações de token de acesso dessa forma, a TechCorp pode incluir informações valiosas sobre o contexto de imitação, facilitando a auditoria e o entendimento das atividades de imitação em seu sistema.

nota

Tenha cuidado ao adicionar reivindicações personalizadas aos seus tokens. Evite incluir informações sensíveis que possam representar riscos de segurança se o token for interceptado ou vazado. Os JWTs são assinados, mas não criptografados, portanto, as reivindicações são visíveis para qualquer pessoa com acesso ao token.