Noizu.MCP.Auth.WWWAuthenticate (Noizu MCP v0.1.6)

Copy Markdown View Source

Parse and format WWW-Authenticate challenges (RFC 9110 §11.6.1), as used by the MCP authorization spec to point clients at protected-resource metadata and signal insufficient_scope step-up.

Summary

Functions

Build a Bearer challenge from a keyword list, dropping nil values.

Escape a value for a quoted-string (RFC 9110 §5.6.4).

Format a challenge header value. params is an enumerable of name/value pairs; values are escaped with escape_quoted/1, so a value carrying CR/LF raises rather than splitting the response header.

Parse a challenge header value.

Types

t()

@type t() :: %Noizu.MCP.Auth.WWWAuthenticate{
  params: %{optional(String.t()) => String.t()},
  scheme: String.t()
}

Functions

bearer_challenge(params)

@spec bearer_challenge([{String.t() | atom(), String.t() | nil}] | map()) ::
  String.t()

Build a Bearer challenge from a keyword list, dropping nil values.

Ordering is preserved, so callers control how the challenge reads:

iex> Noizu.MCP.Auth.WWWAuthenticate.bearer_challenge(
...>   resource_metadata: "https://x/.well-known/oauth-protected-resource",
...>   scope: nil,
...>   error: "invalid_token"
...> )
~s(Bearer resource_metadata="https://x/.well-known/oauth-protected-resource", error="invalid_token")

escape_quoted(value)

@spec escape_quoted(String.t() | atom() | number()) :: String.t()

Escape a value for a quoted-string (RFC 9110 §5.6.4).

Backslash and double quote are escaped. CR, LF, NUL and any other control character are rejected — they cannot be represented in a quoted-string and are the header-injection vector — so this raises ArgumentError rather than emitting a header an attacker chose the shape of. The raised message never includes the offending value.

iex> Noizu.MCP.Auth.WWWAuthenticate.escape_quoted(~s(a"b\\c))
~S(a\"b\\c)

format(scheme \\ "Bearer", params)

@spec format(String.t(), [{String.t() | atom(), String.t()}] | map()) :: String.t()

Format a challenge header value. params is an enumerable of name/value pairs; values are escaped with escape_quoted/1, so a value carrying CR/LF raises rather than splitting the response header.

parse(header)

@spec parse(String.t() | nil) :: t() | nil

Parse a challenge header value.

iex> Noizu.MCP.Auth.WWWAuthenticate.parse(
...>   ~s(Bearer resource_metadata="https://x/.well-known/oauth-protected-resource", error="invalid_token")
...> )
%Noizu.MCP.Auth.WWWAuthenticate{
  scheme: "Bearer",
  params: %{
    "resource_metadata" => "https://x/.well-known/oauth-protected-resource",
    "error" => "invalid_token"
  }
}