ConduitMcp.Client.OAuth.Error exception (ConduitMCP v0.11.0)

Copy Markdown View Source

The error type of the OAuth 2.1 client (ConduitMcp.Client.OAuth.*).

Every OAuth helper that can fail returns {:error, %ConduitMcp.Client.OAuth.Error{}}. Match on :reason, which is one of the closed set in reasons/0.

Fields

  • :reason - an atom from reasons/0.
  • :data - optional, non-secret context: the metadata field that failed (%{field: "token_endpoint", scheme: "javascript"}), a parser detail atom, or, for authorization-response errors, the server's error, error_description and error_uri strings exactly as received. It may hold server-supplied text, so message/1 never renders it.

Secrets

No OAuth helper ever puts a token, refresh token, authorization code, state or PKCE verifier into an error, and message/1 renders only the reason and a fixed description. The message is safe to log.

Reasons

Parsing and URL rules (Challenge, Resource, Discovery, Pending):

  • :invalid_challenge - a WWW-Authenticate value is malformed or over a limit; :data is the detail atom (see ConduitMcp.Client.OAuth.Challenge).
  • :invalid_url - a URL does not parse, has no host, carries user info, or (for an issuer) has a query or fragment; :data names the problem.
  • :insecure_url - a URL that must be https is not (plain http is only admitted for loopback hosts, and for anything but a redirect URI only behind allow_insecure: true).

Discovery and metadata validation (Discovery, PKCE):

  • :resource_mismatch - Protected Resource Metadata resource is not exactly the canonical server URI (RFC 9728 §3.3).
  • :no_authorization_server - the PRM lists no authorization server.
  • :issuer_not_allowed - the chosen authorization server is not in the configured expected_issuers allowlist.
  • :invalid_metadata - PRM or authorization-server metadata is not an object or a field has the wrong type; :data names the field.
  • :issuer_mismatch - authorization-server metadata issuer is not exactly the issuer the metadata URL was built from (RFC 8414 §3.3).
  • :pkce_unsupported - code_challenge_methods_supported is absent or lacks "S256".
  • :insecure_endpoint - an endpoint in the authorization-server metadata is not https (this is also how javascript:, file:, data: and every other scheme is refused); :data names the field and scheme.

Authorization response (Callback):

  • :invalid_state - state is missing or does not match.
  • :principal_mismatch - the callback is completed under a different principal than the one that started the flow.
  • :state_expired - the pending authorization outlived its TTL.
  • :iss_mismatch - the response iss is not exactly the recorded issuer (RFC 9207 §2.4). Nothing else from the response is surfaced.
  • :iss_missing - the authorization server advertises authorization_response_iss_parameter_supported but sent no iss.
  • :missing_code - neither code nor error is present.
  • :invalid_pending - a stored pending authorization could not be restored by ConduitMcp.Client.OAuth.Pending.from_map/1; :data names the field.
  • :access_denied, :invalid_request, :unauthorized_client, :unsupported_response_type, :invalid_scope, :server_error, :temporarily_unavailable - the authorization server returned that RFC 6749 §4.1.2.1 error code.
  • :authorization_error - the authorization server returned any other error code; it is in :data as a string, never as an atom.

Runtime (produced by the HTTP-layer OAuth flow, defined here so the set is closed in one place):

  • :authorization_required - interactive authorization is needed.
  • :no_protected_resource_metadata - every PRM candidate URL failed.
  • :no_authorization_server_metadata - every AS-metadata candidate failed.
  • :authorization_server_changed - pre-registered credentials are bound to a different issuer than the one the server now names.
  • :no_registration_method - no pre-registered client, CIMD or DCR applies.
  • :registration_failed - dynamic client registration was refused.
  • :invalid_grant - the token endpoint rejected the code or refresh token.
  • :token_request_failed - the token endpoint failed otherwise (data: %{reason: :invalid_token_response} for a malformed answer, an access token holding anything but visible ASCII included).
  • :step_up_limit - the step-up attempt limit for a resource and scope set was reached (see ConduitMcp.Client.OAuth.Scopes.step_up_allowed?/4).
  • :url_blocked - ConduitMcp.Client.URLPolicy refused a server-supplied URL before any connection was made (a private, loopback or otherwise internal address, or an unresolvable host); :data is the policy's reason.
  • :client_secret_unavailable - a client secret configured as {:system, "VAR"} names an unset environment variable; :data is the variable name.

Summary

Functions

{:error, new(reason, data)}.

Builds an error, raising ArgumentError when reason is not in reasons/0.

Every valid :reason, in documentation order.

Types

reason()

@type reason() ::
  :invalid_challenge
  | :invalid_url
  | :insecure_url
  | :resource_mismatch
  | :no_authorization_server
  | :issuer_not_allowed
  | :invalid_metadata
  | :issuer_mismatch
  | :pkce_unsupported
  | :insecure_endpoint
  | :invalid_state
  | :principal_mismatch
  | :state_expired
  | :iss_mismatch
  | :iss_missing
  | :missing_code
  | :invalid_pending
  | :access_denied
  | :invalid_request
  | :unauthorized_client
  | :unsupported_response_type
  | :invalid_scope
  | :server_error
  | :temporarily_unavailable
  | :authorization_error
  | :authorization_required
  | :no_protected_resource_metadata
  | :no_authorization_server_metadata
  | :authorization_server_changed
  | :no_registration_method
  | :registration_failed
  | :invalid_grant
  | :token_request_failed
  | :step_up_limit
  | :url_blocked
  | :client_secret_unavailable

t()

@type t() :: %ConduitMcp.Client.OAuth.Error{
  __exception__: true,
  data: term(),
  reason: reason()
}

Functions

error(reason, data \\ nil)

@spec error(reason(), term()) :: {:error, t()}

{:error, new(reason, data)}.

new(reason, data \\ nil)

@spec new(reason(), term()) :: t()

Builds an error, raising ArgumentError when reason is not in reasons/0.

iex> ConduitMcp.Client.OAuth.Error.new(:pkce_unsupported).reason
:pkce_unsupported

reasons()

@spec reasons() :: [reason(), ...]

Every valid :reason, in documentation order.