# `OpenrouterSdk.OAuth`
[🔗](https://github.com/zmzlois/openrouter_sdk/blob/v0.1.0/lib/openrouter_sdk/oauth.ex#L1)

oauth pkce primitives.

this is intentionally just the math + the http exchange. the
consumer owns the redirect route, session storage, and the
`code_verifier` lifetime.

typical flow:

    verifier = OpenrouterSdk.OAuth.generate_code_verifier()
    challenge = OpenrouterSdk.OAuth.code_challenge(verifier)
    url = OpenrouterSdk.OAuth.build_authorize_url(
      "https://myapp.example.com/openrouter/callback",
      code_challenge: challenge,
      code_challenge_method: :s256
    )
    # redirect the user to `url`. on callback, exchange the code:
    {:ok, %{key: api_key}} =
      OpenrouterSdk.OAuth.exchange_code(
        conn.params["code"],
        code_verifier: verifier,
        code_challenge_method: :s256
      )

# `method`

```elixir
@type method() :: :s256 | :plain
```

# `build_authorize_url`

```elixir
@spec build_authorize_url(
  String.t(),
  keyword()
) :: String.t()
```

build the authorization url to redirect the user to.

`callback_url` is required (where openrouter sends the user back
with `?code=...`). `code_challenge` is technically optional per the
api but you should always include it.

# `code_challenge`

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

derive the code_challenge from a verifier.

defaults to s256 (recommended). pass `:plain` to skip hashing — only
ever do that if the transport layer above can't hash, which is rare.

# `exchange_code`

```elixir
@spec exchange_code(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, OpenrouterSdk.Error.t()}
```

exchange an authorization code for an api key.

returns `{:ok, %{key: "...", ...}}` on success — the full decoded
json body is returned so future fields (user info, etc.) are
available transparently.

# `generate_code_verifier`

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

generate a cryptographically random code verifier (rfc 7636).

uses 64 random bytes encoded as url-safe base64 (no padding) — well
within the 43-128 char range the spec allows.

---

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