Contract — Auth Headers and Error Responses
Feature: 057 Close Increment 3 (P3)
1. Bearer token
Every authenticated endpoint expects:
Authorization: Bearer <token><token> is one of:
- A HAP-issued JWT (validated via JWKS from
WBSP_HAP_ISSUER). - A wbsp-api-issued CI JWT (validated against the local Ed25519 public key,
rejected if
jtiis not present in the users store).
Both produce the same Identity shape to downstream middleware:
Identity {
Sub string // HAP sub, or impersonated sub for CI tokens
Email string // for log/display
IsAdmin bool // from users.json at request time
TenantSet []string // from users.json at request time (live lookup)
TokenID string // jti for CI tokens; HAP token's jti or "" for HAP
Source string // "hap" | "ci"
}2. 401 responses
The response body always contains an error field naming the failure mode:
{ "error": "missing Authorization header" }
{ "error": "malformed Bearer token" }
{ "error": "expired token" }
{ "error": "invalid signature" }
{ "error": "revoked CI token" }
{ "error": "unknown issuer" }The CLI uses these messages to decide whether to attempt a re-login (token expired / revoked) vs. surface the failure (unknown issuer / malformed).
3. 403 responses
{ "error": "no tenants mapped to your account" }
{ "error": "you are not authorised to act on tenant <name>" }
{ "error": "admin required" }
{ "error": "tenant <name> not found" }4. 409 responses (concurrency / invariant)
{ "error": "cannot revoke admin: would leave zero admins" }This is the only 409 — all other validation issues are 400.
5. CSRF / CORS
- No CSRF tokens: the API is bearer-authenticated; cookies are not used for auth so CSRF is not applicable.
- CORS: locked down by default; only the wbsp-platform UI origin (when one exists, future feature) is in the allow-list. Today no browser-side caller exists.
6. TLS
The API is reached via the platform's Traefik IngressRoute, which terminates TLS on the ALB+ACM front. wbsp-api itself listens on plain HTTP inside the cluster. No new cert wiring.