Documentation
¶
Overview ¶
Package oidctest is an httptest fake OIDC provider for exercising the `behalf login` flow with no real IdP and no browser.
TEST-ONLY, like internal/testkeys: nothing here may run outside tests. The server implements the minimum conformant surface the flow touches — discovery, JWKS, an authorize endpoint that 302s straight back to the client's loopback redirect with code+state (the "user" approves instantly), and a token endpoint that checks PKCE (S256) and mints an RS256 ID token echoing the requested nonce, per OIDC Core's mandatory nonce return.
Knobs (set before driving a flow) make it adversarial: MintNonce rewrites the echoed nonce (nonce-mismatch rejection) and SignWith substitutes a signing key that is not in the published JWKS (wrong-signature rejection).
NewDeterministic is the reproducible variant: an Ed25519 signing key from a fixed seed, a fixed clock, and an advertised issuer that is a stable name rather than an ephemeral 127.0.0.1 port — so the ID token it mints, and therefore its digest and everything a receipt records about it, is the same bytes every run. cmd/behalf-record needs that to keep its recordings byte-deterministic while performing a genuine login (Q92, D9.2).
Index ¶
Constants ¶
const TokenTTL = time.Hour
TokenTTL is how long an ID token this provider mints stays valid. It is exported because a caller recording a chain has to state the credential's `exp` verbatim (Q23) and must not guess it.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DeterministicOptions ¶
type DeterministicOptions struct {
// Issuer is the advertised issuer and endpoint base, e.g.
// "https://login.demo.internal". It is a NAME, not an address: nothing
// listens there. Drive the flow with Client(), which routes it to the
// local test server.
Issuer string
// Seed derives the provider's Ed25519 signing key.
Seed string
// Sub is the authenticated subject.
Sub string
// At is the fixed instant stamped into iat, and exp one hour later.
At time.Time
// AuthTime and AMR, if set, become ID-token claims.
AuthTime int64
AMR []string
}
DeterministicOptions configures NewDeterministic. Every field is pinned on purpose: an ephemeral port in the issuer, a random signing key or a wall-clock `iat` would each, on their own, make the ID token — and so its digest, and so every receipt that references it — different on every run.
type Server ¶
type Server struct {
// URL is the issuer.
URL string
// Key is the provider's RSA signing key; its public half is published
// at /jwks.
Key *rsa.PrivateKey
// KID is the published key id.
KID string
// MintNonce, if set, transforms the nonce echoed into the ID token
// (default: echo verbatim, as OIDC Core requires).
MintNonce func(requested string) string
// SignWith, if set, signs ID tokens with this key instead of Key —
// a signature the published JWKS cannot verify.
SignWith *rsa.PrivateKey
// Sub is the authenticated subject (default "user-1234").
Sub string
// AuthTime and AMR, if set, are included as ID-token claims.
AuthTime int64
AMR []string
// Now overrides the clock stamped into iat and exp. Nil means time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
Server is a fake OIDC provider.
func NewDeterministic ¶
func NewDeterministic(o DeterministicOptions) *Server
NewDeterministic starts a fake provider whose ID tokens are byte-identical across runs given the same options. Callers must Close it.
TEST AND DEMO MATERIAL ONLY, like the rest of this package: the signing key is derived from a public seed and secures nothing.
func (*Server) Client ¶
Client returns an HTTP client that resolves this provider's advertised issuer to the address it actually listens on, and passes everything else (the loopback redirect above all) through untouched.
This is what makes a stable issuer possible without binding a fixed port: the flow is a real HTTP flow, the issuer in the discovery document, the ID token and the receipt is a stable name, and only the transport knows where that name lives.