oauth
stableBuild OAuth 2.0 authorization URLs, token request bodies, and PKCE challenges for implementing standard OAuth flows without a runtime dependency.
use plugin oauth::{build_auth_url, build_token_request_body, generate_state, …} Functions (14)
- build_auth_url Build an OAuth 2.0 authorization URL
- build_token_request_body Build authorization_code token request body
- generate_state Generate a random CSRF state token
- generate_pkce_verifier Generate a PKCE code verifier
- generate_pkce_challenge Derive SHA-256 PKCE code challenge from verifier
- parse_query_params Parse a query string into a table
- build_bearer_header Format a Bearer authorization header value
- build_refresh_token_body Build a refresh_token grant request body
- build_client_credentials_body Build a client_credentials grant request body
- build_auth_url_pkce Build authorization URL with PKCE parameters
- build_token_request_body_pkce Build PKCE token exchange request body
- join_scopes Join a table of scope strings with spaces
- parse_token_response Parse a JSON token response into a table
- build_basic_auth Build a Basic authorization header value
Overview
oauth is a dependency-free helper kit for implementing OAuth 2.0 clients by hand: it builds the strings every flow needs — authorization URLs, URL-encoded token request bodies, and HTTP Authorization header values — and supplies the cryptographic pieces (random CSRF state, PKCE verifier/challenge) without pulling in a runtime OAuth library. It is stateless: there is no session object or handle, just pure functions that take strings and return strings or parse them into tables, so you bring your own HTTP client to actually send the requests.
Use it whenever you need to drive an authorization-code, PKCE, refresh-token, or client-credentials flow. The mental model is a pipeline: generate state (and, for public clients, a PKCE verifier/challenge), send the user to build_auth_url / build_auth_url_pkce, read the callback with parse_query_params, exchange the code with build_token_request_body / build_token_request_body_pkce, read the result with parse_token_response, and call APIs with build_bearer_header.
Common patterns
Authorization-code flow — start the redirect with a CSRF state, then read it back from the callback:
use plugin oauth::{generate_state, build_auth_url, parse_query_params}
let state = generate_state()
let url = build_auth_url(
"https://accounts.example.com/oauth/authorize",
"my-client-id",
"https://myapp.dev/callback",
"openid profile email",
state
)
print("redirect to: {url}")
let params = parse_query_params("?code=abc123&state={state}")
if params["state"] == state {
print("got code: {params["code"]}")
}
PKCE flow for a public client (no secret) — challenge on the way out, verifier on the way back:
use plugin oauth::{generate_state, generate_pkce_verifier, generate_pkce_challenge, build_auth_url_pkce, build_token_request_body_pkce}
let state = generate_state()
let verifier = generate_pkce_verifier()
let challenge = generate_pkce_challenge(verifier)
let url = build_auth_url_pkce(
"https://auth.example.com/authorize",
"public-client",
"myapp://callback",
"openid profile",
state,
challenge
)
print("open: {url}")
let body = build_token_request_body_pkce("public-client", "code-from-callback", "myapp://callback", verifier)
Exchange a code, read the response, then authenticate an API call:
use plugin oauth::{build_token_request_body, parse_token_response, build_bearer_header}
let body = build_token_request_body("my-client-id", "my-secret", "auth-code", "https://myapp.dev/callback")
// POST `body` to the token endpoint, then parse the JSON response:
let token = parse_token_response('{"access_token":"abc","token_type":"Bearer","expires_in":3600}')
let auth = build_bearer_header(token["access_token"])
print("Authorization: {auth}")
Build an OAuth 2.0 authorization URL
Constructs the OAuth 2.0 authorization endpoint URL with response_type=code and all required parameters URL-encoded. Redirect the user's browser to the returned URL to begin the authorization code flow.
use plugin oauth::{build_auth_url, generate_state}
let state = generate_state()
let url = build_auth_url(
"https://accounts.example.com/oauth/authorize",
"my-client-id",
"https://myapp.dev/callback",
"openid profile email",
state
)
print("redirect to: {url}")
Build authorization_code token request body
Builds a URL-encoded form body for exchanging an authorization code for tokens (grant_type=authorization_code). Send this as the body of a POST request to the token endpoint with Content-Type: application/x-www-form-urlencoded.
use plugin oauth::{build_token_request_body}
let body = build_token_request_body(
"my-client-id",
"my-secret",
"auth-code-from-callback",
"https://myapp.dev/callback"
)
Generate a random CSRF state token
Generates a cryptographically random 32-byte hex string to use as the state parameter. Store this before redirecting and verify it matches the value returned in the callback to prevent CSRF attacks.
use plugin oauth::{generate_state}
let state = generate_state()
print("state: {state}")
Generate a PKCE code verifier
Generates a cryptographically random PKCE code verifier (32 bytes, base64url-encoded). Store this securely between the authorization request and the token exchange.
use plugin oauth::{generate_pkce_verifier, generate_pkce_challenge}
let verifier = generate_pkce_verifier()
let challenge = generate_pkce_challenge(verifier)
Derive SHA-256 PKCE code challenge from verifier
Derives the PKCE S256 code challenge from a verifier by computing SHA-256 and base64url-encoding the result. Pass this as code_challenge in the authorization request and the original verifier at token exchange.
use plugin oauth::{generate_pkce_verifier, generate_pkce_challenge}
let verifier = generate_pkce_verifier()
let challenge = generate_pkce_challenge(verifier)
print("challenge: {challenge}")
Parse a query string into a table
Parses a URL query string (with or without a leading ?) into a table of key-value string pairs. Useful for extracting the code and state parameters from the OAuth callback URL.
use plugin oauth::{parse_query_params}
let params = parse_query_params("?code=abc123&state=xyz")
print("code: {params["code"]}")
print("state: {params["state"]}")
A failed authorization comes back as error (and often error_description) instead of code, so check for it before exchanging:
use plugin oauth::{parse_query_params}
let params = parse_query_params("error=access_denied&error_description=user%20cancelled")
if params["error"] != nil {
print("auth failed: {params["error"]}")
}
Format a Bearer authorization header value
Returns the string "Bearer <token>" for use as the value of an HTTP Authorization header when making authenticated API requests.
use plugin oauth::{build_bearer_header}
let auth = build_bearer_header("eyJhbGci...")
print("Authorization: {auth}")
Feed it straight from a parsed token response to authenticate the next call:
use plugin oauth::{parse_token_response, build_bearer_header}
let token = parse_token_response('{"access_token":"abc123","token_type":"Bearer"}')
let auth = build_bearer_header(token["access_token"])
print("Authorization: {auth}")
Build a refresh_token grant request body
Builds a URL-encoded form body for the refresh_token grant type. Use this to exchange a refresh token for a new access token without requiring user interaction.
use plugin oauth::{build_refresh_token_body}
let body = build_refresh_token_body(
"my-client-id",
"my-secret",
"stored-refresh-token"
)
Build a client_credentials grant request body
Builds a URL-encoded form body for the client_credentials grant type, used for machine-to-machine authentication. The scope parameter is optional.
use plugin oauth::{build_client_credentials_body}
let body = build_client_credentials_body(
"service-client-id",
"service-secret",
"api:read api:write"
)
Build authorization URL with PKCE parameters
Like build_auth_url but adds code_challenge and code_challenge_method=S256 for PKCE flows. Use for public clients (SPAs, mobile apps) that cannot store a client secret.
use plugin oauth::{build_auth_url_pkce, generate_state, generate_pkce_verifier, generate_pkce_challenge}
let state = generate_state()
let verifier = generate_pkce_verifier()
let challenge = generate_pkce_challenge(verifier)
let url = build_auth_url_pkce(
"https://auth.example.com/authorize",
"public-client",
"myapp://callback",
"openid profile",
state,
challenge
)
Build PKCE token exchange request body
Builds the token exchange body for PKCE flows. Includes code_verifier instead of client_secret. No client secret is needed because the verifier proves possession of the original challenge.
use plugin oauth::{build_token_request_body_pkce}
let body = build_token_request_body_pkce(
"public-client",
"auth-code-from-callback",
"myapp://callback",
stored_verifier
)
Join a table of scope strings with spaces
Joins a table of scope strings into a single space-separated string suitable for the scope parameter. Useful when scopes are stored as a list.
use plugin oauth::{join_scopes}
let scopes = #{1: "openid", 2: "profile", 3: "email"}
let scope_str = join_scopes(scopes)
print("scope: {scope_str}")
The joined string drops straight into an authorization URL as the scope argument:
use plugin oauth::{join_scopes, generate_state, build_auth_url}
let scope = join_scopes(#{1: "openid", 2: "email"})
let url = build_auth_url(
"https://accounts.example.com/oauth/authorize",
"my-client-id",
"https://myapp.dev/callback",
scope,
generate_state()
)
print(url)
Parse a JSON token response into a table
Parses a JSON token response body string and extracts known fields into a table: access_token, token_type, expires_in, refresh_token, scope, error, and error_description. Returns only the fields present in the JSON.
use plugin oauth::{parse_token_response}
let token = parse_token_response('{"access_token":"abc","expires_in":3600}')
print("token: {token["access_token"]}")
print("expires in: {token["expires_in"]}")
When the endpoint returns an error payload, the same parser surfaces error and error_description so you can branch on success:
use plugin oauth::{parse_token_response}
let result = parse_token_response('{"error":"invalid_grant","error_description":"code expired"}')
if result["error"] != nil {
print("token exchange failed: {result["error_description"]}")
}
Build a Basic authorization header value
Returns the string "Basic <base64(client_id:client_secret)>" for use as the value of an HTTP Authorization header. Some token endpoints require credentials in the header rather than the body.
use plugin oauth::{build_basic_auth}
let auth = build_basic_auth("my-client-id", "my-secret")
print("Authorization: {auth}")