Appearance
GrydAuth Auth API Reference (V1)
Referencia oficial dos endpoints de autenticacao e configuracoes do usuario no modulo Auth.
Escopo
Este arquivo cobre:
AuthController(/api/v1/auth/*)UserPreferencesController(/api/v1/auth/me/preferences/*)UserMenuFavoritesController(/api/v1/auth/me/menu-favorites/*)
Para endpoints CRUD de entidades (users, roles, permissions, tenants), consulte:
/Users/rogerrayner/Workspace/Workspace_GRYD/backend/Gryd.IO/docs/modules/auth/crud/endpoints.md
De/Para de contratos (frontend)
| Endpoint | Antes | Agora | Observacao |
|---|---|---|---|
POST /api/v1/auth/login | LoginCommand | LoginRequest | requestedAppId suportado no body; X-App-Id no header e priorizado |
POST /api/v1/auth/social-login | SocialLoginCommand | SocialLoginRequest | requestedAppId suportado no body; X-App-Id no header e priorizado |
POST /api/v1/auth/refresh | RefreshTokenCommand | RefreshTokenRequest | tenantId opcional |
POST /api/v1/auth/switch-tenant | SwitchTenantCommand | SwitchTenantRequest | sem mudanca de payload |
POST /api/v1/auth/request-password-reset | RequestPasswordResetCommand | RequestPasswordResetRequest | ipAddress e userAgent continuam server-side |
POST /api/v1/auth/reset-password | ResetPasswordCommand | ResetPasswordRequest | ipAddress e userAgent continuam server-side |
POST /api/v1/auth/change-password | ChangePasswordCommand | ChangePasswordRequest | userId continua extraido do token |
POST /api/v1/auth/complete-first-login | CompleteFirstLoginCommand | CompleteFirstLoginRequest | userId continua extraido do token |
PUT /api/v1/auth/me/preferences/{key} | UpsertUserPreferenceCommand | UpsertUserPreferenceRequest | key continua vindo da rota |
Contrato HTTP (V1)
- Base path:
/api/v1 - Sucesso: payload direto (sem envelope
Result<T>no body HTTP) - Erro:
ProblemDetails(application/problem+json)
Shape de erro
json
{
"type": "https://gryd.io/errors/authentication-error",
"title": "Unauthorized",
"status": 401,
"detail": "Access token expired.",
"instance": "/api/v1/auth/validate",
"traceId": "00-...",
"code": "TOKEN_EXPIRED",
"errors": ["Access token expired."]
}Tipos de response principais
AuthenticationResult
json
{
"token": "eyJ...",
"refreshToken": "eyJ...",
"expiresAt": "2026-02-23T18:00:00Z",
"permissions": ["read:users"],
"isFirstLogin": false,
"mustChangePassword": false,
"daysUntilPasswordExpiration": 30,
"isGlobal": false,
"requiresTenantSelection": false,
"tokenType": "Tenant",
"availableTenants": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Tenant A",
"isDefault": true,
"permissions": ["read:users"],
"domain": "tenant-a"
}
],
"currentTenant": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Tenant A",
"isDefault": true,
"permissions": ["read:users"],
"domain": "tenant-a"
},
"smartAutoSwitched": true
}UserBasicProfileDto
json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "John Doe",
"email": "john@company.com",
"roles": ["Admin"],
"permissions": ["read:users", "update:users"]
}TokenValidationDto
json
{
"isValid": true,
"userId": "550e8400-e29b-41d4-a716-446655440000",
"email": "john@company.com",
"tenantId": "550e8400-e29b-41d4-a716-446655440000",
"expiresAt": "2026-02-23T18:00:00Z"
}PublicKeyDto
json
{
"algorithm": "RSA-OAEP-SHA256",
"publicKey": "-----BEGIN PUBLIC KEY-----...",
"format": "PEM",
"keySizeInBits": 2048,
"keyId": "default"
}PasswordPolicyDto
json
{
"minLength": 8,
"maxLength": 128,
"requireUppercase": true,
"requireLowercase": true,
"requireDigit": true,
"requireSpecialChar": true,
"specialCharacters": "!@#$...",
"enablePasswordExpiration": true,
"expirationDays": 90,
"warnBeforeExpirationDays": 15,
"enablePasswordHistory": true,
"passwordHistoryCount": 5,
"summary": "..."
}ClaimsInfoDto
json
{
"isAuthenticated": true,
"userId": "550e8400-e29b-41d4-a716-446655440000",
"email": "john@company.com",
"tenantId": "550e8400-e29b-41d4-a716-446655440000",
"claims": {
"sub": "550e8400-e29b-41d4-a716-446655440000"
}
}UserPreferenceDto
json
{
"id": "550e8400-e29b-41d4-a716-446655440111",
"key": "theme",
"value": "dark",
"createdAt": "2026-02-23T17:00:00Z",
"updatedAt": "2026-02-23T17:10:00Z"
}UserMenuFavoriteDto
json
{
"id": "550e8400-e29b-41d4-a716-446655440222",
"menuItemId": "dashboard",
"displayOrder": 0,
"createdAt": "2026-02-23T17:00:00Z"
}Modelos de request
LoginRequest
json
{
"email": "john@company.com",
"password": "Password123!",
"isPasswordEncrypted": false,
"preferredTenantId": "550e8400-e29b-41d4-a716-446655440000",
"requestedAppId": "APP_WEB"
}Obs: X-App-Id (header) tem prioridade. Se o header nao for enviado, requestedAppId no body pode ser usado como fallback.
SocialLoginRequest
json
{
"provider": "google",
"accessToken": "oauth-token",
"preferredTenantId": "550e8400-e29b-41d4-a716-446655440000",
"requestedAppId": "APP_WEB"
}Obs: X-App-Id (header) tem prioridade. Se o header nao for enviado, requestedAppId no body pode ser usado como fallback.
RefreshTokenRequest
json
{
"refreshToken": "refresh-token",
"tenantId": "550e8400-e29b-41d4-a716-446655440000"
}tenantId pode ser omitido.
SwitchTenantRequest
json
{
"tenantId": "550e8400-e29b-41d4-a716-446655440000"
}RequestPasswordResetRequest
json
{
"email": "john@company.com",
"baseUrl": "https://app.company.com"
}Obs: ipAddress e userAgent sao capturados no servidor.
ResetPasswordRequest
json
{
"token": "reset-token",
"newPassword": "NewPassword123!",
"confirmPassword": "NewPassword123!",
"isPasswordEncrypted": false
}Obs: ipAddress e userAgent sao capturados no servidor.
ChangePasswordRequest
json
{
"currentPassword": "OldPassword123!",
"newPassword": "NewPassword123!",
"isCurrentPasswordEncrypted": false,
"isNewPasswordEncrypted": false
}Obs: userId e extraido do token no servidor.
CompleteFirstLoginRequest
json
{
"currentPassword": "TempPassword123!",
"newPassword": "NewPassword123!",
"confirmPassword": "NewPassword123!",
"isCurrentPasswordEncrypted": false,
"isNewPasswordEncrypted": false
}Obs: userId e extraido do token no servidor.
UpsertUserPreferenceRequest (PUT por chave)
json
{
"value": "dark"
}Obs: key vem da rota e sobrescreve o body.
UpsertUserPreferencesRequest (bulk)
json
{
"preferences": [
{ "key": "theme", "value": "dark" },
{ "key": "language", "value": "pt-BR" }
]
}AddMenuFavoriteRequest
json
{
"menuItemId": "dashboard"
}ReorderMenuFavoritesRequest
json
{
"orderedMenuItemIds": ["dashboard", "users", "settings"]
}Auth endpoints
Base route: /api/v1/auth
| Metodo | Rota | Auth | Request | Sucesso |
|---|---|---|---|---|
| POST | /api/v1/auth/login | Publico | Body LoginRequest + opcional header X-App-Id (prioritario) | 200 AuthenticationResult |
| POST | /api/v1/auth/social-login | Publico | Body SocialLoginRequest + opcional header X-App-Id (prioritario) | 200 AuthenticationResult |
| POST | /api/v1/auth/refresh | Publico | Body RefreshTokenRequest | 200 AuthenticationResult |
| POST | /api/v1/auth/logout | Publico (idempotente) | Sem body | 204 vazio |
| POST | /api/v1/auth/switch-tenant | Bearer (global ou tenant token) | Body SwitchTenantRequest | 200 AuthenticationResult |
| POST | /api/v1/auth/request-password-reset | Publico | Body RequestPasswordResetRequest | 200 vazio |
| POST | /api/v1/auth/reset-password | Publico | Body ResetPasswordRequest | 200 vazio |
| GET | /api/v1/auth/me | Bearer | - | 200 UserBasicProfileDto |
| GET | /api/v1/auth/me/complete | Bearer | - | 200 UserDto |
| GET | /api/v1/auth/test-claims | Sem atributo de auth no endpoint | - | 200 ClaimsInfoDto |
| GET | /api/v1/auth/validate | Bearer (global ou tenant token) | - | 200 TokenValidationDto |
| POST | /api/v1/auth/change-password | Bearer | Body ChangePasswordRequest | 200 vazio |
| POST | /api/v1/auth/complete-first-login | Bearer (global ou tenant token) | Body CompleteFirstLoginRequest | 200 AuthenticationResult |
| GET | /api/v1/auth/public-key | Publico | - | 200 PublicKeyDto |
| GET | /api/v1/auth/password-policy | Publico | - | 200 PasswordPolicyDto |
Erros:
ProblemDetailscom status conformecode- Tipicos:
400,401,403,404,409,422
Matriz canonica de erro (frontend)
401 (autenticacao/sessao):
TOKEN_MISSINGTOKEN_INVALIDTOKEN_EXPIREDTOKEN_REVOKEDSESSION_INVALIDATEDREFRESH_TOKEN_INVALIDREFRESH_TOKEN_EXPIREDREFRESH_TOKEN_REVOKEDINVALID_CREDENTIALS
403 (autorizacao/permissao/contexto):
FORBIDDENMISSING_PERMISSIONTENANT_FORBIDDENCROSS_TENANT_FORBIDDENAUTH_RISK_FORBIDDEN(ex.: bloqueio por politica de risco emPOST /api/v1/auth/login)
Regra de consumo no frontend:
401: considerar sessao invalida (tentar refresh quando aplicavel; se falhar, logout/login).403: usuario autenticado sem permissao/contexto (nao deslogar automaticamente).- Sempre ler
ProblemDetails.codecomo fonte primaria para roteamento de UX.
Ajustes obrigatorios no frontend (Zero Trust + sessao)
- Fluxo de login:
- Se
POST /api/v1/auth/loginretornar403comcode=AUTH_RISK_FORBIDDEN, tratar como bloqueio de risco (nao executar logout forçado). - Se retornar
401comcode=SESSION_INVALIDATED, tratar como sessao invalida e redirecionar para login.
- Fluxo autenticado (demais endpoints):
401+SESSION_INVALIDATED,TOKEN_EXPIRED,TOKEN_REVOKED,TOKEN_INVALID=> limpar sessao local e iniciar fluxo de reautenticacao.403+MISSING_PERMISSION,FORBIDDEN,TENANT_FORBIDDEN,CROSS_TENANT_FORBIDDEN=> manter sessao e renderizar tela/estado sem acesso.
- Correlacao de sessao de seguranca:
- O backend resolve
sessionIdporsession_idclaim (prioritario), depois headersX-Security-Session-Id/X-Session-Id. - Quando o cliente nao controlar esses headers, o contrato continua funcional via claim/token.
- Se o frontend usar BFF/gateway que manipula sessao, manter envio consistente de
X-Security-Session-Id.
Security endpoints
Base route: /api/v1/security
| Metodo | Rota | Auth | Request | Sucesso |
|---|---|---|---|---|
| POST | /api/v1/security/clear-state | Bearer + admin:system | Body ClearSecurityStateRequest | 200 ClearSecurityStateResultDto |
Request ClearSecurityStateRequest:
json
{
"ipAddress": "127.0.0.1",
"userId": "11111111-1111-1111-1111-111111111111"
}Regras:
- Pelo menos um entre
ipAddressouuserIddeve ser informado. - Endpoint remove somente estado de seguranca relacionado (nao executa flush global).
User Preferences endpoints
Base route: /api/v1/auth/me/preferences
Todos exigem Authorization: Bearer.
| Metodo | Rota | Request | Sucesso |
|---|---|---|---|
| GET | /api/v1/auth/me/preferences | - | 200 UserPreferenceDto[] |
| GET | /api/v1/auth/me/preferences/{key} | Path key | 200 UserPreferenceDto |
| PUT | /api/v1/auth/me/preferences/{key} | Path key + Body UpsertUserPreferenceRequest | 200 UserPreferenceDto |
| PUT | /api/v1/auth/me/preferences | Body UpsertUserPreferencesRequest | 200 UserPreferenceDto[] |
| DELETE | /api/v1/auth/me/preferences/{key} | Path key | 200 vazio |
Erros:
GET/DELETEpor chave inexistente retornam404(ProblemDetails)- Demais falhas retornam
ProblemDetailsconformecode
User Menu Favorites endpoints
Base route: /api/v1/auth/me/menu-favorites
Todos exigem Authorization: Bearer.
| Metodo | Rota | Request | Sucesso |
|---|---|---|---|
| GET | /api/v1/auth/me/menu-favorites | - | 200 UserMenuFavoriteDto[] |
| POST | /api/v1/auth/me/menu-favorites | Body AddMenuFavoriteRequest | 201 UserMenuFavoriteDto + Location |
| DELETE | /api/v1/auth/me/menu-favorites/{menuItemId} | Path menuItemId | 200 vazio |
| PUT | /api/v1/auth/me/menu-favorites/reorder | Body ReorderMenuFavoritesRequest | 200 UserMenuFavoriteDto[] |
Erros:
DELETEde item inexistente retorna404(ProblemDetails)- Demais falhas retornam
ProblemDetailsconformecode
Observacoes finais
- Contrato HTTP V1 mantido (
/api/v1). - Sucesso sem envelope
Result<T>. - Para erros, sempre tratar
ProblemDetails(detail,code,errors,traceId).