Skip to main content

Inspeção de autorização — API

Quatro rotas de diagnóstico para suporte, automação e telas administrativas: verificar se outro usuário teria uma permissão (sem de fato executar a ação), ver a autorização efetiva completa de um usuário com a origem de cada permissão, e checar se o catálogo persistido está consistente com o catálogo esperado em código.

Funcionamento​

  • POST /tenants/:tenantId/authorization/checks: roda a mesma lógica que o AuthorizationGuard usaria para autorizar uma requisição real (RBAC + política), mas devolve o resultado como dado (allowed, granted_permissions, denied_permissions, reason_code) em vez de deixar passar ou lançar 403. É síncrona e não tem efeito colateral. Qualquer erro interno durante a checagem (claim indisponível, política indisponível) é convertido em allowed: false, reason_code: authorization-unavailable — a rota nunca lança 500 por causa da checagem em si.
  • POST /tenants/:tenantId/authorization/bulk-checks: até 50 checagens no mesmo formato acima, cada uma resolvida independentemente (uma checagem inválida/negada não afeta as outras).
  • GET /tenants/:tenantId/authorization/users/:userId/effective: a visão mais completa — devolve o bitmask do tenant e da unidade (se informada), cada permissão efetiva com a lista de atribuições que a concedem (sources[]: qual papel, qual escopo), os papéis ativos do usuário naquele tenant/unidade e as políticas ativas do tenant. Se o tenant não existe/está inativo, ou a unidade informada não pertence a ele, devolve um resultado vazio (200), não um erro — a rota é pensada para nunca revelar por erro se um tenant/unidade existe.
  • GET /tenants/:tenantId/authorization/consistency: compara o catálogo de permissões e os nove papéis de sistema persistidos contra o que o código espera (PermissionCatalog.assignable() e IdentitySystemRoleCatalog.reconciled()), reportando consistent: true/false por verificação. Serve para detectar bootstrap que não rodou ou dessincronia após um deploy.

Endpoints​

MétodoRotaDescrição
POST/v1/tenants/:tenantId/authorization/checksVerifica se um usuário teria uma ou mais permissões
POST/v1/tenants/:tenantId/authorization/bulk-checksAté 50 verificações numa só chamada
GET/v1/tenants/:tenantId/authorization/users/:userId/effectiveAutorização efetiva completa, com origem de cada permissão
GET/v1/tenants/:tenantId/authorization/consistencyCompara o catálogo persistido com o esperado em código

Versão: v1

Swagger: Identity — Authorization inspection · Rota (Dev): http://localhost:3000/v1/tenants/{tenantId}/authorization/checks

Permissões​

RotaGuardsPermissão exigida (escopo tenant)
POST /checks, POST /bulk-checks, GET /users/:userId/effectiveIdentityOAuthAccessTokenGuard, AuthorizationGuardpermission:read e user:read (match: all)
GET /consistencyidempermission:read e role:read (match: all)

Headers​

HeaderObrigatórioDescrição
AuthorizationSimBearer <access_token>

Path parameters​

NomeTipoObrigatórioDescrição
tenantIduuidSimTenant no qual a checagem/consulta é feita
userIduuidSim, em GET .../effectiveUsuário cuja autorização está sendo inspecionada

Query parameters​

GET /users/:userId/effective:

NomeTipoObrigatórioDescrição
unit_iduuidNãoRestringe o cálculo a uma unidade; ausente = só tenant-wide

Body​

POST /checks — IdentityAuthorizationCheckRequest:

json
{ "user_id": "77aa...uuid", "unit_id": "9b3c...uuid", "permissions": ["exam:read", "exam:write"], "match": "all" }
CampoTipoObrigatórioValidação
user_iduuidSim—
unit_iduuidNãose ausente, checa no escopo do tenant
permissionsstring[]Sim1 a 100 itens, sem duplicatas
matchall | anySim—

POST /bulk-checks — IdentityAuthorizationBulkCheckRequest:

json
{ "checks": [ { "user_id": "77aa...", "permissions": ["exam:read"], "match": "all" } ] }
CampoTipoObrigatórioValidação
checks[]IdentityAuthorizationCheckRequestSim1 a 50 itens

Response​

POST /checks e cada item de /bulk-checks — IdentityAuthorizationCheckResponse:

