Pular para o conteúdo principal

Autenticação

A API do Kadu usa um access token da Conta Kobana para autenticar todas as requisições, no header Authorization: Bearer, como as outras APIs da Kobana.

curl -H "Authorization: Bearer $KOBANA_TOKEN" \
-H "Content-Type: application/json" \
https://api.kadu.ia.br/v1/me

O token​

O token é um access token emitido pelo Authorization Server da Kobana (account.kobana.com.br). O Kadu apenas o verifica: não guarda chave de assinatura e não consegue emitir token nenhum.

  • Algoritmo: RS256, e só RS256.
  • Tipo: o cabeçalho do token precisa ter typ: at+jwt (RFC 9068). Outro JWT do mesmo emissor, como um ID token, é recusado.
  • Chave: pública, e descoberta, não configurada. O identificador do emissor é uma URL, e o endpoint das chaves (jwks_uri) é lido do documento de metadata RFC 8414 publicado sob ela (/.well-known/oauth-authorization-server). Quando o Authorization Server rotaciona a chave, o Kadu acompanha sozinho.
  • Quando o Authorization Server é chamado: nunca para verificar um token, porque a checagem da assinatura é local. O que vem dele é o conjunto de chaves, que fica em cache. Uma busca só acontece com o cache vazio, com o conjunto em cache há mais de uma hora, ou quando chega um token com um kid que não está no cache (uma rotação; no máximo uma nova busca a cada 30 segundos). Cada busca tem timeout de 5 segundos.

Claims​

ClaimDescrição
issO identificador do Authorization Server, como URL: https://account[.{env}].kobana.com.br.
audEsta API, como URL: https://api.kadu.ia.br (staging: https://api.staging.kadu.ia.br). Pode ser uma lista: a regra é contém, não igualdade, então um token pode endereçar mais de uma API Kobana.
expObrigatória. Token sem expiração é recusado.
orgA organização em nome da qual o token age, e é obrigatória. É ela que escopa toda resposta. Token sem org é recusado, em vez de cair no sub.
subA pessoa, nunca a organização. Vai para auditoria.
emailQuando presente, liga o token a uma pessoa da organização, e vale também o papel dela. Um token de integração, sem pessoa, vale só pelos escopos.
jtiId único do token, usado na revogação e no limite de requisições.
scopeEscopos separados por espaço (abaixo).

:::info Confira o ambiente O aud de produção não vale em staging, e vice-versa. Não reutilize o token entre ambientes. :::

Testar o token​

GET /v1/me diz quem está por trás do token. É a única rota sem checagem de escopo, justamente para você testar um token novo.

Escopos​

Os escopos têm dois eixos ortogonais: o recurso, sempre prefixado pelo app (kadu/tasks), e o nível de acesso, um escopo global sem prefixo: read, write, delete, import, export ou all. Recurso sem verbo significa leitura. Todo o resto exige os dois: o escopo do recurso e o nível.

O recurso de uma rota é o primeiro segmento depois de /v1: /v1/executions/{id}/logs é do recurso executions.

EscopoConcede leitura de
kadu/organizationsa organização do token
kadu/workspacesworkspaces
kadu/activitiesatividades
kadu/taskstarefas, a pré-checagem do disparo e o endpoint de disparo (o disparo em si é write)
kadu/executionsexecuções, logs, eventos, pendências e a busca (cancelar e continuar são write)
kadu/todospendências (resolver é write)
kadu/filesarquivos de entrada e de saída
kadu/environmentsambientes
kadu/secretssecrets: chave, notas e metadados. O valor nunca é devolvido por endpoint nenhum
kadu/schedulesagendamentos
kadu/memoriesmemórias
kadu/webhookswebhooks (rotacionar o signing secret é write)
kadu/webhook_deliveriesentregas de webhook (reenviar é write)
kadu/repositoryo Playbook (sincronizar é write)
kadu/usageuso e custo
kadu/usersmembros da organização
kadu/membershipsvínculos de pessoas com a organização
kadu/rolespapéis
kadu/system_eventseventos de sistema
kadu/allcuringa do eixo de recurso: lê todos os recursos acima e não concede nível nenhum
Nível de acessoConcede
readLeitura. O escopo do recurso já a implica; num token sem escopo kadu/…, lê todos os recursos
writeTodo POST, PUT e PATCH nos recursos que o token carrega: criar, alterar, disparar, cancelar, sincronizar, rotacionar segredo, reenviar entrega. Não exclui
deleteTodo DELETE nos recursos que o token carrega, e nada mais
importImportação em massa. Nenhum endpoint desta API importa em massa hoje
exportExportação em massa. Nenhum endpoint desta API exporta em massa hoje
allTodos os níveis acima

