Make the /siwe surface audience-aware for cert enrollment#15
Merged
Conversation
step-ca enrolls certificates against dauth's /siwe OIDC provisioner and
validates the sign-in token as an id_token, requiring its own clientID
("step-ca") in the token's `aud`. Today /siwe mints a fixed audience from
JWT_AUDIENCE, so enrollment tokens are rejected on audience before the
on-chain webhook is ever consulted.
Let a caller request a specific, allow-listed `aud` at challenge time while
leaving the default path unchanged:
- SIWE_ALLOWED_AUDIENCES (comma list) is an allow-list of `aud` values a
caller may request; empty means no override is permitted. JWT_AUDIENCE
stays the required default.
- POST /siwe/challenge accepts an optional `audience` array, validated
against the allow-list and bound to the nonce at challenge time so it
cannot be swapped at /token. A disallowed request returns 400 and creates
no usable nonce.
- Issuer.Issue takes a per-issuance audience override; empty falls back to
the configured default.
- The nonce Challenge carries the bound audience; Postgres gains a TEXT[]
audience column with an idempotent ALTER TABLE so existing deployments
self-migrate.
The /exchange data-plane is unaffected: its inbound JWT middleware validates
the /siwe signature only (not iss/aud), and /siwe still signs with the /siwe
keyset alone, preserving the two-keyset boundary.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.
Why
step-ca enrolls certificates against dauth's
/siweOIDC provisioner and validates the sign-in token as an id_token, requiring its own clientID (step-ca) in the token'saud. Today/siwemints a fixed audience fromJWT_AUDIENCE(dimo), so enrollment tokens are rejected on audience before the on-chain connection webhook is ever consulted.This makes the
/siwesurface audience-aware: a caller can request a specific, allow-listedaudat challenge time, while the default behavior is unchanged. It's a minimal, additive spike to unblock enrollment — not a redesign of the audience model.What
SIWE_ALLOWED_AUDIENCES(comma list) — allow-list ofaudvalues a caller may request. Empty (default) means no override is permitted.JWT_AUDIENCEstays the required default audience.POST /siwe/challengeaccepts an optionalaudience(string array). Every value must be on the allow-list or the challenge is rejected with400 invalid_requestand no usable nonce is created. The audience is bound to the nonce at challenge time, so it can't be swapped at/token. Omitted → the configured default.Issuer.Issue(account, audience)takes a per-issuance override; empty falls back to the configured default.Challengecarries the bound audience. In-memory store gets it for free; Postgres gains aTEXT[] audiencecolumn with an idempotentALTER TABLE … ADD COLUMN IF NOT EXISTSso existing deployments self-migrate.Safety / invariants (verified in code)
/exchangedata-plane unaffected — its inbound JWT middleware validates the/siwesignature only, notiss/aud(internal/exchange/middleware/auth.go). Widening the/siweaudience does not touch the authZ path./siwestill signs with the/siwekeyset alone.SIWE_ALLOWED_AUDIENCESunset, any requested audience is rejected; step-ca's environment must set it for enrollment to work.Tests
token: override wins / empty falls back to default.server: default path, allow-listed override →aud:["step-ca"], rejected non-allow-listed → 400 with no nonce, rejected when no allow-list configured, and audience-bound-at-challenge (a/tokenrequest can't change it).nonce/postgres: audience round-trips; theALTER TABLEupgrade path was manually verified to add the column to a pre-existing old-schema table.Docs: README env table +
audrow +/siwe/challengenote updated; Swagger regenerated. OIDC discovery doc needed no change (it doesn't enumerate audiences).🤖 Generated with Claude Code