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
)
Summary
Functions
build the authorization url to redirect the user to.
derive the code_challenge from a verifier.
exchange an authorization code for an api key.
generate a cryptographically random code verifier (rfc 7636).
Types
Functions
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.
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.
@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.
@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.