Qual nível cada endpoint exige​

Um nível por requisição, dado pelo método HTTP. Nenhum nível substitui outro: write não exclui, e delete não escreve.

RequisiçãoExige
GETO escopo do recurso (kadu/tasks)
POST, PUT, PATCHO escopo do recurso + write (kadu/tasks write)
DELETEO escopo do recurso + delete (kadu/tasks delete); write não basta
GET /v1/meQualquer token válido, sem checagem de escopo

Toda exclusão é um DELETE (o recurso ganha deleted_at) e todo DELETE é uma exclusão.

Três exemplos: kadu/executions lê execuções e não cancela; kadu/tasks write também cria, altera e dispara tarefas, mas não as exclui; kadu/tasks write delete faz os três. Nenhum deles alcança secrets.

A forma granular kadu/tasks:write (ou kadu/tasks:delete) é exceção da gramática, não o formato padrão: satisfaz os dois eixos daquele recurso sozinha e existe para "lê um recurso, escreve outro". kadu/* é curinga de cadastro e nunca é emitido num token: a comparação aqui é sempre literal.

Um token que carrega escopos kadu/… fica limitado aos recursos que eles nomeiam, e os níveis agem só dentro deles. Um token sem escopo kadu/… fica limitado ao nível: lê com read, write ou all; escreve com write ou all; exclui com delete ou all. Um token sem nenhum dos dois não alcança nada além de GET /v1/me.

Escopo insuficiente responde 403 INSUFFICIENT_SCOPE, com os níveis que bastariam em details.required e os escopos do token em details.granted.

Pipeline de validação​

Nesta ordem:

  1. Extrai Authorization: Bearer <jwt>.
  2. Verifica a assinatura com a chave pública do Authorization Server, aceitando só RS256 e typ: at+jwt.
  3. Valida iss, que o aud contém esta API, e que exp está presente e no futuro.
  4. Confere se o jti foi revogado.
  5. Resolve a organização pelo org.
  6. Aplica o limite de requisições do token.
  7. Confere o escopo contra o endpoint.
  8. Com pessoa no token, confere o papel dela na organização (Permissões).

Isolamento entre organizações​

Toda consulta é filtrada pela organização do org. Não há como apontar outra organização pelo corpo ou pela query. Um recurso de outra organização responde 404 NOT_FOUND, e não 403, para não revelar que ele existe.

Modos de falha​

HTTPcodeCausa
401INVALID_TOKENJWT ausente, malformado, com assinatura inválida ou com outro typ
401INVALID_AUDIENCEaud de outro app ou de outro ambiente
401INVALID_ISSUERemissor diferente do Authorization Server da Kobana
401TOKEN_EXPIREDexp no passado
401TOKEN_REVOKEDjti revogado
403UNKNOWN_ORGANIZATIONo org aponta uma organização que o Kadu não conhece
403INSUFFICIENT_SCOPEtoken sem o escopo exigido
403USER_PERMISSION_DENIED, NOT_A_MEMBERo papel da pessoa do token não permite, ou ela não pertence à organização (Permissões)
429RATE_LIMITEDlimite do token estourado (Limites)
503AS_UNAVAILABLEas chaves de assinatura não puderam ser buscadas no Authorization Server. A verificação em si é local, então isso só acontece nos momentos listados em Quando o Authorization Server é chamado. Não é problema do seu token: tente de novo depois do Retry-After. É 5xx de propósito: um 401 mandaria você depurar uma credencial boa durante uma indisponibilidade nossa.

Veja Erros para o envelope completo.

Boas práticas​

  • Nunca versione token em git. Use variáveis de ambiente ou um cofre (AWS Secrets Manager, Vault…).
  • Use um token por integração: dá para revogar um sem derrubar os outros.
  • Peça os escopos mínimos: kadu/executions para quem só acompanha, kadu/tasks write para quem dispara, delete só para quem exclui.
  • Revogue na hora qualquer token vazado.