Proof Key for Code Exchange (RFC 7636), S256 only.
The client never sends the plain method: an authorization server that
does not advertise "S256" in code_challenge_methods_supported (or omits
the field) is refused before any authorization or token request (OA-1).
iex> ConduitMcp.Client.OAuth.PKCE.challenge("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")
"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
Summary
Functions
The S256 code challenge for verifier (a string or a Secret wrapping
one): BASE64URL(SHA256(ASCII(verifier))) without padding (RFC 7636 §4.2).
Returns :ok when s256_supported?/1 holds, else raises a
ConduitMcp.Client.OAuth.Error with reason :pkce_unsupported.
The only code_challenge_method the client sends: "S256".
Whether decoded authorization-server metadata lists "S256" in
code_challenge_methods_supported. The method name is compared exactly
(it is case-sensitive); a missing or malformed field means false.
Whether verifier is a syntactically valid RFC 7636 §4.1 code verifier.
A fresh code verifier: 32 bytes from :crypto.strong_rand_bytes/1,
base64url-encoded without padding (43 characters, RFC 7636 §4.1).
Functions
@spec challenge(String.t() | ConduitMcp.Client.Secret.t()) :: String.t()
The S256 code challenge for verifier (a string or a Secret wrapping
one): BASE64URL(SHA256(ASCII(verifier))) without padding (RFC 7636 §4.2).
Raises ArgumentError unless verifier is 43–128 characters of
[A-Za-z0-9-._~] (RFC 7636 §4.1).
@spec ensure_s256!(map()) :: :ok
Returns :ok when s256_supported?/1 holds, else raises a
ConduitMcp.Client.OAuth.Error with reason :pkce_unsupported.
@spec method() :: String.t()
The only code_challenge_method the client sends: "S256".
Whether decoded authorization-server metadata lists "S256" in
code_challenge_methods_supported. The method name is compared exactly
(it is case-sensitive); a missing or malformed field means false.
iex> ConduitMcp.Client.OAuth.PKCE.s256_supported?(%{"code_challenge_methods_supported" => ["plain", "S256"]})
true
iex> ConduitMcp.Client.OAuth.PKCE.s256_supported?(%{"code_challenge_methods_supported" => ["plain"]})
false
iex> ConduitMcp.Client.OAuth.PKCE.s256_supported?(%{})
false
Whether verifier is a syntactically valid RFC 7636 §4.1 code verifier.
@spec verifier() :: String.t()
A fresh code verifier: 32 bytes from :crypto.strong_rand_bytes/1,
base64url-encoded without padding (43 characters, RFC 7636 §4.1).
The verifier is a secret; wrap it in ConduitMcp.Client.Secret wherever it
is held (as ConduitMcp.Client.OAuth.Pending does).