ADORSYS-GIS / ADORSYS-GIS/lightbridge-authz
[Story]: Enforce the audience claim on the usage query listener (and decide whether it goes platform-wide)
- Linguagem predominante
- Rust
- Estrelas
- 0
- Forks
- 1
- Merge médio
- 6h 42min
- PRs com merge (30d)
- 246
Descrição
### Story Statement
As the operator of the usage query API, I want the bearer tokens it accepts to be bound to an intended audience, so that a token minted for a different client cannot be replayed against tenant usage and spend data.
### Real Intent
#603 gave `/usage/v1/usage/query` a real authentication gate (JWKS-validated bearer + ownership check). The adversarial security review of that PR found the gate accepts **any** RS256 token from the configured JWKS, because `oauth2.audience` is unset and `lightbridge-authz-bearer` therefore runs with `validate_aud = false`.
No cross-tenant escalation was constructible — whatever `sub` arrives still routes through the ownership authority and reaches only that subject's own accounts — so this is hardening, not an open hole. But it is a replay surface on a data-plane route that now *has* a bearer gate to hang the check on, which is exactly when the check is cheap. The reviewer's words: *"the cheap hardening now that this listener has a bearer gate is to set `oauth2.audience`."*
There is a second, larger question underneath: `aud` is unenforced **repo-wide** (ADR-0025 records it), so this story should decide whether the usage listener is a one-off or the first step of a platform-wide posture change.
### Background and Context
- `crates/lightbridge-authz-bearer/src/lib.rs` enforces `aud` only when `oauth2.audience` is configured; every current config leaves it unset.
- Prod sets `oauth2.audience` values elsewhere in the estate (`ai-helm-values` `lightbridge-app.yaml` carries an audience list), so the plumbing exists — this is a configuration + enforcement decision, not new machinery.
- Related but distinct: ADR-0030's `client_credentials` tokens honour RFC 8707 `audience` against `allowed_audiences`, and that ADR explicitly records that `allowed_audiences` is **not** a privilege boundary — containment is the empty permission set. Enforcing `aud` on resource servers is the other half of that picture.
### Source of truth (links)
- lightbridge-authz#603 (the PR whose security review raised this; the finding is quoted in the PR body under Security Considerations)
- ADR-0025 (records that `aud` is not enforced)
- ADR-0030 §"allowed_audiences is not a privilege boundary"
- #570 (the ownership gate this sits on top of)
### Acceptance Criteria
- [ ] A decision is recorded (ADR amendment or issue comment by the maintainer) on whether `aud` enforcement is **usage-listener-only** or **platform-wide**; the rest of the criteria follow that choice.
- [ ] `oauth2.audience` is set for the usage service and enforced — a token whose `aud` does not match is refused with the same opaque 401/403 as any other bearer failure (no new error taxonomy, no leak of which check failed).
- [ ] A test, written to fail first, proves a valid-signature token bearing the wrong `aud` is refused by `/usage/v1/usage/query`.
- [ ] A companion `ai-helm-values` change lands in the same rollout, or the story explicitly records why the deployed config already satisfies it. (The usage overlay was extended in ai-helm-values#332; this adds one key to the same block.)
- [ ] If the decision is platform-wide: the same enforcement and refusal test exist for `authz-api` and `authz-budget`, and the migration ordering is stated (tokens in flight at cutover must not be silently rejected).
### Out of Scope
- Changing how `authz-idp` mints `aud`/`azp` (ADR-0011 Decision 5 governs that; `client_credentials` is the one grant where `aud` may differ from `client_id`, per ADR-0030).
- The local-stack trust-root drift — separate story.
### Dependencies and Blockers
None blocking. Sits on top of merged #603/#570. If the maintainer chooses platform-wide, coordinate the rollout with the values repo.
### Assumptions
1. Every current token issuer stamps an `aud` that the resource servers can be configured to expect — **verify before enforcing**, because an issuer that omits `aud` entirely would be refused outright once `validate_aud` flips on. This is the assumption most likely to bite.
### Implementation Notes
- The bearer service already supports this; the work is configuration, a decision, and tests.
- Keep the refusal opaque and fail-closed — the usage query handler's existing 401/403 shape is the model.
### Test Expectations
Fail-first refusal test per surface enforced; a positive test that a correctly-audienced token still passes; if platform-wide, one refusal test per service.
### Verification evidence
To be supplied by the implementing PR: the fail-first output (test red before the enforcement, green after) and the rendered config showing the audience key.
### Human accountable owner
Stephane Segning Lambou
### AI Usage Declaration
AI-assisted (Claude): this issue was drafted from a finding raised by an adversarial security review of #603 during the 2026-08-31 working session. The finding is quoted, not invented; no code has been written for it. Human review and prioritisation pending.
Guia de contribuição
Avaliação
Esta issue ainda não foi avaliada.