KemalIdentity::OIDC::PendingCodec
Turns a Pending into one signed string, and back.
Why this is not left to the application
A flow's state has to survive a round trip through the provider, so it has to go somewhere:
a table keyed by state, or a cookie. The cookie needs no schema, which makes it the
obvious choice and also the one with a sharp edge — it holds the PKCE verifier, and an
application that reaches for to_json and a plain cookie has just put a secret where the
browser can read it and anybody can rewrite it.
So: signed with the application's key, and never treated as trustworthy input. A value that
does not authenticate is nil, not an exception and not a partially-parsed struct.
What it is not
Not encryption. The payload is signed, not sealed, so the browser can read the verifier
it is carrying. That is fine and is how PKCE works in a browser flow: the verifier proves
that whoever redeems the code is whoever started the flow, and the browser is that party.
What matters is that nobody can change it — swapping in their own state or nonce is
precisely the attack, and the signature is what stops it.
Store the result in a cookie that is HttpOnly, Secure, SameSite=Lax and scoped to the
callback path, and delete it as soon as the flow completes.
Constants
Domain separation from every other thing signed with the same key. A signature that is valid in two places is a signature that can be moved between them.
Constructors
Instance methods
The Pending inside value, or nil if it does not authenticate.
Everything here arrives from a browser, so nothing raises: a truncated cookie, one signed
with a rotated key, one somebody edited, and one that is not a cookie at all all produce
the same nil.
payload.signature, both base64url.
Raises ArgumentError for a flow too large to be carried, rather than returning a value
#open? will refuse. The cap is checked on both sides on purpose: a cookie that seals and
does not open is a login that silently never completes, and the browser would be holding
the evidence.