json
{ "data": { "allowed": true, "granted_permissions": ["exam:read"], "denied_permissions": [], "reason_code": "policy-allowed" } }

GET /users/:userId/effective — 200 (IdentityEffectiveAuthorizationResponse):

json
{
"data": {
"user_id": "77aa...uuid",
"tenant_id": "1f2a...uuid",
"unit_id": "9b3c...uuid",
"tenant_mask": "17",
"unit_masks": { "9b3c...uuid": "17" },
"permissions": [
{
"name": "exam:read",
"resource": "exam",
"action": "read",
"bit_position": 0,
"sources": [ { "assignment_id": "3c9e...uuid", "role_id": "0198f1a2-...", "role_name": "doctor", "scope": "tenant", "unit_id": null } ]
}
],
"roles": [ { "assignment_id": "3c9e...uuid", "role_id": "0198f1a2-...", "name": "doctor", "display_name": "Doctor", "scope": "tenant", "unit_id": null, "expires_at": null } ],
"active_policies": [ { "id": "5e1c...uuid", "name": "deny-read-only-export", "permission_name": "user:export", "effect": "deny", "priority": 100, "version": 1 } ]
}
}

GET /consistency — 200 (IdentityAuthorizationConsistencyResponse):

json
{
"data": {
"consistent": true,
"checks": [
{ "name": "permission-catalog", "consistent": true, "expected": 104, "actual": 104 },
{ "name": "system-role-catalog", "consistent": true, "expected": 9, "actual": 9 }
]
}
}

Mapa de campos​

CampoTipoValores possíveisDescrição
reason_code (checks)stringpolicy-allowed, policy-denied, rbac-denied, claim-unavailable, authorization-unavailableMotivo da decisão simulada
sources[].scopestringtenant, unitDe onde vem a concessão daquela permissão
checks[].name (consistency)stringpermission-catalog, system-role-catalogQual verificação de consistência
checks[].expected / .actualinteger—Contagem esperada (em código) vs. persistida (no banco)

Erros​

Classe de erroerrorCodeStatusQuando ocorre
(validação de payload/UUID)BAD_REQUEST / INVALID_UUID400corpo ou tenantId/userId malformados; checks[] vazio ou acima de 50
UnauthenticatedErrorIDENTITY_INVALID_OAUTH_ACCESS_TOKEN401access token ausente/inválido
ForbiddenAction (guard)—403ator sem permission:read+user:read (checks/effective) ou permission:read+role:read (consistency) no tenant

POST /checks e /bulk-checks nunca propagam um erro de negócio para o corpo da resposta — uma falha interna vira allowed: false, reason_code: "authorization-unavailable" com 200. A autorização (ou falta dela) do usuário-alvo nunca vaza como 403/404 — só como um campo do resultado.

Regras de negócio​

IDRegraComportamento esperado
RN-01Checagem é uma simulação, não uma autorização realnão popula authorizationContext, não tem efeito colateral, pode ser chamada repetidamente
RN-02Checagem nunca lança erro de negócioqualquer falha interna vira allowed:false com reason_code descritivo, sempre 200
RN-03effective nunca revela por erro se um tenant/unidade existetenant inativo ou unidade fora do tenant → resultado vazio, 200
RN-04effective explica a origem de cada permissãocada permissão listada traz a(s) atribuição(ões)/papel(éis) que a concedem, não só o nome
RN-05consistency compara contagem e conteúdo, não só quantidadesystem-role-catalog também compara o conjunto de permissões de cada papel de sistema persistido contra o esperado

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPDMinimizaçãoeffective/checks só devolvem os campos de autorização necessários ao diagnóstico, sem dado clínico
HIPAA / ANVISAAuditabilidade da decisão de acessoeffective expõe a origem de cada permissão (papel/atribuição) para investigação de incidente

Variáveis de ambiente​

Nenhuma variável de ambiente é consumida diretamente por estas rotas.

Tempo médio de resposta​

A confirmar — responsável: time de Identity; data: 24/09/2026. Sem medição publicada.

Requisitos não funcionais​

RequisitoDefinição
IdempotênciaSim (leitura/simulação pura, sem efeito colateral)
PaginaçãoNão se aplica
Rate limitNão identificado
CacheNão identificado — cada checagem/consulta lê a claim atual
AuditoriaNão — rotas de leitura/diagnóstico, sem evento de auditoria dedicado

Relacionado​