Skip to content

GrydAuth CRUD Endpoints (V1) ​

Referencia oficial dos endpoints de gestao do modulo Auth (V1).

Escopo ​

Este documento cobre:

  • UsersController (/api/v1/users/*)
  • AdminUsersController (/api/v1/admin/users/*)
  • RolesController (/api/v1/roles/*)
  • PermissionsController (/api/v1/permissions/*)
  • TenantsController (/api/v1/tenants/*)
  • AdminTenantsController (/api/v1/admin/tenants/*)

De/Para frontend (migracao) ​

Endpoints removidos ​

  • GET /api/v1/users/{id}/tenant-access
  • PUT /api/v1/users/{id}/tenant-access
  • PUT /api/v1/admin/users/{id}/tenant-access (nao existe no contrato final; para acrescentar um vinculo sem regravar a matriz, use POST /api/v1/admin/users/{id}/tenants, abaixo)

Endpoints novos ​

  • POST /api/v1/admin/users/{id}/tenants
    • Acrescenta um vinculo tenant/roles ao usuario sem tocar nos demais (aditivo). Feito para a acao em lote "Adicionar a uma organizacao": uma chamada por usuario, independente das outras.

Endpoints alterados ​

  • GET /api/v1/admin/users/{id}
    • Agora retorna UserDto com matriz completa em tenants.
  • PUT /api/v1/admin/users/{id}
    • Contrato unico de atualizacao admin com blocos opcionais.
    • tenants (quando enviado) executa full replace da matriz tenant/roles do usuario.

Endpoints principais para tela admin de usuarios ​

  • POST /api/v1/admin/users
  • GET /api/v1/admin/users
  • GET /api/v1/admin/users/{id}
  • PUT /api/v1/admin/users/{id}
  • POST /api/v1/admin/users/{id}/tenants (acrescenta um vinculo, sem substituir a matriz)
  • GET /api/v1/admin/tenants/{tenantId}/roles (apoio para montar selecao de roles por tenant)

Contrato HTTP ​

  • Base path: /api/v1
  • Sucesso: payload direto (sem envelope Result<T> no body)
  • Erro: ProblemDetails (application/problem+json)

Shape de erro ​

json
{
  "type": "https://gryd.io/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "User not found",
  "instance": "/api/v1/admin/users/550e8400-e29b-41d4-a716-446655440000",
  "traceId": "00-...",
  "code": "USER_NOT_FOUND",
  "errors": ["User not found"]
}

Query params comuns (QueryParameters) ​

  • page (default 1)
  • pageSize (default 20, max 100)
  • sortBy (opcional)
  • sortDirection (Ascending | Descending)
  • search (opcional)
  • includeDeleted (default false)

Filtros das listagens de usuarios (UsersQueryParameters) ​

Valem para GET /api/v1/users e GET /api/v1/admin/users, alem dos params comuns. Todos sao opcionais; ausente = sem filtro. Os filtros sao aplicados antes da contagem, entao totalCount respeita o filtro (a UI conta abas com pageSize=1).

  • isActive (true | false): estado do usuario.
  • roleName (texto): usuarios com esse papel (no tenant do token no trilho escopado; em qualquer tenant ativo no trilho admin sem tenantId).
  • isLockedOut (true | false): bloqueio de conta no instante da requisicao. true = so contas bloqueadas agora (lockoutExpiresAt no futuro); false = so as nao bloqueadas, incluindo as de bloqueio ja expirado. O bloqueio e do usuario (global), nao do vinculo com o tenant.

Os itens das duas listagens trazem isLockedOut (mesma regra do filtro) e lockoutExpiresAt (fim do bloqueio; so tem significado quando isLockedOut = true — um valor no passado e um bloqueio expirado ainda nao limpo).

DTOs principais ​

CreateUserDto ​

json
{
  "email": "novo@empresa.com",
  "password": "SenhaForte123!",
  "isPasswordEncrypted": false,
  "firstName": "Novo",
  "lastName": "Usuario",
  "phoneNumber": "+5511999999999",
  "tenants": [
    {
      "tenantId": "11111111-1111-1111-1111-111111111111",
      "roleIds": [
        "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
      ],
      "isDefault": true
    }
  ],
  "appIds": ["APP_WEB"],
  "isActive": true
}

UpdateUserDto (tenant-scoped) ​

json
{
  "firstName": "Nome",
  "lastName": "Sobrenome",
  "phoneNumber": "+5511999999999",
  "appIds": ["APP_WEB"],
  "isActive": true
}

AdminUpdateUserDto (contrato unico de update admin) ​

json
{
  "firstName": "Nome",
  "lastName": "Sobrenome",
  "phoneNumber": "+5511999999999",
  "appIds": ["APP_WEB"],
  "isActive": true,
  "tenants": [
    {
      "tenantId": "11111111-1111-1111-1111-111111111111",
      "roleIds": [
        "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
      ],
      "isDefault": true
    },
    {
      "tenantId": "22222222-2222-2222-2222-222222222222",
      "roleIds": [],
      "isDefault": false
    }
  ]
}

Regra de tenants no PUT /api/v1/admin/users/{id}:

  • tenants = null (ou omitido): nao altera matriz tenant/roles.
  • tenants = []: remove todos os vinculos tenant/roles do usuario.
  • tenants preenchido: substitui a matriz inteira (full replace).

AdminUserEditDto (retorno de edicao admin) ​

Shape usado por:

  • GET /api/v1/admin/users
  • GET /api/v1/admin/users/{id}
  • PUT /api/v1/admin/users/{id}
  • POST /api/v1/admin/users/{id}/tenants

Campos:

  • id, email, firstName, lastName, phoneNumber, isActive
  • isLockedOut, lockoutExpiresAt
  • createdAt, updatedAt
  • appIds
  • tenants:
    • tenantId, roleIds, isDefault

Obs:

  • POST /api/v1/admin/users ainda retorna UserDto.
  • Fluxo recomendado para front: apos criar, chamar GET /api/v1/admin/users/{id} para hidratar a tela de edicao no contrato unificado.

Admin Users ​

Base route: /api/v1/admin/users

Permissao padrao: admin:system

Regras de autorizacao para rotas cross-tenant (/api/v1/admin/users*):

  • Requer admin:system ou manage:all-users — e nada mais. Desde o ADR 0007 (A1) a permissao responde sozinha pelo alcance cross-tenant: nao ha coluna, cache nem passo de banco a acertar depois de conceder a permissao pela API de roles.
  • Estar nesta rota nao dispensa ter vinculo UserTenant no tenant do proprio token (A2): alcance de dados e identidade de tenant sao perguntas separadas, com portoes separados.
  • Falha na regra de alcance retorna 403 com code = CROSS_TENANT_FORBIDDEN; falha na politica do endpoint retorna 403 com code = MISSING_PERMISSION.
MetodoRotaRequestResponse sucesso
GET/api/v1/admin/usersQuery UsersQueryParameters (isActive, roleName, isLockedOut) + tenantId opcional200 PagedResult<AdminUserEditDto>
POST/api/v1/admin/usersBody CreateUserDto201 UserDto + Location
GET/api/v1/admin/users/{id}Path id200 AdminUserEditDto
PUT/api/v1/admin/users/{id}Path id + Body AdminUpdateUserDto200 AdminUserEditDto
DELETE/api/v1/admin/users/{id}Path id200 vazio
GET/api/v1/admin/users/{id}/tenantsPath id200 UserTenantDto[]
POST/api/v1/admin/users/{id}/tenantsPath id + Body CreateUserTenantAssignmentDto201 AdminUserEditDto + Location

POST /api/v1/admin/users/{id}/tenants (vinculo aditivo) ​

Acrescenta um vinculo tenant/roles ao usuario e devolve o AdminUserEditDto inteiro, ja com o vinculo novo, para o front atualizar a linha sem outra leitura. Os demais vinculos nao mudam. Mesma autorizacao das outras rotas desta secao (admin:system ou manage:all-users).

Body (CreateUserTenantAssignmentDto, o mesmo item de tenants do PUT):

json
{
  "tenantId": "a8c0d5a2-2f7e-4c2e-9d4e-2f0b6f1c7a10",
  "roleIds": ["5f1c8a8e-7b1a-4c7e-8a55-0f4e2d3c9b21"],
  "isDefault": false
}
  • roleIds: obrigatorio, de 1 a 50 ids; ids repetidos contam uma vez.
  • isDefault = true: o vinculo novo vira o padrao e os outros perdem o padrao na mesma gravacao. false nao mexe no padrao atual.

Resposta de sucesso: 201 Created, Location: /api/v1/admin/users/{id}, body AdminUserEditDto.

Erros, na ordem em que sao conferidos:

SituacaocodeStatus
tenantId vazio, roleIds vazio/nulo, id de role vazio, mais de 50 rolesvalidacao (errors)400
usuario nao existe (ou foi excluido)USER_NOT_FOUND404
tenant nao existeTENANT_NOT_FOUND404
role inexistente ou de outro tenant (anti-disclosure: indistinguiveis)ROLE_NOT_FOUND404
roles concedem permissao que o chamador nao tem no tenant do proprio tokenROLE_ASSIGNMENT_ESCALATION_BLOCKED403
usuario ja vinculado ao tenant (vinculo ativo ou desativado)USER_ALREADY_ASSIGNED409
tenant atingiu maxUsersTENANT_CAPACITY_REACHED400

Regras:

  • Idempotente no conflito: repetir a chamada responde 409 USER_ALREADY_ASSIGNED e nao grava nada — os papeis do corpo novo nao sao mesclados ao vinculo existente. Duas chamadas simultaneas para o mesmo par usuario/tenant tambem terminam em um 201 e um 409.
  • Usuario inativo e aceito (a listagem admin mostra inativos). Usuario excluido e 404.
  • Anti-escalonamento: quem tem admin:system concede qualquer role. Quem tem so manage:all-users concede apenas roles cujas permissoes ele mesmo possui no tenant do proprio token; o bloqueio vale para a chamada inteira.
  • Capacidade: o mesmo teste do POST /api/v1/tenants/{id}/users. O status e 400 porque o codigo nao tem sufixo mapeado; trate pelo code.
  • Tenant do tipo grupo: segue a regra do PUT (nao ha restricao).
  • O usuario vinculado perde as sessoes em aberto (o vinculo novo invalida os tokens dele), como em qualquer mudanca de acesso.

Users (tenant-scoped) ​

Base route: /api/v1/users

MetodoRotaPermissaoRequestResponse sucesso
GET/api/v1/usersread:usersQuery UsersQueryParameters (isActive, roleName, isLockedOut)200 PagedResult<UserDto>
GET/api/v1/users/{id}read:usersPath id200 UserDto
GET/api/v1/users/by-email/{email}read:usersPath email200 UserDto
POST/api/v1/userscreate:usersBody CreateUserDto201 UserDto + Location
PUT/api/v1/users/{id}update:usersPath id + Body UpdateUserDto200 UserDto
DELETE/api/v1/users/{id}delete:usersPath id200 vazio
GET/api/v1/users/{userId}/permissionsread:usersPath userId200 string[]
GET/api/v1/users/{userId}/rolesread:usersPath userId200 RoleDto[]
GET/api/v1/users/{userId}/tenantsread:usersPath userId200 UserTenantDto[]
POST/api/v1/users/{id}/activateupdate:usersPath id200 vazio
POST/api/v1/users/{id}/deactivateupdate:usersPath id200 vazio

Admin Tenants ​

Base route: /api/v1/admin/tenants

Permissao: admin:system

MetodoRotaRequestResponse sucesso
GET/api/v1/admin/tenants/summaryQuery search, isActive, type, hierarchyScope, parentTenantId — aceita tambem manage:all-tenants200 TenantsSummaryDto
GET/api/v1/admin/tenants/lookupQuery search, page, pageSize (max 50), ids (max 50), type, hierarchyScope, parentTenantId — aceita tambem manage:all-tenants200 PagedResult<TenantLookupItemDto>
GET/api/v1/admin/tenants/{tenantId}/rolesPath tenantId + Query RolesQueryParameters200 PagedResult<RoleWithUserCountDto>
GET/api/v1/admin/tenants/{tenantId}/roles/lookupPath tenantId + Query search, page, pageSize (max 50), includeSystemRoles, ids (max 50)200 PagedResult<RoleLookupItemDto>

Roles ​

Base route: /api/v1/roles

MetodoRotaPermissaoRequestResponse sucesso
GET/api/v1/rolesread:rolesQuery RolesQueryParameters (inclui isSystemRole)200 PagedResult<RoleWithUserCountDto>
GET/api/v1/roles/summaryread:rolesQuery search200 RolesSummaryDto
GET/api/v1/roles/lookupread:rolesQuery search, page, pageSize (max 50), includeSystemRoles, ids (max 50)200 PagedResult<RoleLookupItemDto>
GET/api/v1/roles/{id}read:rolesPath id200 RoleWithUserCountDto
POST/api/v1/rolescreate:rolesBody CreateRoleDto201 RoleDto + Location
PUT/api/v1/roles/{id}update:rolesPath id + Body UpdateRoleDto200 RoleDto
DELETE/api/v1/roles/{id}delete:rolesPath id200 vazio

UpdateRoleDto agora suporta sincronizacao da lista de permissoes (permissionIds) no proprio PUT /api/v1/roles/{id}:

  • permissionIds = null (ou omitido): nao altera permissoes do role.
  • permissionIds = []: remove todas as permissoes do role.
  • permissionIds preenchido: substitui integralmente a lista (full replace).

Permissions ​

Base route: /api/v1/permissions

MetodoRotaPermissaoRequestResponse sucesso
GET/api/v1/permissionsread:permissionsQuery PermissionsQueryParameters200 PagedResult<PermissionDetailsDto>
GET/api/v1/permissions/catalogread:permissionsQuery PermissionCatalogQueryParameters200 PagedResult<PermissionCatalogGroupDto>
GET/api/v1/permissions/catalog/categoriesread:permissionsQuery PermissionCategorySuggestionsQueryParameters200 PermissionCategorySuggestionResultDto
GET/api/v1/permissions/{id}read:permissionsPath id200 PermissionDetailsDto
GET/api/v1/permissions/by-code/{code}read:permissionsPath code200 PermissionDetailsDto
POST/api/v1/permissionscreate:permissionsBody CreatePermissionDto (code, description, category)201 PermissionDetailsDto + Location
GET/api/v1/permissions/import/templatecreate:permissionsQuery format (Csv/Json) + mode (Permissions/Catalog)200 arquivo (text/csv ou application/json)
POST/api/v1/permissions/importcreate:permissionsBody PermissionImportRequestDto + Query mode, upsert, dryRun200 PermissionImportResultDto
POST/api/v1/permissions/import/filecreate:permissionsMultipart (file) + Query mode, upsert, dryRun200 PermissionImportResultDto
PUT/api/v1/permissions/{id}update:permissionsPath id + Body UpdatePermissionDto (description, category)200 PermissionDetailsDto
DELETE/api/v1/permissions/{id}delete:permissionsPath id200 vazio

Tenants ​

Base route: /api/v1/tenants

MetodoRotaPermissaoRequestResponse sucesso
GET/api/v1/tenantsread:tenantsQuery TenantsQueryParameters200 PagedResult<TenantDto>
GET/api/v1/tenants/summaryread:tenantsQuery search, isActive, type, hierarchyScope, parentTenantId200 TenantsSummaryDto
GET/api/v1/tenants/lookupread:tenantsQuery search, page, pageSize (max 50), ids (max 50), type, hierarchyScope, parentTenantId200 PagedResult<TenantLookupItemDto>
GET/api/v1/tenants/{id}read:tenantsPath id200 TenantDto
POST/api/v1/tenantscreate:tenantsBody CreateTenantDto201 TenantDto + Location
PUT/api/v1/tenants/{id}update:tenantsPath id + Body UpdateTenantDto200 TenantDto
DELETE/api/v1/tenants/{id}delete:tenantsPath id200 vazio

Observacoes frontend ​

  • Guia detalhado de migracao (catalogo de permissoes e breaking changes):
    • docs/modules/auth/frontend-permissions-migration.md
  • Filtros e resumos das listas do admin (hierarchyScope=grouped, userCount, isSystemRole, /roles/summary, /tenants/summary, /admin/tenants/summary):
    • docs/frontend/admin-list-summaries-contract.md
  • Fluxo recomendado de criacao/edicao admin:
    1. POST /api/v1/admin/users para criar.
    2. GET /api/v1/admin/users/{id} para carregar tela de edicao (inclui tenants).
    3. PUT /api/v1/admin/users/{id} para salvar tudo em uma chamada unica.
  • Acao em lote "Adicionar a uma organizacao": uma chamada POST /api/v1/admin/users/{id}/tenants por usuario selecionado (ex.: Promise.allSettled). Conte 201 como vinculado, 409 USER_ALREADY_ASSIGNED como "ja tinha acesso" e o resto como falha. Nunca use o PUT com tenants para isso: ele substitui a matriz e apaga alteracoes concorrentes.
  • Para montar seletor de roles por tenant em tela admin:
    • use GET /api/v1/admin/tenants/{tenantId}/roles/lookup (item { id, name, description }, sem permissoes) e ?ids= para mostrar o nome de roles ja gravadas em uma chamada; contrato em docs/frontend/role-lookup-contract.md. A listagem GET /api/v1/admin/tenants/{tenantId}/roles continua sendo a tela de administracao.
  • Para tela de permissao por funcionalidade:
    • use GET /api/v1/permissions/catalog (lista agrupada por categoria).
    • use GET /api/v1/permissions/catalog/categories (autocomplete de categorias).
    • para carga em massa, use POST /api/v1/permissions/import ou POST /api/v1/permissions/import/file.
  • Trate erros sempre por ProblemDetails (detail, code, errors, traceId).

Erros de autenticacao/autorizacao (regra de consumo) ​

  • 401 (TOKEN_MISSING, TOKEN_INVALID, TOKEN_EXPIRED, TOKEN_REVOKED, SESSION_INVALIDATED): sessao invalida, fluxo de reautenticacao.
  • 403 (FORBIDDEN, MISSING_PERMISSION, TENANT_FORBIDDEN, CROSS_TENANT_FORBIDDEN): usuario autenticado sem permissao/contexto, sem logout automatico.

Released under the MIT License.