← Documentação

Para quem desenvolve

Como ligar um sistema à Central de Identidade para login (OpenID Connect) e como usar a API da central para gerir pessoas.

A Central de Identidade é um provedor OpenID Connect (OIDC): o padrão aberto de login que fica por cima do OAuth2. Em vez de guardar senhas, o seu sistema manda a pessoa para a central, ela entra lá, e o sistema recebe de volta quem ela é. Qualquer biblioteca cliente de OIDC da sua linguagem serve; esta página diz o que a central espera e onde ela foge do comum.

Antes de integrar, leia também o Checklist de integração: o que o seu sistema precisa fazer, além do protocolo.

Antes de começar: o client

Para a central aceitar o seu sistema, ele precisa de um client: o cadastro do sistema na central, com um identificador (client_id). Quem cadastra é um administrador global. Leve para ele:

  • Tipo: confidencial, se o sistema guarda um segredo no servidor (o caso comum de um backend), ou público, se não tem onde guardar (um app que roda só no navegador ou no celular). Essa escolha não muda depois.
  • Redirect URIs: os endereços para onde a central devolve a pessoa depois do login.
  • Post-logout redirect URIs, se o sistema for usar a saída pela central (veja Sair).
  • Scopes de que o sistema precisa (veja Quem é a pessoa).
  • Grants: authorization_code e refresh_token para login; client_credentials só se o sistema for chamar a API em nome próprio (veja API).

Um client confidencial recebe um secret, a senha do sistema. Ele aparece uma vez só, para o administrador, no momento do cadastro: guarde na hora, num cofre de segredos.

Login

Endpoints do login

O quê Caminho
Discovery GET /.well-known/openid-configuration
Autorização GET /authorize
Token POST /token
UserInfo GET ou POST /userinfo
Chaves públicas (JWKS) GET /jwks
Saída GET /end-session

O documento de discovery tem esses endereços e o issuer, mas não lista os scopes e claims: eles estão na seção Quem é a pessoa, abaixo.

O fluxo

A central só aceita o fluxo authorization code com PKCE, e o PKCE é obrigatório para todo client, inclusive os confidenciais. PKCE é a prova de que quem troca o código pelo token é o mesmo sistema que pediu o login: o sistema gera um segredo aleatório (code_verifier), manda o hash dele no pedido de login (code_challenge) e o segredo original na troca.

  1. O sistema manda a pessoa para /authorize:

    https://home.vetorzero.com.br/authorize
      ?response_type=code
      &client_id=meu-sistema
      &redirect_uri=https://meu-sistema.exemplo.com.br/callback
      &scope=openid profile email
      &state=<aleatório>
      &nonce=<aleatório>
      &code_challenge=<BASE64URL(SHA256(code_verifier))>
      &code_challenge_method=S256
    

    Mande code_challenge_method=S256 sempre. Sem ele, a biblioteca do servidor assume plain, que a central recusa.

  2. A pessoa entra com a conta Google (ou passa direto, se já tiver entrado) e volta para a redirect_uri com code e state. Não existe tela de consentimento: todos os sistemas ligados à central são da própria empresa.

  3. O sistema troca o código por tokens:

    curl -X POST "https://home.vetorzero.com.br/token" \
      -u 'meu-sistema:SECRET' \
      -d grant_type=authorization_code \
      -d code=CODE \
      -d redirect_uri=https://meu-sistema.exemplo.com.br/callback \
      -d code_verifier=CODE_VERIFIER
    

    Um client confidencial se autentica com HTTP Basic (como acima) ou com client_id e client_secret no corpo. Um client público manda só o client_id no corpo.

  4. A resposta traz access_token, id_token e refresh_token. O id_token já tem as claims dos scopes pedidos; confira nele o nonce que você mandou.

Tokens

Token Validade
Código de autorização 10 minutos
Access token 1 hora
ID token 1 hora
Refresh token 1 mês
  • O access token e o ID token são JWT assinados com RS256. As chaves públicas estão em /jwks.
  • Cada refresh token vale uma vez só: ao usá-lo, você recebe um novo e o antigo é revogado. Guarde sempre o último.
  • A central não tem endpoint de revogação nem de introspecção de token.

Regras de redirect URI

  • Tem que ser https. A exceção é http em localhost ou 127.0.0.1, para desenvolvimento.
  • A comparação é exata, caractere por caractere. Em http://127.0.0.1 a porta é ignorada; em http://localhost, a porta precisa bater.

Quem é a pessoa: scopes e claims

Cada scope libera um conjunto de claims (os dados da pessoa). O scope openid entra sempre.

Scope Claim Tipo O que é
openid sub string Identificador da pessoa, como vlidc_usr_…
profile name string Nome
profile picture string ou null Foto da conta Google principal
email email string Email principal
email email_verified bool Se o email principal foi confirmado
verified_emails verified_emails string[] Todos os emails confirmados
employment employer string ou null Domínio da empresa que contratou a pessoa; null para freelancer
employment served_organizations string[] Domínios das empresas que ela atende
employment job_title string ou null Cargo
employment department string ou null Departamento

As claims vêm no ID token e também em /userinfo.

Identifique a pessoa pelo sub, nunca pelo email. Isso e o resto do que o seu sistema precisa fazer com essas claims, incluindo como associar usuários que já existiam, estão no Checklist de integração.

