Repository navigation
feat(auth-permission): accept a session token as an alternative to x-api-key - #66
Conversation
…lternative to x-api-key Routes opt in via RouteProperties.acceptsSessionToken; the Authorization: Bearer JWT is verified offline against wave-auth-api's JWKS (jose, RS256/ES256 only) and its brokers claim becomes the resolved broker scope. No permission check runs on this path — wave-auth-api's session tokens deliberately carry no permissions claim, so the route's own opt-in is the authorization decision. Every existing x-api-key caller is unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
| await checkRoutePermission( | ||
| request, | ||
| route, | ||
| apiKey, | ||
| options.validator, | ||
| brokerIdHeader, | ||
| brokerIdMaxLength, | ||
| ); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Este arquivo está muito granade e difícil de ser lido, creio que a função checkRoutePermission e suas funções auxiliares podem ficar em um arquivo apartado.
também a renomearia para deixar claro que seu contexto é de API Key
There was a problem hiding this comment.
Movido — checkRoutePermission renomeado para checkApiKeyPermission em check-api-key-permission.ts. A lógica compartilhada com o path de session token foi para broker-scope-resolution.ts.
| brokerIdMaxLength, | ||
| ); | ||
| await assertHasPermission(request, route); | ||
| }); |
There was a problem hiding this comment.
O code scanning achou o seguinte erro:
This route handler performs authorization, but is not rate-limited.
This route handler performs authorization, but is not rate-limited.
There was a problem hiding this comment.
Rate limiting fica fora deste hook de propósito: ele roda dentro de um framework compartilhado usado por vários serviços com perfis de tráfego bem diferentes, então um limite fixo aqui seria arbitrário. Documentei essa decisão no JSDoc de registerPermissionAuth — o rate limiting é esperado na camada de infra/gateway de cada serviço consumidor, o mesmo padrão que o próprio wave-auth-api usa (Cloud Armor via IaC), não em código de aplicação.
There was a problem hiding this comment.
Atualização: adicionei rate limiting de fato, opt-in via options.rateLimit (usa @fastify/rate-limit, agrupado por credencial em vez de IP por padrão). Segue sem valor padrão — continua sendo opt-in pelo mesmo motivo (perfis de tráfego diferentes por serviço).
…eview feedback Moves the API-key check (renamed checkApiKeyPermission, its context wasn't clear before) and the session-token check into their own files, and pulls their shared broker-scope logic into broker-scope-resolution.ts, so the hook file itself is left as pure orchestration. Also documents why this hook does no rate limiting of its own — that's left to each consuming service's infrastructure/gateway, the same layer wave-auth-api itself relies on. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds @fastify/rate-limit as a dependency and an optional rateLimit option on registerPermissionAuth, bucketed by the presented credential (API key or session token) rather than IP by default, so a leaked credential can't roam free across IPs and one shared NAT doesn't throttle unrelated callers together. A request over budget throws the new TooManyRequestsError. Uses @fastify/rate-limit's low-level createRateLimit, not its automatic per-route wiring (global: true): that wiring only reaches routes registered after the plugin finishes booting, and registerPermissionAuth is synchronous, so a caller's very next line registering its own routes would silently never get rate-limited. createRateLimit is read lazily on the first real request instead, once .ready() has necessarily already resolved. Left off by default: this hook runs inside a shared framework with no single traffic profile that fits every consuming service, and several already rate-limit at their own infra/gateway layer instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Some credentials (API keys or session tokens) authorize a whole tenant rather than a subset of its brokers. A route can now opt out of broker scope resolution entirely with brokerScoped: false — x-broker-id is not read, an empty broker scope on the verdict/token is not treated as a denial, and request.brokerContext is left unset. Also replaces the ??= lazy-init in rate-limit-check.ts's createRateLimitCheck with a plain if-block for a more conventional read. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The directory had grown to ~20 flat files across two credential paths, a shared rate-limit feature and generic header helpers. Groups them into api-key/, session-token/, rate-limit/ and shared/, leaving only the public surface (register-permission-auth.ts, route-properties.ts, broker-context.ts, index.ts) and errors/ at the top level. No behavior change — only import paths moved; the package's own @wave-tech/framework/auth-permission export is unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
RoutePropertiesgainsacceptsSessionToken: routes that opt in acceptAuthorization: Bearer <sessionToken>in addition tox-api-key.WaveAuthSessionTokenVerifier, which checks the signature against wave-auth-api's JWKS (jose,RS256/ES256only, explicit algorithm allowlist against alg-confusion) and expiry, then readssub/brokers.permissionsclaim (a token must not survive key revocation as an authorization decision), and/auth/permissions/validateitself has no bearer-token path. A route'sacceptsSessionToken: trueis the authorization decision — it trusts the token's broker scope only.x-broker-idnarrows the token's broker scope the same way it narrows an API key's, and the existing single-broker ambiguity check applies identically.registerPermissionAuthfails fast at registration time if a route setsacceptsSessionTokenwithout asessionTokenVerifierconfigured.x-api-keycaller and route is unaffected.Test plan
pnpm run lintpnpm test(213/213 passing, including newwave-auth-session-token-verifier.spec.tsand thesession tokensblock inregister-permission-auth.spec.ts— valid/expired/wrong-key/missing-claims/HS256-confusion/malformed-JWT cases, plus route opt-in, broker narrowing, and the fail-fast config check)pnpm run buildtim-network-adapter) toacceptsSessionTokenis a separate follow-up once a concrete caller needs it🤖 Generated with Claude Code