Profile Server API
Endpoints

Me

Dados do próprio usuário autenticado — permissões efetivas e tenants.

Endpoints que respondem sobre quem fez a requisição. O usuário sai sempre do token (não há :userId na rota), então não há como consultar outra pessoa por aqui — por isso são os únicos endpoints /api/* sem @Permissions e sem @RequireRole. Base path: /api/me · autenticação de usuário.

MétodoRotaScopeResourceAção (role)Sucesso
GET/api/me/permissions———200
GET/api/me/tenants———200

Controle de acesso. Qualquer usuário autenticado acessa os dois. A autorização real continua nos demais endpoints (RolesGuard) — o que vem daqui serve para o painel esconder menus e ações, nunca como decisão de acesso.

Bootstrap: os dois funcionam sem tenant ativo resolvido (RJGDD-16). Toda rota /api/* normalmente exige um tenant resolvível — sem X-Tenant-Id e com 0 ou 2+ tenants, o KeycloakAuthMiddleware devolve 403 (ver Autenticação). Esses dois endpoints são a exceção: resolvem sem tenant/persona em vez de 403 (resolveTenantScopeAllowingBootstrap). Sem essa válvula, ninguém com 0 ou 2+ tenants conseguiria carregar a própria lista de tenants — a chamada que alimenta o seletor exigiria um tenant já selecionado, um impasse. Com o tenant não resolvido, GET /api/me/permissions devolve permissions: [] (nenhuma persona conhecida ainda) até a pessoa escolher um tenant no seletor; GET /api/me/tenants não é afetado, porque nunca dependeu do tenant ativo para montar a resposta.


Permissões efetivas

GET /api/me/permissions — roles do token e todo par modulo:acao liberado pela persona do tenant ativo + essas roles (via listAccessiblePermissions).

roles traz só o que ainda vem do Keycloak: a persona global (se for o caso) e as roles de exceção (modulo:acao[:deny]). A persona por tenant (tenant-admin/coordinator/ technician/viewer) não aparece aqui — ela decide o conteúdo de permissions, mas não é exposta neste payload (ver Autenticação).

curl http://localhost:3000/api/me/permissions \
  -H "Authorization: Bearer $ACCESS_TOKEN"

200 OK

{
  "roles": ["documents:delete"],
  "permissions": [
    "documents:read",
    "documents:create",
    "documents:update",
    "documents:delete",
    "document-types:read",
    "organizations:read"
  ]
}

Meus tenants

GET /api/me/tenants — tenants a que o usuário autenticado pertence, para montar o seletor de tenant ativo do painel. O tenant escolhido volta em cada requisição no header X-Tenant-Id (ver Autenticação).

Só devolve tenants ativos: um tenant inativo não alcança cooperativa nenhuma, e oferecê-lo na escolha deixaria a tela vazia sem explicação. O payload é enxuto de propósito — keycloakClientId, allowedScopes e metadata são configuração de infra e só aparecem nas rotas administrativas de tenants.

curl http://localhost:3000/api/me/tenants \
  -H "Authorization: Bearer $ACCESS_TOKEN"

200 OK

{
  "data": [
    {
      "id": "t1b2c3d4-e5f6-4789-a123-456789abcdef",
      "name": "Acme Tenant",
      "description": "Cooperativa de reciclagem da zona norte",
      "active": true,
      "persona": "technician"
    }
  ]
}
CampoTipoDescrição
iduuidvalor a enviar em X-Tenant-Id
namestringnome técnico do tenant
descriptionstring | nulllabel exibido no seletor; quando null, o front usa name
activebooleansempre true neste endpoint (inativos saem)
personastring | nullpersona do usuário neste vínculo (UserTenant.persona); null quando o vínculo ainda não recebeu persona atribuída — qualquer request feita com esse tenant ativo recebe 403 até a reatribuição manual (ver PLANO-PERSONA-POR-TENANT.md §15)

Erros

StatuscodeQuando
404USER_NOT_FOUNDusuário do token não tem cadastro local