Bounded, node-local single-use state for OAuth authorization-code flows.
Transactions move atomically through pending -> code_ready -> redeemed.
The store retains only SHA-256 digests of state values and authorization
codes, so neither value appears in the store's retained process state or
crash reports.
The built-in store is intentionally node-local because the loopback callback flow is owned by one ExMCP client process. Applications implementing a distributed browser callback should terminate that callback on the same node or provide their own end-to-end authorization provider.
Summary
Functions
Invalidates a transaction after a failed or abandoned flow.
Returns a specification to start this module under a supervisor.
Atomically marks an authorization code as redeemed with its exact redirect URI.
Registers a new authorization transaction using a digest of its random state.
Atomically validates and consumes one callback for a transaction.
Types
@type callback_error() :: :state_mismatch | :missing_authorization_code | :missing_callback_issuer | :missing_expected_issuer | :authorization_transaction_not_found | :authorization_transaction_replayed | :oauth_transaction_store_unavailable | {:issuer_mismatch, keyword()}
@type state() :: %{ entries: %{optional(transaction_id()) => entry()}, max_entries: pos_integer(), ttl_ms: pos_integer() }
@type transaction_id() :: String.t()
Functions
@spec abort( transaction_id(), keyword() ) :: :ok
Invalidates a transaction after a failed or abandoned flow.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec redeem_code(transaction_id(), String.t(), String.t(), keyword()) :: :ok | {:error, :authorization_transaction_not_found | :authorization_code_not_ready | :authorization_code_replayed | :authorization_code_mismatch | :redirect_uri_mismatch | :oauth_transaction_store_unavailable}
Atomically marks an authorization code as redeemed with its exact redirect URI.
@spec register(String.t(), String.t() | nil, String.t(), keyword()) :: {:ok, transaction_id()} | {:error, term()}
Registers a new authorization transaction using a digest of its random state.
@spec start_link(keyword()) :: GenServer.on_start()
@spec validate_callback(transaction_id(), map(), keyword()) :: {:ok, String.t()} | {:error, callback_error()}
Atomically validates and consumes one callback for a transaction.