Visão Geral da Autenticação
A API não possui rotas de login ou de refresh de token — a autenticação é feita inteiramente pelo Auth0. O backend só valida o token que o cliente já obteve do Auth0 (ou uma API Key, para integrações).
O login acontece direto no Auth0 (Universal Login / SDK do frontend). Esta API nunca recebe email/senha — ela só recebe o accessToken já emitido pelo Auth0 e o valida a cada requisição.
Duas estratégias de autenticação
Toda rota (exceto as marcadas como públicas) exige um dos dois headers abaixo. A estratégia é detectada automaticamente pelo StrategyAuthGuard, nesta ordem:
| Ordem | Header | Estratégia | Uso típico |
|---|---|---|---|
| 1 | x-api-key | API Key | Integrações externas/servidor a servidor |
| 2 | Authorization: Bearer {accessToken} | JWT (Auth0) | Frontend / usuários logados |
Se nenhum dos dois headers estiver presente, a resposta é 401 Unauthorized com a mensagem "Credenciais não fornecidas".
JWT (Auth0)
O accessToken é um JWT RS256 assinado pelo Auth0. A validação (Auth0VerifyService) faz, nesta ordem:
- Decodifica o header do token para pegar o
kid(key id). - Busca a chave pública correspondente no JWKS do Auth0 (
https://{AUTH0_DOMAIN}/.well-known/jwks.json), com cache. - Verifica assinatura,
audience(AUTH0_AUDIENCE) eissuer(https://{AUTH0_DOMAIN}/). - Confere se o
jtido token está na blacklist do Redis (auth0:blacklist:jti:{jti}) — se estiver,401com"Token revogado". - Confere se o
subdo usuário está na blacklist do Redis (auth0:blacklist:sub:{sub}) — se estiver,401com"Usuário desativado". - Extrai
accountIdeuserIddas custom claims namespaced pelo Auth0 ({AUTH0_AUDIENCE}/accountId,{AUTH0_AUDIENCE}/userId). SemaccountId,401com"Token sem accountId".
sub (ID do usuário no Auth0, ex: auth0|6a579131...), org_id (tenant/organização), permissions (array de permissões RBAC do Auth0), jti, iat, exp, aud, iss.
Erros possíveis ao validar um JWT:
| Situação | Resposta |
|---|---|
Token sem kid no header, ou não decodificável | 401 — "Token inválido" |
Assinatura, audience ou issuer inválidos, token expirado | 401 — "Credenciais inválidas" |
Token sem claim jti | 401 — "Token sem jti" |
jti na blacklist (logout/revogação) | 401 — "Token revogado" |
sub na blacklist (usuário desativado) | 401 — "Usuário desativado" |
Token sem a claim {audience}/accountId | 401 — "Token sem accountId" |
Revogação de tokens
Não há um endpoint HTTP dedicado para revogar jti/sub — a blacklist é escrita diretamente no Redis por código interno (Auth0VerifyService.revokeToken / revokeUserBySub), tipicamente disparada ao desativar um usuário ou por rotinas administrativas. POST /auth/logout (veja Logout) revoga o refresh token no Auth0, não o accessToken atual — o access token continua válido até expirar ou até algo colocar seu jti/sub na blacklist.
API Key
Alternativa ao JWT para integrações. Enviada no header x-api-key. A chave é hasheada e comparada contra a tabela ApiKeys; o payload de autenticação resultante carrega accountId e o uuid da API Key (sem userId, sub ou permissions de usuário — permissões de API Key são checadas contra as permissões vinculadas à própria chave no banco).
Permissões (@CheckPermissions)
Rotas marcadas com @CheckPermissions('permissao_x', 'permissao_y') exigem que o payload autenticado tenha todas as permissões listadas:
- JWT (Auth0): a checagem é feita contra o array
permissionsjá embutido no token (roles/permissions configuradas no Auth0 RBAC) — não bate no banco nem no Auth0 a cada requisição. - API Key: a checagem consulta as permissões associadas à chave no banco local (
PermissionsService.hasPermissionsApiKey).
Se faltar alguma permissão, a resposta é 403 Forbidden — "Permissões insuficientes".
Rotas públicas
Rotas marcadas com @IsPublic() pulam a autenticação inteiramente (StrategyAuthGuard retorna true sem checar nenhum header). No módulo de laboratórios, por exemplo, nenhuma rota é pública; em /auth, apenas POST /auth/logout é.
Payload disponível nas rotas (@TokenPayloadParam())
Dentro de um controller, use o decorator @TokenPayloadParam() para acessar os dados extraídos do token/API Key:
async minhaRota(@TokenPayloadParam() payload: AuthPayload) {
payload.accountId; // sempre presente
payload.authType; // 'jwt' | 'api_key'
payload.userId; // só JWT
payload.sub; // só JWT
payload.orgId; // só JWT
payload.permissions; // só JWT
payload.apiKeyUuid; // só API Key
}