Skip to content

OAuth access tokens: serialize single-valued aud as a string; make multi-audience a policy-gated opt-in #200

@saucam

Description

@saucam

Problem

aud is modeled as a Go slice (domain/token.go:14Audience []string) and is always set from a []string (internal/service/credential.go:382). lestrrat-go/jwx serializes a slice as a JSON array regardless of length, so even a single-audience access token emits the array form:

"aud": ["https://rs.example.com"]

rather than the bare string "aud": "https://rs.example.com".

RFC 7519 §4.1.3 permits aud to be either a single case-sensitive string or an array. For the OAuth access-token profile a single string is the safer default: a multi-valued aud is a bearer pattern that enables lateral movement — a token valid for both RS-A and RS-B can be replayed from one to the other by whoever holds it. Sender-constraining via DPoP (RFC 9449) mitigates replay, but a sender-constrained token and a multi-audience token do not compose cleanly, so single-audience is the right default for the AT profile.

Proposed change

  1. For the OAuth access-token profile, serialize aud as a single string when there is exactly one value.
  2. Keep array semantics where SPIFFE JWT-SVID consumers expect them (JWT-SVID requires a non-empty array).
  3. Treat multi-audience as an explicit, policy-gated opt-in rather than the default path.
  4. No verify-side change needed — jwx handles the polymorphic (string-or-array) aud transparently on parse.

Relationship to other issues

References

  • RFC 7519 §4.1.3 — aud is a single string OR an array
  • RFC 8725 (JWT BCP) / RFC 9700 (OAuth 2.0 Security BCP) — audience-restriction guidance
  • Reported by Andrii Deinega via WIMSE WG review of ZeroID.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestspec-complianceDeviation from SPIFFE/WIMSE/JWT-SVID specs

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions