Skip to content

Decide what the broker claims when a deployment cannot bind a token to one product #37

Description

@kanushka

Problem Statement

The broker verifies that a token it hands a module is bound to the audience that module named. On Asgardeo that check cannot distinguish one product from another, and no configuration makes it able to.

internal/auth/narrowing.go refuses unless slices.Contains(facts.Audiences, request.Audience). #17's definition of done states the intent: "the issued token's audience covers the requested product audience". Measured against both supported products on 2026-08-06:

Product What lands in an access token's aud
Asgardeo The client ID, always. Not configurable — the Audience field on the application's Protocol tab belongs to the ID token and does not reach access tokens.
Identity Server 7.3.0 The client ID, plus every entry of the application's audience list — so the API resource identifier, once registered there.

Recorded in docs/research/asgardeo-redirect-uri-and-scope-narrowing.md §3 and §3.1, and in sections 2.5 and 3.5 of the login walkthrough.

The consequence is narrow but real. On Asgardeo, products.<namespace>.audience must be set to the client ID or every acquisition refuses with auth.narrowing_unavailable. One identity serving three product namespaces therefore carries the same audience in all three, and the check passes for all three regardless of which product asked. It still proves the token was minted for this client. It cannot prove the token was minted for this product.

A check that cannot fail is worth naming rather than leaving to be discovered. #35 fixed exactly this class of defect one layer up: the live smoke run compared a token's scopes against a request that was the whole session, so its narrowing verification held however the deployment behaved. This is the same shape, in the shipped broker rather than in a test, and bounded to one product.

What is not in question

  • The broker's behavior is correct as written. It verifies rather than assumes, and refuses what it cannot prove. That is right under every outcome below and nothing here proposes changing it.
  • Identity Server needs nothing. Register the resource identifier in the application's audience list and the check means what it says.
  • This is not a defect report. Both products work today. The question is what the shell should claim about what it proved.

The decision

What should the shell do when a supported deployment structurally cannot bind an access token to anything narrower than the client?

Option 1 — Accept and document. Status quo. The walkthrough already states the per-product rule and the research document records why. Costs nothing. Leaves a check that reads as product-scoped and is client-scoped on one of the two products, discoverable only by reading section 2.5.

Option 2 — Name what was proven. Keep accepting, but have the shell distinguish "audience verified as product-specific" from "audience verified as client-wide" wherever it reports what it did. No refusal, no schema change; the weaker guarantee stops being invisible.

Option 3 — Require the weakening to be written down. The context document declares, per identity, that the deployment binds audiences only to the client — for example an explicit audienceBinding field. An audience equal to the client ID is then a decision someone recorded, not a coincidence of pasting the same string into two fields. Costs a schema addition and a migration note. Closest in spirit to the existing rule that writing configuration grants nothing.

Option 4 — Refuse a client-wide audience. Reject an audience equal to the client ID. Makes the guarantee uniform and makes Asgardeo unusable. Listed for completeness; recommended against.

Recommendation: Option 2, with Option 3 if the audience check is ever load-bearing for isolation between namespaces on one identity. Option 1 is a reasonable deferral as long as this issue stays open to record that it was a choice.

Notes

  • Whatever is decided touches section 4.3 of the login walkthrough, where audience is defined, and section 2.5, which currently explains the Asgardeo constraint as a registration fact rather than as a design position.
  • Asgardeo's behavior was measured on one trial tenant. It is not documented by Asgardeo and could change; the verdict cells are per-deployment and say so.
  • Organization switching and resource-first (RFC 9728) discovery are both out of scope for the first slice and may change what an audience can mean later. That is an argument for deciding what the shell claims now rather than encoding a policy that a later slice invalidates.

Follows from #17, whose definition of done this clause belongs to. #17 has no other open work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority/NormalTasks with a normal priority levelType/QuestionHighlights queries or clarifications needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions