Auth completo (Argon2id + JWT/refresh rotativo), permissões, Companies e OpenAPI
- Auth (apps/api/src/auth/): Argon2id com parâmetros versionados
(ADR-0005), access token JWT de 15min, refresh token opaco de 256 bits
com rotação e detecção de replay (reuso de token já rotacionado revoga
toda a cadeia de sessões do usuário). Endpoints /api/v1/auth/
{login,refresh,logout,me}.
- Permissões resource+action+scope (ADR-0004) via PermissionGuard,
resolvidas do banco a cada request (nunca cacheadas no JWT) — mudança
de papel tem efeito imediato. super_admin com bypass hardcoded,
espelhando a trava já existente no banco.
- Companies (migration + CRUD): leitura aberta a qualquer autenticado,
escrita restrita a companies:manage; validação de CNPJ via
packages/domain-shared (dígito verificador).
- SeedService: cria o primeiro super_admin a partir de
EDEN_SUPERADMIN_EMAIL/PASSWORD só quando `users` está vazia.
- OpenAPI/Swagger em /api/docs; ValidationPipe global com
whitelist+forbidNonWhitelisted (proteção contra mass-assignment).
- packages/database: repositórios (roles/users/sessions/audit/companies)
reutilizáveis por api e worker.
Validado manualmente contra a stack real (não só typecheck): login com
senha errada → 401 genérico; refresh rotaciona e invalida o token
anterior; reuso do token antigo → replay detectado, cadeia revogada;
logout idempotente; audit log com a sequência completa de eventos;
CNPJ inválido rejeitado; mass-assignment rejeitado.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
47
apps/api/src/auth/auth.controller.ts
Normal file
47
apps/api/src/auth/auth.controller.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Body, Controller, Get, HttpCode, Post, Req, UseGuards } from "@nestjs/common";
|
||||
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
|
||||
import type { Request } from "express";
|
||||
import { AuthService } from "./auth.service.js";
|
||||
import { LoginDto } from "./dto/login.dto.js";
|
||||
import { RefreshDto } from "./dto/refresh.dto.js";
|
||||
import { JwtAuthGuard } from "./jwt-auth.guard.js";
|
||||
import { CurrentUser } from "./decorators/current-user.decorator.js";
|
||||
import type { EdenAuthenticatedUser } from "./jwt-auth.guard.js";
|
||||
|
||||
function requestContext(req: Request) {
|
||||
return {
|
||||
ipAddress: req.ip ?? null,
|
||||
userAgent: req.headers["user-agent"] ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@ApiTags("auth")
|
||||
@Controller("v1/auth")
|
||||
export class AuthController {
|
||||
constructor(private readonly auth: AuthService) {}
|
||||
|
||||
@Post("login")
|
||||
@HttpCode(200)
|
||||
async login(@Body() dto: LoginDto, @Req() req: Request) {
|
||||
return this.auth.login(dto.email, dto.password, dto.applicationKey, requestContext(req));
|
||||
}
|
||||
|
||||
@Post("refresh")
|
||||
@HttpCode(200)
|
||||
async refresh(@Body() dto: RefreshDto, @Req() req: Request) {
|
||||
return this.auth.refresh(dto.refreshToken, requestContext(req));
|
||||
}
|
||||
|
||||
@Post("logout")
|
||||
@HttpCode(204)
|
||||
async logout(@Body() dto: RefreshDto, @Req() req: Request) {
|
||||
await this.auth.logout(dto.refreshToken, requestContext(req));
|
||||
}
|
||||
|
||||
@Get("me")
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async me(@CurrentUser() user: EdenAuthenticatedUser) {
|
||||
return this.auth.getProfile(user.id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user