docs: record the bound_audiences requirement for JWT auth - #174
Merged
Conversation
Checked the implementation against Vault's JWT auth documentation. The client
conforms -- the login endpoint, body, and the optional `role` with its
`default_role` fallback all match the API reference (the concept page calls
`role` required, the API reference does not; the API reference is normative and
we already test the default_role path). What was missing was on our side of the
docs.
Vault requires a `jwt` role to bind the audience its tokens carry, and does not
catch the omission at role-creation time: a role with any other bound constraint
is accepted, and the failure appears only at login. Reproduced against a live
Vault:
role with no bound constraint -> 400 at create, "must have at least one
bound constraint when creating/updating a role"
role with bound_subject, no aud -> created (204)
login with a JWT carrying aud -> 400 "audience claim found in JWT but no
audiences bound to the role"
Our own GitHub Actions example walked into this: it uses
core.getIDToken('vault'), which mints aud: vault, so the role must bind that
audience, and the README never said so. Anyone copying the snippet and creating
a role without it got a 400 whose wording does not obviously point at the role.
The JWT section now carries the requirement, both error strings verbatim so they
are searchable, and a role-creation example; the GitHub Actions section spells
out the audience it mints. Also recorded that Vault does not require an `exp`
claim -- a token minted without one is accepted and never expires -- which
matters for anyone writing their own jwtProvider.
Every quoted string was taken from a live Vault and verified identical on 1.21
and 2.0. No code change; the client already surfaces these as VaultHttpError
with Vault's own message intact.
Signed-off-by: kurok <22548029+kurok@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checked our JWT implementation against Vault's JWT auth docs and the API reference.
The client conforms
POST auth/jwt/loginPOST /auth/${mount}/login, default mountjwtjwtrequired,roleoptional{ jwt }, addsroleonly when setdefault_roleif not provided"roleentirely when unsetjwt, oidc explicitly out of scopeThe two HashiCorp pages disagree on
role: the concept page calls it required, the API reference calls it optional with adefault_rolefallback. We follow the API reference, which is normative — and that path is already covered by a unit test, a demo scenario and the README.No code change here.
What was missing — on our side
Vault requires a
jwtrole to bind the audience its tokens carry, and does not catch the omission when the role is created. Reproduced against a live Vault:So the naive mistake is blocked, but the realistic one is not.
Our own GitHub Actions example walked straight into it. It uses
core.getIDToken('vault'), which mintsaud: vault, so the Vault role must bind that audience — and the README never said so. Anyone copying that snippet and creating a role withoutbound_audiences=vaultgot a 400 whose wording doesn't obviously point at their role config.Change
vault write .../role/...example that binds the audience, and the note that anaudarray matches on any entry.core.getIDToken('vault')mints, with a matching role example, and warns that callinggetIDToken()with no argument uses GitHub's default audience instead.expclaim — a token minted without one is accepted and never expires, which matters for anyone writing their ownjwtProvider.Verification
Every quoted string was taken from a live Vault, not from the docs, and checked on both supported lines:
must have at least one bound constraint when creating/updating a roleaudience claim found in JWT but no audiences bound to the roleerror validating token: invalid audience (aud) claim: audience claim does not match any expected audienceIdentical on both, so the docs don't need version qualifiers.
I deliberately did not add a test asserting these strings. It would be in this repo's style, but it couples CI to third-party error prose and would fail on a Vault reword that is not our bug — a false alarm rather than a caught defect. The strings are stable across both supported lines today, and the CHANGELOG records where they came from.
lint clean, 391 unit passing, and both README-drift guards still green (#162's error table, #164's public-API coverage).