Pluggable persistence boundary for issuer-bound OAuth credentials.
ExMCP deliberately does not ship a file-backed secret store. Applications
can provide an OS keychain, encrypted database, or other appropriate adapter
as either AdapterModule or {AdapterModule, adapter_state}.
Registration records are stored under a versioned key containing the exact
authorization-server issuer and client ID. Adapters may maintain a separate
index from a non-secret local context (normally the MCP resource URL) to that
key, but fetch_registration/3 validates both the returned key and record
before credentials can be reused.
Token keys include the exact issuer and client ID plus resource, audience, subject/client identity, and granted scopes. Raw access and refresh tokens are never part of a key.
Legacy records without an issuer are rejected with
{:credential_migration_required, kind}. Call bind_legacy_registration/2
or bind_legacy_token/2 only after independently establishing the issuer;
ExMCP never attaches an unkeyed record to the currently discovered issuer.
Adapter contract
An adapter implements this behaviour. context must be a stable,
non-secret local identifier. A registration fetch returns the exact storage
key with the record so ExMCP can reject a corrupt or cross-issuer index.
Summary
Functions
Explicitly binds a legacy registration after its issuer is independently verified.
Explicitly binds a legacy token after every partition field is independently verified.
Fetches and validates a registration for an exact issuer.
Fetches a token only from its complete authorization partition.
Persists a validated issuer-bound registration.
Persists a token under its complete, non-secret authorization key.
Builds the versioned persistence key for a client registration.
Builds a complete token partition key without including token material.
Types
Callbacks
@callback fetch_token(token_key(), adapter_state :: term()) :: {:ok, ExMCP.Authorization.CredentialStore.Token.t() | map()} | :not_found | {:error, term()}
@callback put_registration( context(), registration_key(), ExMCP.Authorization.CredentialStore.Registration.t(), adapter_state :: term() ) :: :ok | {:error, term()}
@callback put_token( token_key(), ExMCP.Authorization.CredentialStore.Token.t(), adapter_state :: term() ) :: :ok | {:error, term()}
Functions
@spec bind_legacy_registration(map(), String.t()) :: {:ok, ExMCP.Authorization.CredentialStore.Registration.t()} | {:error, term()}
Explicitly binds a legacy registration after its issuer is independently verified.
@spec bind_legacy_token(map(), map()) :: {:ok, ExMCP.Authorization.CredentialStore.Token.t()} | {:error, term()}
Explicitly binds a legacy token after every partition field is independently verified.
@spec fetch_registration(store(), context(), String.t()) :: {:ok, ExMCP.Authorization.CredentialStore.Registration.t()} | :not_found | {:error, term()}
Fetches and validates a registration for an exact issuer.
@spec fetch_token(store(), ExMCP.Authorization.CredentialStore.Token.t() | map()) :: {:ok, ExMCP.Authorization.CredentialStore.Token.t()} | :not_found | {:error, term()}
Fetches a token only from its complete authorization partition.
@spec put_registration( store(), context(), ExMCP.Authorization.CredentialStore.Registration.t() | map() ) :: :ok | {:error, term()}
Persists a validated issuer-bound registration.
@spec put_token(store(), ExMCP.Authorization.CredentialStore.Token.t() | map()) :: :ok | {:error, term()}
Persists a token under its complete, non-secret authorization key.
@spec registration_key(String.t(), String.t()) :: {:ok, registration_key()} | {:error, term()}
Builds the versioned persistence key for a client registration.
@spec token_key(ExMCP.Authorization.CredentialStore.Token.t() | map()) :: {:ok, token_key()} | {:error, term()}
Builds a complete token partition key without including token material.