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 do 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, após 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 Management API do Logto)

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​

Veja 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). Esta 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. Este 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"
}
}

Esta 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"
}
}

Este contexto pode então ser usado em uma função getCustomJwtClaims() para adicionar reivindicações específicas ao token de acesso final. Veja 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.