Skip to content

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)

EndpointAntesAgoraObservacao
POST /api/v1/auth/loginLoginCommandLoginRequestrequestedAppId suportado no body; X-App-Id no header e priorizado
POST /api/v1/auth/social-loginSocialLoginCommandSocialLoginRequestrequestedAppId suportado no body; X-App-Id no header e priorizado
POST /api/v1/auth/refreshRefreshTokenCommandRefreshTokenRequesttenantId opcional
POST /api/v1/auth/switch-tenantSwitchTenantCommandSwitchTenantRequestsem mudanca de payload
POST /api/v1/auth/request-password-resetRequestPasswordResetCommandRequestPasswordResetRequestipAddress e userAgent continuam server-side
POST /api/v1/auth/reset-passwordResetPasswordCommandResetPasswordRequestipAddress e userAgent continuam server-side
POST /api/v1/auth/change-passwordChangePasswordCommandChangePasswordRequestuserId continua extraido do token
POST /api/v1/auth/complete-first-loginCompleteFirstLoginCommandCompleteFirstLoginRequestuserId continua extraido do token
PUT /api/v1/auth/me/preferences/{key}UpsertUserPreferenceCommandUpsertUserPreferenceRequestkey 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

MetodoRotaAuthRequestSucesso
POST/api/v1/auth/loginPublicoBody LoginRequest + opcional header X-App-Id (prioritario)200 AuthenticationResult
POST/api/v1/auth/social-loginPublicoBody SocialLoginRequest + opcional header X-App-Id (prioritario)200 AuthenticationResult
POST/api/v1/auth/refreshPublicoBody RefreshTokenRequest200 AuthenticationResult
POST/api/v1/auth/logoutPublico (idempotente)Sem body204 vazio
POST/api/v1/auth/switch-tenantBearer (global ou tenant token)Body SwitchTenantRequest200 AuthenticationResult
POST/api/v1/auth/request-password-resetPublicoBody RequestPasswordResetRequest200 vazio
POST/api/v1/auth/reset-passwordPublicoBody ResetPasswordRequest200 vazio
GET/api/v1/auth/meBearer-200 UserBasicProfileDto
GET/api/v1/auth/me/completeBearer-200 UserDto
GET/api/v1/auth/test-claimsSem atributo de auth no endpoint-200 ClaimsInfoDto
GET/api/v1/auth/validateBearer (global ou tenant token)-200 TokenValidationDto
POST/api/v1/auth/change-passwordBearerBody ChangePasswordRequest200 vazio
POST/api/v1/auth/complete-first-loginBearer (global ou tenant token)Body CompleteFirstLoginRequest200 AuthenticationResult
GET/api/v1/auth/public-keyPublico-200 PublicKeyDto
GET/api/v1/auth/password-policyPublico-200 PasswordPolicyDto

Erros:

  • ProblemDetails com status conforme code
  • Tipicos: 400, 401, 403, 404, 409, 422

Matriz canonica de erro (frontend)

401 (autenticacao/sessao):

  • TOKEN_MISSING
  • TOKEN_INVALID
  • TOKEN_EXPIRED
  • TOKEN_REVOKED
  • SESSION_INVALIDATED
  • REFRESH_TOKEN_INVALID
  • REFRESH_TOKEN_EXPIRED
  • REFRESH_TOKEN_REVOKED
  • INVALID_CREDENTIALS

403 (autorizacao/permissao/contexto):

  • FORBIDDEN
  • MISSING_PERMISSION
  • TENANT_FORBIDDEN
  • CROSS_TENANT_FORBIDDEN
  • AUTH_RISK_FORBIDDEN (ex.: bloqueio por politica de risco em POST /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.code como fonte primaria para roteamento de UX.

Ajustes obrigatorios no frontend (Zero Trust + sessao)

  1. Fluxo de login:
  • Se POST /api/v1/auth/login retornar 403 com code=AUTH_RISK_FORBIDDEN, tratar como bloqueio de risco (nao executar logout forçado).
  • Se retornar 401 com code=SESSION_INVALIDATED, tratar como sessao invalida e redirecionar para login.
  1. 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.
  1. Correlacao de sessao de seguranca:
  • O backend resolve sessionId por session_id claim (prioritario), depois headers X-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

MetodoRotaAuthRequestSucesso
POST/api/v1/security/clear-stateBearer + admin:systemBody ClearSecurityStateRequest200 ClearSecurityStateResultDto

Request ClearSecurityStateRequest:

json
{
  "ipAddress": "127.0.0.1",
  "userId": "11111111-1111-1111-1111-111111111111"
}

Regras:

  • Pelo menos um entre ipAddress ou userId deve 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.

MetodoRotaRequestSucesso
GET/api/v1/auth/me/preferences-200 UserPreferenceDto[]
GET/api/v1/auth/me/preferences/{key}Path key200 UserPreferenceDto
PUT/api/v1/auth/me/preferences/{key}Path key + Body UpsertUserPreferenceRequest200 UserPreferenceDto
PUT/api/v1/auth/me/preferencesBody UpsertUserPreferencesRequest200 UserPreferenceDto[]
DELETE/api/v1/auth/me/preferences/{key}Path key200 vazio

Erros:

  • GET/DELETE por chave inexistente retornam 404 (ProblemDetails)
  • Demais falhas retornam ProblemDetails conforme code

User Menu Favorites endpoints

Base route: /api/v1/auth/me/menu-favorites

Todos exigem Authorization: Bearer.

MetodoRotaRequestSucesso
GET/api/v1/auth/me/menu-favorites-200 UserMenuFavoriteDto[]
POST/api/v1/auth/me/menu-favoritesBody AddMenuFavoriteRequest201 UserMenuFavoriteDto + Location
DELETE/api/v1/auth/me/menu-favorites/{menuItemId}Path menuItemId200 vazio
PUT/api/v1/auth/me/menu-favorites/reorderBody ReorderMenuFavoritesRequest200 UserMenuFavoriteDto[]

Erros:

  • DELETE de item inexistente retorna 404 (ProblemDetails)
  • Demais falhas retornam ProblemDetails conforme code

Observacoes finais

  • Contrato HTTP V1 mantido (/api/v1).
  • Sucesso sem envelope Result<T>.
  • Para erros, sempre tratar ProblemDetails (detail, code, errors, traceId).

Released under the MIT License.