/userinfo

/userinfo devolve as mesmas claims a partir de um access token. O token vai só no header Authorization: Bearer <token>, tanto em GET quanto em POST; não há parâmetro no corpo.

curl "https://home.vetorzero.com.br/userinfo" -H "Authorization: Bearer ACCESS_TOKEN"

Um token de client_credentials é recusado aqui (403): ele não representa uma pessoa.

Sair

Sair da central não tira a pessoa do seu sistema, e sair do seu sistema não tira ela da central. Cada lado tem a sua sessão.

Para encerrar também a sessão na central, mande a pessoa para /end-session, com os parâmetros na query string:

https://home.vetorzero.com.br/end-session
  ?id_token_hint=<o id_token da pessoa>
  &post_logout_redirect_uri=https://meu-sistema.exemplo.com.br/
  &state=<opcional>
  • A post_logout_redirect_uri precisa estar cadastrada no client. Ela também exige id_token_hint ou client_id.
  • Com id_token_hint válido, a saída é direta. Sem ele, a central pode mostrar uma tela de confirmação antes.

Pessoas desativadas

Quando um administrador desativa alguém, a central revoga os tokens dessa pessoa: /userinfo e o refresh passam a recusar. Um JWT que o seu sistema valida sozinho, só com a chave pública, continua válido até expirar (no máximo 1 hora). Se o seu sistema precisa reagir na hora, consulte /userinfo ou faça refresh em vez de confiar só na validade do JWT.

API

A API em /api/v1 permite que outro sistema gerencie pessoas: listar, consultar, cadastrar, editar, desativar e cuidar de emails. A especificação completa, em OpenAPI, está em /api/doc (para ler no navegador) e em /api/doc.json.

Acesso

Toda chamada leva um access token no header Authorization: Bearer. Há dois jeitos de obter um:

  • Em nome do próprio sistema (client_credentials): o sistema age como ele mesmo, com os papéis que o client recebeu. Exige client confidencial.

    curl -X POST "https://home.vetorzero.com.br/token" -u 'meu-sistema:SECRET' -d grant_type=client_credentials
    
  • Em nome de uma pessoa: o access token do login dela. A chamada vale com os papéis da pessoa.

Nos dois casos, o client precisa ter "Acesso à API" liberado por um administrador global, que também define os IPs de onde ele pode chamar (sem nenhum IP, qualquer um é aceito). Todos os endpoints de hoje exigem o papel de administrador de pessoas. As chamadas ficam registradas na auditoria da central, com o client de origem.

O bearer é o access token, não o secret. Mandar o secret no lugar dá 401.

Endpoints da API

Método Caminho O quê
GET /api/v1/users?limit=&offset= Lista pessoas
POST /api/v1/users Cadastra uma pessoa (name, email)
GET /api/v1/users/{id} Consulta uma pessoa
PUT /api/v1/users/{id}/profile Troca nome e email
PUT /api/v1/users/{id}/employment Troca empregador, empresas atendidas, cargo e departamento
POST /api/v1/users/{id}/emails Adiciona um email (a pessoa confirma pelo link)
DELETE /api/v1/users/{id}/emails/{address} Remove um email
POST /api/v1/users/{id}/deactivate Desativa
POST /api/v1/users/{id}/reactivate Reativa

O {id} é o mesmo sub do login. Empresas são identificadas pelo domínio. Uma pessoa cadastrada pela API recebe o convite por email, como no cadastro pela tela, e não há exclusão de pessoa: o equivalente é desativar.

Convenções

  • Os nomes da API estão em inglês (campos, mensagens de erro).
  • Não há PATCH. O PUT manda o estado inteiro do recurso: toda chave é obrigatória, inclusive as que aceitam null. Chave faltando dá erro.
  • Idempotency-Key (opcional, só em POST): repetir a mesma chamada com a mesma chave devolve a resposta guardada, com o header Idempotent-Replayed: true, em vez de repetir o efeito. Só respostas 2xx são guardadas, por pelo menos 7 dias. A mesma chave com outro corpo dá 422; uma chamada enquanto a primeira ainda roda dá 409.
  • Paginação: limit de 1 a 100 (padrão 50) e offset. A resposta traz items, total e os links next e prev.
  • Erros seguem o RFC 9457 (application/problem+json), com title, status e, quando ajuda, detail. Erro de validação é 422, com a lista em violations; recurso inexistente é 404; conflito (como email já usado) é 409.

Quando algo dá errado

invalid_request "PKCE is required" no /authorize: faltou o code_challenge. Se ele foi, confira o code_challenge_method=S256.

invalid_client no /token: secret errado, client desativado ou, no client_credentials, chamada de um IP fora da lista do client.

Erro no /authorize logo de cara, sem passar pelo login: a redirect_uri provavelmente não bate exatamente com a cadastrada. Atenção à barra no fim e à porta do localhost.

access_denied no /authorize durante um teste: o administrador está impersonando outra pessoa na central. Impersonando, a central não deixa entrar em nenhum sistema.

403 em /userinfo: o token é de client_credentials, que não representa uma pessoa.

401 na API: falta o bearer, o bearer é o secret em vez do access token, ou o client está desativado, sem acesso à API ou chamando de um IP não liberado. O detail diz qual.