Noizu.MCP.Auth.Server.Params (Noizu MCP v0.1.6)

Copy Markdown View Source

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

allowed(value, allowlist, opts \\ [])

@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.

basic_credentials(header)

@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.

fetch(params, key, opts \\ [])

@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).

fetch_all(params, keys, opts \\ [])

@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.

optional(params, key, opts \\ [])

@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.

space_list(params, key, opts \\ [])

@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 → [].

within_scope(requested, permitted)

@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.