An authorization-code flow between the authorization request and the callback: everything the client must remember to validate the response and redeem the code.
new/1 generates the two secrets of the flow: a state of 32 random bytes
(base64url, 43 characters) and a PKCE code verifier (ConduitMcp.Client.OAuth.PKCE.verifier/0),
both wrapped in ConduitMcp.Client.Secret, and stamps a 10 minute TTL. It
records the issuer the flow was started against (for the RFC 9207 iss
check), the canonical resource, the redirect URI (the token request must
repeat it exactly), the requested scopes and the principal - the end-user
or session identity of the host - that the flow is bound to.
The host stores it
Pending is plain data the host keeps between the redirect and the
callback: in its session store, a database row, or an encrypted cookie.
to_map/1 gives a string-keyed map of strings, integers, booleans and
lists (plus the principal as given) for that; from_map/1 validates and
restores it. Restore the struct before calling
ConduitMcp.Client.OAuth.Callback.validate/3.
The map holds secrets in plain text (state and code_verifier).
Keep it server-side, or encrypt it (for example with
Plug.Crypto.encrypt/4); signing alone does not hide it. inspect/1 of
the struct shows both as #Secret<redacted>.
Single use is the host's job
state must be usable exactly once (OA-2). Because Pending is data, the
library cannot see a second copy being presented, so a replayed callback
is rejected only if the host's store enforces it: look the pending entry
up by state and delete it in the same step, before validating, and on
failure as well as on success. ConduitMcp.Client.OAuth.Callback.validate/3
checks the state value, principal binding, expiry and iss; it does not
and cannot check consumption.
Serialization of the principal
The principal is stored in to_map/1 exactly as given. A host that
JSON-encodes the map should use a string principal.
Summary
Functions
The PKCE S256 code challenge for the pending verifier.
Whether the pending authorization has expired at Unix time now.
Restores a map from to_map/1. Every field is type-checked; a missing or
malformed one gives :invalid_pending with %{field: name} as data (the
value itself is never put in the error). The expiry is not checked here.
Starts a pending authorization with a fresh state and code verifier.
The pending authorization as a string-keyed map for host storage. It
contains state and code_verifier in plain text; see the moduledoc.
Types
@type t() :: %ConduitMcp.Client.OAuth.Pending{ client_id: String.t() | nil, created_at: integer(), expires_at: integer(), iss_parameter_supported: boolean(), issuer: String.t(), principal: term(), redirect_uri: String.t(), resource: String.t(), scopes: [String.t()], state: ConduitMcp.Client.Secret.t(), token_endpoint: String.t() | nil, verifier: ConduitMcp.Client.Secret.t() }
:state,:verifier- the flow's secrets.:redirect_uri- sent in the authorization request; must be repeated in the token request.:issuer- the authorization server'sissuer, as validated.:resource- the canonical resource URI (RFC 8707resource).:scopes- the requested scopes.:principal- the host identity the flow is bound to (nilif none).:iss_parameter_supported- the AS metadata'sauthorization_response_iss_parameter_supportedwastrue.:client_id,:token_endpoint- the client identity and token endpoint the flow was started with, so the code is redeemed at the same authorization server (nilwhen not recorded).:created_at,:expires_at- Unix seconds.
Functions
The PKCE S256 code challenge for the pending verifier.
Whether the pending authorization has expired at Unix time now.
@spec from_map(term()) :: {:ok, t()} | {:error, ConduitMcp.Client.OAuth.Error.t()}
Restores a map from to_map/1. Every field is type-checked; a missing or
malformed one gives :invalid_pending with %{field: name} as data (the
value itself is never put in the error). The expiry is not checked here.
Starts a pending authorization with a fresh state and code verifier.
Raises NimbleOptions.ValidationError on invalid options.
Options
:redirect_uri- Required. AnhttpsURL, orhttpon a loopback host (RFC 8252 §7.3); no fragment.:issuer(String.t/0) - Required. The validatedissuer.:resource(String.t/0) - Required. The canonical resource URI.:scopes(list ofString.t/0) - The requested scopes. The default value is[].:principal(term/0) - The host identity to bind the flow to. The default value isnil.:iss_parameter_supported(boolean/0) - The AS advertisesauthorization_response_iss_parameter_supported: true. The default value isfalse.:client_id- The client id used. The default value isnil.:token_endpoint- The validated token endpoint. The default value isnil.:ttl(pos_integer/0) - Lifetime in seconds. The default value is600.:now(integer/0) - Current Unix time in seconds (default: the system clock).
The pending authorization as a string-keyed map for host storage. It
contains state and code_verifier in plain text; see the moduledoc.