Parameter extraction for the OAuth endpoints.
Plug hands over %{"key" => value} where a value may be a binary, a list
(the parameter was sent twice), or a nested map (key[sub]=…). Only a single
binary is ever a valid OAuth parameter, so anything else is rejected rather
than coerced — ?scope=mcp&scope=admin picking one arm is how two halves of
a server disagree about what was requested.
Every failure is a Noizu.MCP.Auth.Server.Errors struct, so the caller can
render it without composing a message of its own (and therefore without
reflecting input).
iex> Noizu.MCP.Auth.Server.Params.fetch(%{"client_id" => "abc"}, "client_id")
{:ok, "abc"}
iex> {:error, error} = Noizu.MCP.Auth.Server.Params.fetch(%{"a" => ["1", "2"]}, "a")
iex> error.code
:invalid_request
Summary
Functions
Check a value against an allowlist of permitted values.
Extract HTTP Basic client credentials from an authorization header value.
Fetch a required single-valued parameter.
Fetch several required parameters at once, returning a map keyed by the same strings. Fails on the first missing one.
Fetch an optional single-valued parameter as {:ok, value | nil}.
Parse a space-delimited list parameter (scope, response_type) into a
deduplicated list, preserving order. Absent → [].
Require every requested scope to be within what the server (or client) may
grant. Returns the requested scopes, or invalid_scope.
Functions
@spec allowed(String.t(), [String.t()], keyword()) :: :ok | {:error, Noizu.MCP.Auth.Server.Errors.t()}
Check a value against an allowlist of permitted values.
The value is never echoed in the error — the caller only learns that what it sent is not on the list.
@spec basic_credentials(String.t() | nil) :: {:ok, {String.t(), String.t()} | nil} | {:error, Noizu.MCP.Auth.Server.Errors.t()}
Extract HTTP Basic client credentials from an authorization header value.
{:ok, {client_id, secret}}, {:ok, nil} when the header is absent or not
Basic, or invalid_client when it is Basic but unparseable. Per RFC 6749
§2.3.1 both halves are form-urlencoded inside the credential.
@spec fetch(map(), String.t(), keyword()) :: {:ok, String.t()} | {:error, Noizu.MCP.Auth.Server.Errors.t()}
Fetch a required single-valued parameter.
Options: :code (error code on failure, default :invalid_request),
:max (length cap, default 4096).
@spec fetch_all(map(), [String.t()], keyword()) :: {:ok, map()} | {:error, Noizu.MCP.Auth.Server.Errors.t()}
Fetch several required parameters at once, returning a map keyed by the same strings. Fails on the first missing one.
@spec optional(map(), String.t(), keyword()) :: {:ok, String.t() | nil} | {:error, Noizu.MCP.Auth.Server.Errors.t()}
Fetch an optional single-valued parameter as {:ok, value | nil}.
A present-but-blank value reads as absent: ?state= is a client that built
the query carelessly, not a client asserting the empty string.
@spec space_list(map(), String.t(), keyword()) :: {:ok, [String.t()]} | {:error, Noizu.MCP.Auth.Server.Errors.t()}
Parse a space-delimited list parameter (scope, response_type) into a
deduplicated list, preserving order. Absent → [].
@spec within_scope([String.t()], [String.t()]) :: {:ok, [String.t()]} | {:error, Noizu.MCP.Auth.Server.Errors.t()}
Require every requested scope to be within what the server (or client) may
grant. Returns the requested scopes, or invalid_scope.