Documentation

OAuth Flow

Radiant Auth implements OAuth 2.1 Authorization Code with PKCE (S256).

Architecture

Your App (RP)

Confidential / public client

Radiant Auth

auth.rdnt.live

User

Login / consent

  1. App redirects to /api/auth/oauth2/authorize with PKCE (S256)
  2. User authenticates on Radiant Auth (email or Discord)
  3. Auth redirects back with ?code=…&state=…
  4. App exchanges code + code_verifier at /api/auth/oauth2/token
  5. App verifies tokens (JWKS) and creates a local session

1. Authorization request

Redirect the user's browser to the authorize endpoint. Always include PKCE and a cryptographically random state.

authorize.ts
const url = new URL("https://auth.rdnt.live/api/auth/oauth2/authorize");
url.searchParams.set("response_type", "code");
url.searchParams.set("client_id", "YOUR_CLIENT_ID");
url.searchParams.set("redirect_uri", "https://your-app.example/api/auth/callback");
url.searchParams.set("scope", "openid profile email offline_access");
url.searchParams.set("state", state);
url.searchParams.set("nonce", nonce);
url.searchParams.set("code_challenge", codeChallenge);
url.searchParams.set("code_challenge_method", "S256");
// Optional: request a JWT audience for your API
url.searchParams.set("resource", "https://your-app.example");

redirect(url.toString());
Tip
Store code_verifier, state, and nonce in an httpOnly cookie with a short TTL (for example 10 minutes) before redirecting.

2. User authentication

If the user is not signed in to Radiant Auth, they are redirected to the login UI. After login (and consent when required), Radiant Auth redirects back to your redirect_uri:

callback URL
https://your-app.example/api/auth/callback?code=AUTH_CODE&state=STATE
Warning
If the user denies access or an error occurs, the callback may include error and error_description instead of code. Handle these cases explicitly.

3. Token exchange

Exchange the authorization code on your backend only — never in a public browser context when you hold a client secret.

token.ts
const body = new URLSearchParams({
  grant_type: "authorization_code",
  code,
  redirect_uri: "https://your-app.example/api/auth/callback",
  client_id: "YOUR_CLIENT_ID",
  client_secret: "YOUR_CLIENT_SECRET",
  code_verifier: codeVerifier,
});

const res = await fetch("https://auth.rdnt.live/api/auth/oauth2/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body,
});

const tokens = await res.json();
// { access_token, token_type, expires_in, refresh_token?, scope, id_token? }

4. Success response

A successful token response typically includes:

  • access_token — Bearer token for APIs / UserInfo
  • id_token — JWT identity token when openid is used
  • refresh_token — when offline_access was granted
  • expires_in — access token lifetime in seconds (commonly ~3600)
  • token_typeBearer
  • scope — granted scopes

PKCE checklist

  • Generate a high-entropy verifier (≥ 43 characters, URL-safe)
  • Challenge = Base64URL(SHA-256(verifier))
  • Send code_challenge_method=S256
  • Send the original verifier on the token request
  • Never log or expose the verifier to the client after exchange