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
kidque 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
| Claim | Descrição |
|---|---|
iss | O identificador do Authorization Server, como URL: https://account[.{env}].kobana.com.br. |
aud | Esta 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. |
exp | Obrigatória. Token sem expiração é recusado. |
org | A 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. |
sub | A pessoa, nunca a organização. Vai para auditoria. |
email | Quando 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. |
jti | Id único do token, usado na revogação e no limite de requisições. |
scope | Escopos 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.
| Escopo | Concede leitura de |
|---|---|
kadu/organizations | a organização do token |
kadu/workspaces | workspaces |
kadu/activities | atividades |
kadu/tasks | tarefas, a pré-checagem do disparo e o endpoint de disparo (o disparo em si é write) |
kadu/executions | execuções, logs, eventos, pendências e a busca (cancelar e continuar são write) |
kadu/todos | pendências (resolver é write) |
kadu/files | arquivos de entrada e de saída |
kadu/environments | ambientes |
kadu/secrets | secrets: chave, notas e metadados. O valor nunca é devolvido por endpoint nenhum |
kadu/schedules | agendamentos |
kadu/memories | memórias |
kadu/webhooks | webhooks (rotacionar o signing secret é write) |
kadu/webhook_deliveries | entregas de webhook (reenviar é write) |
kadu/repository | o Playbook (sincronizar é write) |
kadu/usage | uso e custo |
kadu/users | membros da organização |
kadu/memberships | vínculos de pessoas com a organização |
kadu/roles | papéis |
kadu/system_events | eventos de sistema |
kadu/all | curinga do eixo de recurso: lê todos os recursos acima e não concede nível nenhum |
| Nível de acesso | Concede |
|---|---|
read | Leitura. O escopo do recurso já a implica; num token sem escopo kadu/…, lê todos os recursos |
write | Todo POST, PUT e PATCH nos recursos que o token carrega: criar, alterar, disparar, cancelar, sincronizar, rotacionar segredo, reenviar entrega. Não exclui |
delete | Todo DELETE nos recursos que o token carrega, e nada mais |
import | Importação em massa. Nenhum endpoint desta API importa em massa hoje |
export | Exportação em massa. Nenhum endpoint desta API exporta em massa hoje |
all | Todos 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ção | Exige |
|---|---|
GET | O escopo do recurso (kadu/tasks) |
POST, PUT, PATCH | O escopo do recurso + write (kadu/tasks write) |
DELETE | O escopo do recurso + delete (kadu/tasks delete); write não basta |
GET /v1/me | Qualquer 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:
- Extrai
Authorization: Bearer <jwt>. - Verifica a assinatura com a chave pública do Authorization Server,
aceitando só
RS256etyp: at+jwt. - Valida
iss, que oaudcontém esta API, e queexpestá presente e no futuro. - Confere se o
jtifoi revogado. - Resolve a organização pelo
org. - Aplica o limite de requisições do token.
- Confere o escopo contra o endpoint.
- 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
| HTTP | code | Causa |
|---|---|---|
| 401 | INVALID_TOKEN | JWT ausente, malformado, com assinatura inválida ou com outro typ |
| 401 | INVALID_AUDIENCE | aud de outro app ou de outro ambiente |
| 401 | INVALID_ISSUER | emissor diferente do Authorization Server da Kobana |
| 401 | TOKEN_EXPIRED | exp no passado |
| 401 | TOKEN_REVOKED | jti revogado |
| 403 | UNKNOWN_ORGANIZATION | o org aponta uma organização que o Kadu não conhece |
| 403 | INSUFFICIENT_SCOPE | token sem o escopo exigido |
| 403 | USER_PERMISSION_DENIED, NOT_A_MEMBER | o papel da pessoa do token não permite, ou ela não pertence à organização (Permissões) |
| 429 | RATE_LIMITED | limite do token estourado (Limites) |
| 503 | AS_UNAVAILABLE | as 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/executionspara quem só acompanha,kadu/tasks writepara quem dispara,deletesó para quem exclui. - Revogue na hora qualquer token vazado.