# `ConduitMcp.Client.OAuth.PKCE`
[🔗](https://github.com/nyo16/conduit_mcp/blob/v0.11.0/lib/conduit_mcp/client/oauth/pkce.ex#L1)

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"

# `challenge`

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

# `ensure_s256!`

```elixir
@spec ensure_s256!(map()) :: :ok
```

Returns `:ok` when `s256_supported?/1` holds, else raises a
`ConduitMcp.Client.OAuth.Error` with reason `:pkce_unsupported`.

# `method`

```elixir
@spec method() :: String.t()
```

The only `code_challenge_method` the client sends: `"S256"`.

# `s256_supported?`

```elixir
@spec s256_supported?(map()) :: boolean()
```

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

# `valid_verifier?`

```elixir
@spec valid_verifier?(term()) :: boolean()
```

Whether `verifier` is a syntactically valid RFC 7636 §4.1 code verifier.

# `verifier`

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
