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.
Configure environment variables
SetAUTH_ISSUER,AUTH_CLIENT_ID,AUTH_CLIENT_SECRET,AUTH_REDIRECT_URI, and optionallyAUTH_AUDIENCE. Keep secrets in your platform secret store (Wrangler secrets, Vercel env, etc.).Implement PKCE helpers
Generate a verifier and S256 challenge on every login attempt.Add a login route
Persist PKCE state, then redirect to the authorize endpoint.Add a callback handler
Validate state, exchange the code, verify tokens, create a local session.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.