Login dos seus usuários
O mesmo widget que verifica identidade também cadastra e autentica os usuários do seu produto: e-mail e senha, código por e-mail ou SMS, segundo fator quando o risco pede, e recuperação de senha. Com módulos de pessoa no mesmo flow, o cadastro já nasce com a verificação de identidade. Cada organização e cada ambiente tem a sua própria base de usuários. Como liga. No painel, ligue Autenticação no flow. O seu backend cria a sessão com o blocoauth (o code_challenge do PKCE S256), entrega o id da sessão à página, e o widget conduz o login. No fim ele entrega um código de uso único, que vale por um minuto e só serve com o code_verifier que ficou no seu backend e com a sua chave secreta.
Verifique o token no seu servidor. O token de acesso é um JWT (typ at+jwt, RS256) de vida curta, com uma chave por organização e ambiente. Aceite só RS256, confira o typ, o iss exato, o aud e o exp, e busque a chave pelo kid só no JWKS do emissor que você espera, nunca numa URL que vem dentro do token. O JWKS fica em https://api.unifokal.com/v1/auth/t/{organization_id}/{ambiente}/.well-known/jwks.json, sem chave nenhuma, e pode ser guardado em cache por dez minutos.
Escopos. Toda rota abaixo exige uma chave secreta criada com o escopo da rota: auth:token, auth:users:read, auth:users:write, fga:check ou fga:write. Chaves sem restrição não têm esses escopos. Toda resposta com token ou código sai com Cache-Control: no-store.
POST /v1/auth/token
Troca o código do widget (authorization_code) e renova (refresh_token). O refresh roda a cada uso: guarde sempre o último. Reapresentar um refresh antigo derruba a sessão inteira e dispara o webhook auth.refresh_reuse_detected. O corpo pode ir como formulário ou JSON.
client_credentials com o id e o segredo em Authorization: Basic, sem a chave secreta. A resposta não tem refresh: guarde o token até perto do exp.
400 invalid_grant (código ou refresh inválido, usado, vencido, ou verificador errado), 400 invalid_scope e invalid_target (M2M), 401 invalid_client (M2M), 403 insufficient_scope, 402 insufficient_balance (M2M depois de saldo negativo prolongado; o login dos seus usuários nunca para por saldo) e os limites de uso 429 rate_limited, spend_cap_reached e volume_cap_reached.
POST /v1/auth/revoke
Revoga a sessão de um refresh token (token) ou uma sessão pelo id (session_id), como no logout. Responde 200 mesmo para token desconhecido. O token de acesso já emitido vale até o exp: por isso ele é curto.
GET /v1/auth/users
Lista os usuários por cursor, com filtro por e-mail (sem diferença de caixa), telefone, external_id e estado. Nenhuma resposta traz senha, segredo ou hash. Por id, em GET /v1/auth/users/{id}, vêm o telefone inteiro, o metadado privado e os fatores.
PATCH /v1/auth/users/{id}
Grava o external_id (a sua referência do titular, sem forma de CPF, e-mail ou telefone), o public_metadata (vai no token), o private_metadata (nunca sai no token) e o estado: disabled e locked derrubam as sessões. 409 external_id_conflict quando outro usuário do ambiente já tem a referência.
DELETE /v1/auth/users/{id}
Apaga o usuário, derruba as sessões e avisa pelo webhook user.deleted. As rotas POST /v1/auth/users/{id}/unlock (destrava) e POST /v1/auth/users/{id}/sessions/revoke (derruba todas as sessões) completam a gestão.
POST /v1/fga/check
Autorização fina no estilo Zanzibar: um modelo por organização e ambiente (PUT /v1/fga/model, cada gravação é uma versão nova e imutável), tuplas objeto#relação@usuário (POST /v1/fga/tuples grava e apaga numa transação, GET /v1/fga/tuples lê) e as perguntas: check, batch-check e list-objects. Uma escrita confirmada já vale para a checagem seguinte. Quando a resolução passa de um teto, a resposta é 422 fga_resolution_limit, nunca um false silencioso.
user.created, user.updated, user.deleted, user.locked, user.unlocked, auth.suspicious_sign_in, auth.refresh_reuse_detected e auth.compromised_password, assinados como os demais e entregues no webhook do flow do cadastro. Login comum não gera webhook.