Authentication
The Kadu API uses a Kobana Account access token to authenticate every
request, in the Authorization: Bearer header, like Kobana's other APIs.
curl -H "Authorization: Bearer $KOBANA_TOKEN" \
-H "Content-Type: application/json" \
https://api.kadu.ia.br/v1/me
The token​
The token is an access token issued by Kobana's Authorization Server
(account.kobana.com.br). Kadu only verifies it: it keeps no signing key
and cannot issue any token.
- Algorithm:
RS256, and onlyRS256. - Type: the token header must have
typ: at+jwt(RFC 9068). Another JWT from the same issuer, such as an ID token, is rejected. - Key: public, and discovered, not configured. The issuer identifier
is a URL, and the keys endpoint (
jwks_uri) is read from the RFC 8414 metadata document published under it (/.well-known/oauth-authorization-server). When the Authorization Server rotates the key, Kadu follows on its own. - When the Authorization Server is called: never to verify a token,
because signature checking is local. What comes from it is the key set,
which is cached. A fetch happens only with an empty cache, with the cached
set older than an hour, or when a token arrives with a
kidthat is not in the cache (a rotation; at most one new fetch every 30 seconds). Each fetch has a 5-second timeout.
Claims​
| Claim | Description |
|---|---|
iss | The Authorization Server's identifier, as a URL: https://account[.{env}].kobana.com.br. |
aud | This API, as a URL: https://api.kadu.ia.br (staging: https://api.staging.kadu.ia.br). Can be a list: the rule is contains, not equality, so a token can address more than one Kobana API. |
exp | Required. A token with no expiration is rejected. |
org | The organization on whose behalf the token acts, and it is required. It scopes every response. A token without org is rejected, instead of falling back to sub. |
sub | The person, never the organization. Goes to auditing. |
email | When present, ties the token to a person in the organization, and their role applies too. An integration token, with no person, is governed by scopes only. |
jti | Unique token id, used for revocation and for the rate limit. |
scope | Space-separated scopes (below). |
:::info Check the environment
Production's aud is not valid in staging, and vice versa. Do not reuse the
token across environments.
:::
Test the token​
GET /v1/me tells you who is behind the token. It is the only route without a
scope check, precisely so you can test a new token.
Scopes​
Scopes have two orthogonal axes: the resource, always prefixed by the app
(kadu/tasks), and the access level, a global scope with no prefix:
read, write, delete, import, export or all. A resource without a
verb means read. Everything else requires both: the resource scope and the
level.
A route's resource is the first segment after /v1:
/v1/executions/{id}/logs belongs to the executions resource.
| Scope | Grants read access to |
|---|---|
kadu/organizations | the token's organization |
kadu/workspaces | workspaces |
kadu/activities | activities |
kadu/tasks | tasks, the run pre-check and the run endpoint (the run itself is write) |
kadu/executions | executions, logs, events, pending items and search (cancel and continue are write) |
kadu/todos | pending items (resolving is write) |
kadu/files | input and output files |
kadu/environments | environments |
kadu/secrets | secrets: key, notes and metadata. The value is never returned by any endpoint |
kadu/schedules | schedules |
kadu/memories | memories |
kadu/webhooks | webhooks (rotating the signing secret is write) |
kadu/webhook_deliveries | webhook deliveries (resending is write) |
kadu/repository | the Playbook (syncing is write) |
kadu/usage | usage and cost |
kadu/users | organization members |
kadu/memberships | links between people and the organization |
kadu/roles | roles |
kadu/system_events | system events |
kadu/all | wildcard of the resource axis: reads all the resources above and grants no level |
| Access level | Grants |
|---|---|
read | Read. The resource scope already implies it; on a token with no kadu/… scope, reads all resources |
write | Every POST, PUT and PATCH on the resources the token carries: create, update, run, cancel, sync, rotate secret, resend delivery. Does not delete |
delete | Every DELETE on the resources the token carries, and nothing else |
import | Bulk import. No endpoint in this API imports in bulk today |
export | Bulk export. No endpoint in this API exports in bulk today |
all | All the levels above |
Which level each endpoint requires​
One level per request, determined by the HTTP method. No level substitutes for
another: write does not delete, and delete does not write.
| Request | Requires |
|---|---|
GET | The resource scope (kadu/tasks) |
POST, PUT, PATCH | The resource scope + write (kadu/tasks write) |
DELETE | The resource scope + delete (kadu/tasks delete); write is not enough |
GET /v1/me | Any valid token, with no scope check |
Every deletion is a DELETE (the resource gets deleted_at) and every
DELETE is a deletion.
Three examples: kadu/executions reads executions and does not cancel;
kadu/tasks write also creates, updates and runs tasks, but does not delete
them; kadu/tasks write delete does all three. None of them reaches secrets.
The granular form kadu/tasks:write (or kadu/tasks:delete) is an exception
to the grammar, not the default format: it satisfies both axes of that
resource on its own and exists for "reads one resource, writes another".
kadu/* is a registration wildcard and is never issued in a token: the
comparison here is always literal.
A token that carries kadu/… scopes is limited to the resources they name,
and the levels act only within them. A token with no kadu/… scope is limited
by level: it reads with read, write or all; writes with write or
all; deletes with delete or all. A token with neither reaches nothing
beyond GET /v1/me.
Insufficient scope responds 403 INSUFFICIENT_SCOPE, with the levels that
would suffice in details.required and the token's scopes in details.granted.
Validation pipeline​
In this order:
- Extracts
Authorization: Bearer <jwt>. - Verifies the signature with the Authorization Server's public key,
accepting only
RS256andtyp: at+jwt. - Validates
iss, thataudcontains this API, and thatexpis present and in the future. - Checks whether the
jtihas been revoked. - Resolves the organization from
org. - Applies the token's rate limit.
- Checks the scope against the endpoint.
- With a person in the token, checks their role in the organization (Permissions).
Isolation between organizations​
Every query is filtered by the org organization. There is no way to point to
another organization through the body or the query. A resource from another
organization responds 404 NOT_FOUND, not 403, so as not to reveal that it
exists.
Failure modes​
| HTTP | code | Cause |
|---|---|---|
| 401 | INVALID_TOKEN | JWT missing, malformed, with an invalid signature or with another typ |
| 401 | INVALID_AUDIENCE | aud from another app or another environment |
| 401 | INVALID_ISSUER | issuer different from Kobana's Authorization Server |
| 401 | TOKEN_EXPIRED | exp in the past |
| 401 | TOKEN_REVOKED | revoked jti |
| 403 | UNKNOWN_ORGANIZATION | the org points to an organization Kadu does not know |
| 403 | INSUFFICIENT_SCOPE | token without the required scope |
| 403 | USER_PERMISSION_DENIED, NOT_A_MEMBER | the role of the token's person does not allow it, or they do not belong to the organization (Permissions) |
| 429 | RATE_LIMITED | token limit exceeded (Limits) |
| 503 | AS_UNAVAILABLE | the signing keys could not be fetched from the Authorization Server. Verification itself is local, so this only happens at the moments listed in When the Authorization Server is called. It is not a problem with your token: try again after Retry-After. It is a 5xx on purpose: a 401 would send you to debug a good credential during an outage on our side. |
See Errors for the full envelope.
Best practices​
- Never commit a token to git. Use environment variables or a vault (AWS Secrets Manager, Vault…).
- Use one token per integration: you can revoke one without taking down the others.
- Ask for the minimum scopes:
kadu/executionsfor whoever only follows,kadu/tasks writefor whoever runs,deleteonly for whoever deletes. - Revoke any leaked token immediately.