Expand description
An OpenID Connect relying party — the half of docs/10
D6 that 48 §48.13 named as unbuilt.
crate::identity made an actor a decision of the runtime and offered two providers, both of
which the process can also mint for: DevIdentity believes a claim and SignedIdentity
verifies a shared secret. Neither suits a public identity provider, because a process that can
verify a credential it could also have issued cannot tell “this user authenticated” from “this
process said so”. This module is the asymmetric answer: the signature is checked against a
public key fetched from the issuer, so the only thing that can produce a credential is the
issuer.
§What is here
- Discovery —
{issuer}/.well-known/openid-configuration, which supplies the authorization, token and JWKS endpoints so an operator configures one URL rather than four. - A key set, fetched over TLS, cached, and refetched when a
kidmisses. - ID-token verification — RS256/384/512, PS256/384/512, ES256 and ES384, and nothing else:
noneand the HMAC family are refused by name, because “verify with whatever the token says” is how a relying party is talked into treating a public key as a shared secret. - The claim checks, all of them, in one place: issuer, audience, authorized party, expiry, not-before, and the nonce when there is a nonce to check.
- The authorization-code flow with PKCE, so a browser can obtain a token in the first place.
§Where the trust actually comes from
Two links, and both are load-bearing. The signature says the issuer produced the token. TLS
says the key set came from the issuer — there is nothing else protecting it, which is why the
issuer must be an https URL and why there is no flag to relax that. crate::outbound is
what makes the second link real, and its own tests are where a handshake is actually performed:
this module is tested against a scripted [beck_core::net::Outbound], because a relying party
tested against a server written beside it tests agreement with itself
(docs/82 §82.10).
§What is deliberately not here
No session of Beck’s own: the cookie the flow sets is the ID token, so a session lasts as long as the issuer said it should and no longer. That makes token refresh unnecessary rather than missing — there is no local session to keep alive — and it makes logout the deletion of one cookie. §48.13 of the report says what it costs: a user is sent back to the issuer when the token expires, and an issuer that mints five-minute tokens will do that every five minutes.
No UserInfo request: the claims are the ID token’s. identity = managed() is built and is
beck-infra’s (§48.8) — what reaches here from it is one thing, Config::in_cluster, and its
field says what it costs.
Structs§
- Completion
- The browser came back and the token verified.
- Config
- What an operator states. Everything else is discovered.
- Login
- Where to send the browser, and what to remember while it is gone.
- Provider
- The endpoints, as the issuer published them.
- Refusal
- Why a token was refused, in the operator’s words.
- Relying
Party - A relying party: one issuer, one client, one key set.
- Verified
- What a verified ID token said.
Constants§
- CLOCK_
SKEW_ MS - How far the clock may be wrong before a token is refused for it.
- LOGIN_
WINDOW_ MS - How long a login may take between
/auth/loginand/auth/callback. - REFETCH_
FLOOR_ MS - The floor between two key-set fetches, whatever asks for one.
- REFRESH_
EVERY_ MS - How long a fetched key set is used before it is fetched again.
Functions§
- query_
params a=1&b=2, decoded. A repeated name keeps every value, and the reader takes the first — which is the behaviour that makes?code=good&code=evilunambiguous.