Token Audience Validation
also called Audience Restriction, aud Claim
Verifying that a signed token was issued for the service consuming it, without which a legitimately obtained low-privilege token is replayable against a high-privilege service.
A signed token proves that an identity provider issued it and that it has not been tampered with. It does not prove that it was issued for you. Without an audience check, any service trusting the same issuer will accept any token that issuer produced.
The attack that follows is not exotic. An attacker obtains a token legitimately — by signing up for a low-privilege service that uses the same identity provider — and presents it to a high-privilege service. The signature verifies, the expiry is valid, the issuer is trusted. Access is granted. This is privilege escalation through an entirely valid credential, and no signature verification catches it.
The full validation set
Every use of a signed token should verify:
- Signature, against the expected key — with the algorithm pinned by the verifier, never taken from the token. Accepting the token's own algorithm claim is how libraries have historically been persuaded to accept unsigned tokens, or to verify an asymmetric token using the public key as an HMAC secret.
- Expiry, with a small clock-skew tolerance.
- Issuer, against an allowlist.
- Audience, matching this service.
- Key identifier, resolved against a cached trusted key set that respects rotation — fetching per request makes the identity provider a hot-path dependency; never refreshing breaks everything at once when keys rotate.
- Authorization claims revalidated against current state where they matter, since a role captured at issue time is stale by definition.
Industry example
Platforms exposing many services behind one identity provider — a communications API with messaging, telephony, verification and billing services, or any multi-product SaaS with unified authentication — are exactly where this matters, because the same issuer serves services with very different privilege levels.
The design consequence is that each service must have a distinct audience identifier, and the identity provider must issue tokens scoped to the audience the client actually requested rather than a generic one. A platform that issues one broadly-scoped token for convenience has made audience validation impossible downstream, however carefully each service validates.
The same reasoning applies to internal service-to-service calls: a token minted for one internal service should not be usable at another, which requires per-service audiences rather than a single "internal" one.
Failure scenarios
- No audience claim issued at all, making validation impossible downstream.
- A single audience for the whole platform, which is equivalent to no audience.
- Validation implemented in one service and not others, so the weakest implementation defines the posture.
- Audience checked but issuer not, so a token from an untrusted provider with the right audience string passes.
- A shared library where validation is optional, and some callers omit it.
Trade-offs
Per-service audiences mean the client must know which service it is calling when it requests a token, which complicates token acquisition and can require multiple tokens for a multi-service operation. A token exchange mechanism resolves this and adds a component.
The alternative — a single broad token — is simpler and converts every service into a potential escalation path. For anything beyond a trivial deployment, the complexity is the correct trade.
Interview question
"An attacker signs up for your free tier, gets a valid token, and presents it to your enterprise admin API. The signature verifies and the token has not expired. What stops them, and what would you check across your services right now?"