Documentation

Integration Guide

Wire Authorization Code + PKCE, callbacks, tokens, and sessions into your application.

This guide assumes Radiant Networks has issued YOUR_CLIENT_ID and YOUR_CLIENT_SECRET and registered your redirect URIs. Credentials are never self-service in these docs — contact Radiant Networks for client provisioning.

  1. Configure environment variables

    Set AUTH_ISSUER, AUTH_CLIENT_ID, AUTH_CLIENT_SECRET, AUTH_REDIRECT_URI, and optionally AUTH_AUDIENCE. Keep secrets in your platform secret store (Wrangler secrets, Vercel env, etc.).
  2. Implement PKCE helpers

    Generate a verifier and S256 challenge on every login attempt.
  3. Add a login route

    Persist PKCE state, then redirect to the authorize endpoint.
  4. Add a callback handler

    Validate state, exchange the code, verify tokens, create a local session.
  5. Refresh and revoke

    Refresh access tokens before expiry; revoke on logout.

PKCE

pkce.ts
import { createHash, randomBytes } from "node:crypto";

function base64Url(buf: Buffer) {
  return buf.toString("base64url");
}

export function createPkcePair() {
  const verifier = base64Url(randomBytes(32));
  const challenge = base64Url(createHash("sha256").update(verifier).digest());
  return { verifier, challenge };
}

Redirects

login.ts
// GET /api/auth/login
export async function startLogin(request: Request, env: Env) {
  const { verifier, challenge } = createPkcePair();
  const state = crypto.randomUUID();
  const nonce = crypto.randomUUID();

  // Persist verifier/state/nonce in httpOnly cookie or KV/session store

  const url = new URL("https://auth.rdnt.live/api/auth/oauth2/authorize");
  url.searchParams.set("response_type", "code");
  url.searchParams.set("client_id", env.AUTH_CLIENT_ID); // YOUR_CLIENT_ID
  url.searchParams.set("redirect_uri", env.AUTH_REDIRECT_URI);
  url.searchParams.set("scope", "openid profile email offline_access");
  url.searchParams.set("state", state);
  url.searchParams.set("nonce", nonce);
  url.searchParams.set("code_challenge", challenge);
  url.searchParams.set("code_challenge_method", "S256");
  if (env.AUTH_AUDIENCE) url.searchParams.set("resource", env.AUTH_AUDIENCE);

  return Response.redirect(url.toString(), 302);
}
Warning
Only redirect to Radiant Auth from your backend (or a carefully designed public client). Never put YOUR_CLIENT_SECRET in frontend bundles.

Callback handler

callback.ts
// GET /api/auth/callback?code=...&state=...
export async function handleCallback(request: Request, env: Env) {
  const url = new URL(request.url);
  const code = url.searchParams.get("code");
  const state = url.searchParams.get("state");
  if (!code || !state) return new Response("Missing code/state", { status: 400 });

  // Verify state against stored value; load code_verifier

  const body = new URLSearchParams({
    grant_type: "authorization_code",
    code,
    redirect_uri: env.AUTH_REDIRECT_URI,
    client_id: env.AUTH_CLIENT_ID, // YOUR_CLIENT_ID
    client_secret: env.AUTH_CLIENT_SECRET, // YOUR_CLIENT_SECRET
    code_verifier: storedVerifier,
  });

  const tokenRes = await fetch("https://auth.rdnt.live/api/auth/oauth2/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body,
  });
  if (!tokenRes.ok) return new Response("Token exchange failed", { status: 502 });

  const tokens = await tokenRes.json();
  // Verify id_token with JWKS, upsert user, store tokens server-side
  // Set opaque session cookie for the browser
  return Response.redirect("/account", 302);
}

Access tokens

Use the access token as a Bearer credential for UserInfo and, when applicable, your own APIs. Prefer verifying JWT access tokens locally with JWKS when they are audience-bound to your app.

Refresh tokens

Request offline_access at authorize time. When the access token is near expiry, call the token endpoint with grant_type=refresh_token:

refresh.ts
const body = new URLSearchParams({
  grant_type: "refresh_token",
  refresh_token: storedRefreshToken,
  client_id: "YOUR_CLIENT_ID",
  client_secret: "YOUR_CLIENT_SECRET",
});

const res = await fetch("https://auth.rdnt.live/api/auth/oauth2/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body,
});
Tip
If the response includes a new refresh token, replace the stored value (rotation).

Session handling

  • Store access/refresh/id tokens server-side (database or encrypted cookie store).
  • Give the browser an opaque session ID cookie (HttpOnly, Secure, SameSite=Lax).
  • On each request, load the session and refresh tokens if needed.
  • On logout, revoke tokens and clear the cookie.
Note
See platform-specific samples under Examples — each follows this same flow.