From c10de0fdb51fc0280093b4f03a96452f014af295 Mon Sep 17 00:00:00 2001 From: Rafael Alves Lopes Date: Fri, 8 May 2026 17:14:48 -0300 Subject: [PATCH] Delete page "Auth" --- Auth.md | 260 -------------------------------------------------------- 1 file changed, 260 deletions(-) delete mode 100644 Auth.md diff --git a/Auth.md b/Auth.md deleted file mode 100644 index 36ea8c4..0000000 --- a/Auth.md +++ /dev/null @@ -1,260 +0,0 @@ -# Módulo de Autenticação - -## Visão geral - -O módulo `auth` centraliza toda a lógica de autenticação do Omnichannel. Ele suporta múltiplos provedores de identidade e emite JWT próprio da aplicação, independente de qual provedor foi usado. - -Provedores implementados: - -- **LDAP / Active Directory** — login com usuário e senha do AD corporativo -- **Microsoft OAuth (Entra ID)** — login via conta Microsoft com redirect OAuth 2.0 - -A arquitetura foi desenhada para facilitar a adição de novos provedores no futuro. - ---- - -## Estrutura de arquivos - -``` -src/modules/auth/ -├── auth.module.ts # Registro do módulo no NestJS -├── auth.controller.ts # Rotas HTTP -├── auth.service.ts # Fachada — delega para os providers -├── auth.config.ts # Leitura de variáveis de ambiente -├── auth-token.service.ts # Emissão de JWT da aplicação -├── auth.types.ts # Interfaces TypeScript compartilhadas -└── providers/ - ├── ldap-auth.provider.ts # Autenticação LDAP/AD - ├── microsoft-oauth.provider.ts # Autenticação Microsoft OAuth - └── oauth-state.service.ts # Proteção CSRF para OAuth -``` - ---- - -## Rotas disponíveis - -| Método | Rota | Descrição | -|--------|---------------------------------|------------------------------------------------| -| GET | `/auth/config` | Retorna quais provedores estão habilitados | -| POST | `/auth/login` | Login com usuário e senha (LDAP/AD) | -| GET | `/auth/oauth/microsoft/start` | Inicia o fluxo OAuth com a Microsoft | -| GET | `/auth/oauth/microsoft/callback`| Callback que a Microsoft chama após o login | - ---- - -## Variáveis de ambiente - -```env -# Servidor -PORT=3001 -FRONTEND_URL=http://localhost:3000 - -# JWT -JWT_SECRET=uma-chave-longa-e-aleatoria -JWT_EXPIRES_IN=8h - -# Provedores ativos (separados por vírgula) -AUTH_PROVIDERS=ldap - -# LDAP / Active Directory -LDAP_ENABLED=true -LDAP_URL=ldaps://servidor-ad:636 -LDAP_DOMAIN=empresa.com.br -LDAP_USER_DN_TEMPLATE={{username}}@empresa.com.br -LDAP_SEARCH_BASE=DC=empresa,DC=com -LDAP_SEARCH_FILTER=(&(objectClass=user)(sAMAccountName={{username}})) -LDAP_TIMEOUT_MS=5000 - -# Microsoft Entra ID (desabilitado por padrão) -MICROSOFT_ENABLED=false -MICROSOFT_TENANT_ID=common -MICROSOFT_CLIENT_ID= -MICROSOFT_CLIENT_SECRET= -MICROSOFT_REDIRECT_URI=http://localhost:3001/auth/oauth/microsoft/callback -MICROSOFT_SUCCESS_REDIRECT_URL=http://localhost:3000/login -``` - -> `JWT_SECRET` deve ser uma string longa e aleatória. Em produção, nunca use o valor padrão do `.env.development`. - ---- - -## Fluxo LDAP / Active Directory - -``` -Frontend - → POST /auth/login { username, password } - → AuthController - → AuthService.loginWithLdap() - → LdapAuthProvider.authenticate() - → Conecta no servidor AD (LDAP_URL) - → Faz bind com o usuário e senha - → Se o bind falhar: UnauthorizedException - → Busca dados do usuário no diretório (se LDAP_SEARCH_BASE configurado) - → Monta objeto AuthenticatedUser - → AuthTokenService.issueToken() - → Gera JWT assinado com JWT_SECRET - → Retorna { token, user } para o frontend -``` - -O AD apenas valida a identidade. O JWT emitido é da aplicação, não do AD. - ---- - -## Fluxo Microsoft OAuth - -``` -1. Frontend redireciona para GET /auth/oauth/microsoft/start - → Backend gera um state assinado (proteção CSRF) - → Backend redireciona para login.microsoftonline.com - -2. Usuário autentica na Microsoft - -3. Microsoft chama GET /auth/oauth/microsoft/callback?code=...&state=... - → Backend valida o state (assinatura + expiração) - → Backend troca o code por access_token (chamada server-to-server) - → Backend consulta Microsoft Graph /me para obter dados do usuário - → AuthTokenService.issueToken() gera JWT próprio - → Backend redireciona para MICROSOFT_SUCCESS_REDIRECT_URL?token=... - -4. Frontend salva o token e navega para /home -``` - ---- - -## Proteção CSRF com OAuth State - -O `OAuthStateService` protege o fluxo OAuth contra ataques de CSRF. - -**Como funciona:** - -1. No início do fluxo, o backend cria um state: - - Gera um nonce aleatório + timestamp - - Converte para base64url - - Assina com HMAC-SHA256 usando o `JWT_SECRET` - - Formato final: `payload.assinatura` - -2. No callback, o backend verifica: - - O state tem os dois pedaços (`payload.assinatura`) - - A assinatura é válida (recalcula e compara com `timingSafeEqual`) - - O state não expirou (padrão: 10 minutos, configurável via `MICROSOFT_STATE_MAX_AGE_MS`) - -Se qualquer verificação falhar, o callback é rejeitado com `400 Bad Request`. - ---- - -## JWT da aplicação - -Após qualquer autenticação bem-sucedida, o `AuthTokenService` emite um JWT com o seguinte payload: - -```json -{ - "sub": "identificador-do-usuario", - "name": "Nome Completo", - "email": "usuario@empresa.com", - "provider": "ldap", - "username": "usuario" -} -``` - -O `sub` é atualmente o email ou identificador externo. Quando houver banco de dados, deve ser substituído pelo ID interno da tabela `users`. - ---- - -## Como adicionar um novo provedor - -1. Crie o arquivo em `src/modules/auth/providers/novo-provedor.provider.ts`: - -```typescript -import { Injectable } from '@nestjs/common'; -import { AuthConfigService } from '../auth.config'; -import { AuthTokenService } from '../auth-token.service'; -import { AuthResult } from '../auth.types'; - -@Injectable() -export class NovoProvedorProvider { - constructor( - private readonly authConfig: AuthConfigService, - private readonly authToken: AuthTokenService, - ) {} - - async authenticate(/* dados necessários */): Promise { - // 1. Valide as credenciais no provedor externo - // 2. Monte o objeto AuthenticatedUser - // 3. Emita o token com this.authToken.issueToken(user) - // 4. Retorne { token, user } - } -} -``` - -2. Registre o provider em `auth.module.ts`: - -```typescript -providers: [ - AuthConfigService, - AuthService, - AuthTokenService, - LdapAuthProvider, - MicrosoftOAuthProvider, - OAuthStateService, - NovoProvedorProvider, // adicione aqui -], -``` - -3. Injete no `AuthService` e exponha o método necessário: - -```typescript -constructor( - private readonly authConfig: AuthConfigService, - private readonly ldapAuthProvider: LdapAuthProvider, - private readonly microsoftOAuthProvider: MicrosoftOAuthProvider, - private readonly novoProvedorProvider: NovoProvedorProvider, // injete aqui -) {} - -loginComNovoProvedor(dados: any) { - return this.novoProvedorProvider.authenticate(dados); -} -``` - -4. Adicione a rota no `AuthController`. - -5. Se o provedor precisar de configuração, adicione as variáveis no `AuthConfigService` e no `.env`. - ---- - -## Diagnóstico de problemas - -### Login LDAP falha com `UnauthorizedException` - -- Verifique se `LDAP_URL` está acessível a partir do servidor backend -- Confirme que `LDAP_DOMAIN` ou `LDAP_USER_DN_TEMPLATE` está correto -- Teste a conectividade: `ldapsearch -H ldaps://servidor:636 -x` -- Verifique `LDAP_TIMEOUT_MS` — servidores lentos podem estar expirando -- O erro é genérico intencionalmente para não vazar informações. Adicione um `console.log(_error)` temporário no `catch` do `ldap-auth.provider.ts` para ver o erro real - -### Login Microsoft falha com `400 Bad Request` - -- O state expirou (padrão: 10 minutos). Se o usuário demorou muito na tela da Microsoft, repita o fluxo -- Verifique se `MICROSOFT_REDIRECT_URI` no `.env` é idêntico ao cadastrado no Azure App Registration -- Confirme que `MICROSOFT_CLIENT_ID` e `MICROSOFT_CLIENT_SECRET` estão corretos e não expiraram - -### Token inválido no frontend - -- Verifique se `JWT_SECRET` não mudou entre deploys — isso invalida todos os tokens emitidos anteriormente -- Confirme que o frontend está enviando o header `Authorization: Bearer ` - -### `GET /auth/config` retorna os provedores errados - -- Verifique `LDAP_ENABLED` e `MICROSOFT_ENABLED` no `.env` -- Reinicie o servidor — variáveis de ambiente são lidas na inicialização - ---- - -## O que ainda falta para produção - -- [ ] Tabela `users` no banco de dados -- [ ] Tabela `auth_identities` para vincular provedores externos ao usuário interno -- [ ] `sub` do JWT usando ID interno do banco, não email externo -- [ ] Guard NestJS para proteger rotas privadas (`@UseGuards(AuthGuard)`) -- [ ] Roles e permissões -- [ ] Auditoria de login -- [ ] Trocar token na query string por cookie HTTP-only (reduz exposição no browser) \ No newline at end of file