feat: add full project - backend, frontend, docker, specs and configs
This commit is contained in:
parent
b77c7d5a01
commit
e6cb06255b
24489 changed files with 61341 additions and 36 deletions
175
.specify/features/005-authentication/contracts/auth-api.md
Normal file
175
.specify/features/005-authentication/contracts/auth-api.md
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# API Contract: Auth Endpoints
|
||||
|
||||
**Prefixo**: `/api/v1/auth`
|
||||
**Blueprint**: `auth_bp` em `backend/app/routes/auth.py`
|
||||
**Content-Type**: `application/json`
|
||||
**Autenticação**: Bearer token via header `Authorization` (onde indicado)
|
||||
|
||||
---
|
||||
|
||||
## POST /api/v1/auth/register
|
||||
|
||||
Cria uma nova conta de cliente. Token de acesso emitido imediatamente na resposta.
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "João Silva",
|
||||
"email": "joao@exemplo.com",
|
||||
"password": "minhasenha123"
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Tipo | Obrigatório | Regras |
|
||||
|-------|------|-------------|--------|
|
||||
| `name` | string | ✅ | 1–150 caracteres |
|
||||
| `email` | string (RFC 5321) | ✅ | Email válido; normalizado para lowercase antes de persistir |
|
||||
| `password` | string | ✅ | Mínimo 8 caracteres |
|
||||
|
||||
### Respostas
|
||||
|
||||
**201 Created**
|
||||
```json
|
||||
{
|
||||
"access_token": "<jwt_string>",
|
||||
"user": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "João Silva",
|
||||
"email": "joao@exemplo.com",
|
||||
"role": "client",
|
||||
"created_at": "2026-04-13T15:00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**409 Conflict** — e-mail já cadastrado
|
||||
```json
|
||||
{ "error": "E-mail já cadastrado." }
|
||||
```
|
||||
|
||||
**422 Unprocessable Entity** — falha de validação Pydantic
|
||||
```json
|
||||
{
|
||||
"error": "Dados inválidos.",
|
||||
"details": [
|
||||
{ "loc": ["body", "password"], "msg": "String should have at least 8 characters", "type": "string_too_short" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## POST /api/v1/auth/login
|
||||
|
||||
Autentica um cliente existente e emite token de acesso.
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "joao@exemplo.com",
|
||||
"password": "minhasenha123"
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Tipo | Obrigatório |
|
||||
|-------|------|-------------|
|
||||
| `email` | string (email válido) | ✅ |
|
||||
| `password` | string | ✅ |
|
||||
|
||||
### Respostas
|
||||
|
||||
**200 OK**
|
||||
```json
|
||||
{
|
||||
"access_token": "<jwt_string>",
|
||||
"user": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "João Silva",
|
||||
"email": "joao@exemplo.com",
|
||||
"role": "client",
|
||||
"created_at": "2026-04-13T15:00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**401 Unauthorized** — e-mail não encontrado **ou** senha incorreta (mesma resposta para não revelar qual campo falhou — FR-007, SC-006)
|
||||
```json
|
||||
{ "error": "Credenciais inválidas." }
|
||||
```
|
||||
|
||||
**422 Unprocessable Entity** — falha de validação Pydantic
|
||||
```json
|
||||
{ "error": "Dados inválidos.", "details": [...] }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GET /api/v1/auth/me
|
||||
|
||||
Retorna dados do usuário autenticado. Requer `Authorization: Bearer <token>`.
|
||||
|
||||
### Headers
|
||||
|
||||
```
|
||||
Authorization: Bearer <jwt_string>
|
||||
```
|
||||
|
||||
### Respostas
|
||||
|
||||
**200 OK**
|
||||
```json
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "João Silva",
|
||||
"email": "joao@exemplo.com",
|
||||
"role": "client",
|
||||
"created_at": "2026-04-13T15:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
**401 Unauthorized** — sem token, token inválido ou token expirado
|
||||
```json
|
||||
{ "error": "Token de acesso inválido ou expirado." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JWT Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"sub": "<uuid-string do ClientUser>",
|
||||
"exp": <unix timestamp — now() + 7 dias>
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| Algorithm | HS256 |
|
||||
| Secret | `JWT_SECRET_KEY` (variável de ambiente) |
|
||||
| TTL | 7 dias (604800 s) |
|
||||
| Claim `sub` | UUID do `ClientUser` como string |
|
||||
|
||||
---
|
||||
|
||||
## Envelope de Erro Padrão
|
||||
|
||||
Todas as respostas de erro seguem o envelope:
|
||||
|
||||
```json
|
||||
{ "error": "<mensagem legível>" }
|
||||
```
|
||||
|
||||
Erros de validação 422 incluem `details` com a lista de erros Pydantic.
|
||||
Respostas de erro **nunca** incluem informações que permitam distinguir e-mail vs. senha incorretos (SC-006).
|
||||
|
||||
---
|
||||
|
||||
## Notas de Segurança
|
||||
|
||||
- Nenhuma resposta inclui `password_hash`
|
||||
- `401` em credenciais inválidas NÃO DEVE indicar qual campo está incorreto
|
||||
- `JWT_SECRET_KEY` NUNCA deve aparecer em logs ou respostas da API
|
||||
- CORS configurado explicitamente em `create_app()` (sem wildcard em produção)
|
||||
Loading…
Add table
Add a link
Reference in a new issue