Radon

Configuration

Every option the Radon constructor accepts, what it does, and when to change it.

The Radon constructor takes one options object. Only adapter and session are strictly required; everything else has a sensible default or is only needed for a specific feature.

lib/auth.ts
export const auth = new Radon({
  adapter: postgresAdapter(pool),          // required — where users live
  session: { secret: process.env.RADON_SECRET! }, // required — signs JWTs
  appName: "Acme",                          // shown in default email copy
  providers: { /* the sign-in methods you enable */ },
  code: { length: 6, ttlMs: 600_000, maxAttempts: 5 },
  rateLimit: { maxPerWindow: 3, windowMs: 600_000 },
});

adapter — where users live

The bridge to your database. Required. See Adapters for every built-in option and how to write your own.

adapter: postgresAdapter(pool)

session — JWT session settings

Required (session.secret specifically). Radon issues stateless JWT session tokens: the token is a signed value in an HttpOnly cookie, verified without a database read. The same secret also signs magic-link and password-reset tokens.

session: {
  secret: process.env.RADON_SECRET!,   // required
  expiresInSec: 7 * 24 * 60 * 60,      // default: 7 days
  issuer: "acme",                       // optional JWT `iss` claim
  audience: "acme-app",                 // optional JWT `aud` claim
  clockToleranceSec: 0,                 // skew tolerance on verify
}

Want revocable, per-device sessions?

Stateless JWTs can't be revoked individually before they expire. If you need "log out this one device" or a live list of active sessions, add the Pro multi-device sessions or refresh tokens providers, which are database-backed and revocable.

providers — the sign-in methods

Opt into the methods you want; leave the rest out. Each is documented on its own Auth Methods page. The free-tier providers:

providers: {
  emailCode:     { sender, template?, from? },
  magicLink:     { sender, baseUrl, ttlMs?, template?, from? },
  emailPassword: { sender, resetUrl, hasher?, minLength?, resetTemplate?, from? },
  google:        { clientId?, clientSecret?, redirectUri?, scopes?, fetch? },
}

sender is an email sender. baseUrl (magic link) and resetUrl (password) are where those links point in your app. Google credentials fall back to the RADON_GOOGLE_CLIENT_ID, RADON_GOOGLE_CLIENT_SECRET, and RADON_GOOGLE_REDIRECT_URI env vars.

Pro providers (oauth, phoneOtp, totp, webauthn, refresh, sessions, apiKeys, orgs, account) live under the same providers key — see Radon Pro.

appName — email branding

A string shown in the default email templates ("Your Acme code is…"). Also the default issuer for TOTP and relying-party name for passkeys.

appName: "Acme"

code — one-time-code engine

Controls every 6-digit code (email codes, phone OTP) Radon issues.

code: {
  length: 6,          // digits in the code (default 6)
  ttlMs: 600_000,     // lifetime in ms (default 10 minutes)
  maxAttempts: 5,     // wrong tries before the code locks (default 5)
}

rateLimit — abuse protection

Radon rate-limits code/link sends per identifier out of the box. Tune it:

rateLimit: {
  maxPerWindow: 3,      // sends allowed per window (default 3)
  windowMs: 600_000,    // window length in ms (default 10 minutes)
}

A caller over the limit gets a RateLimitError (HTTP 429) carrying retryAfterMs.

Pro & security options

OptionPurpose
licenseKeyRadon Pro license key. Falls back to RADON_LICENSE_KEY. Verified in auth.init(). See Pro.
licenseAdvanced license config ({ key?, verifyUrl?, watermark?, fetch? }).
encryptionKey32-byte key for encryption-at-rest of reversible secrets (TOTP). Falls back to RADON_ENCRYPTION_KEY.
onEventError(error, event) => void — called when an event handler throws.
hasherOverride the engine's code/token hasher (SHA-256 by default).
nowInjectable clock (() => Date) for testing.

The session cookie's name and security flags are configured at the framework handler, not on the Radon instance. They're secure by default (HttpOnly, Secure, SameSite=Lax, name radon_session):

app/api/auth/[...radon]/route.ts
const handler = radonNextHandler(auth, {
  basePath: "/api/auth",
  cookieName: "acme_session",           // rename the cookie
  cookieOptions: { sameSite: "lax", domain: ".acme.com" },
  successRedirect: "/dashboard",         // after magic-link / OAuth callback
  failureRedirect: "/signin?error=1",
});

sameSite and OAuth

If you embed auth in a cross-site iframe you may need sameSite: "none" with secure: true (browsers reject None without Secure). Otherwise keep "lax" — it's safer against CSRF.

Full type

interface RadonSdkConfig {
  adapter: RadonAdapter;                  // required
  session: {
    secret: string;                       // required
    expiresInSec?: number;                // default 604800 (7 days)
    issuer?: string;
    audience?: string;
    clockToleranceSec?: number;
  };
  providers?: ProvidersConfig;
  appName?: string;
  code?: { length?: number; ttlMs?: number; maxAttempts?: number };
  rateLimit?: { maxPerWindow?: number; windowMs?: number };
  licenseKey?: string;
  license?: { key?: string; verifyUrl?: string; watermark?: boolean; fetch?: typeof fetch };
  encryptionKey?: string;
  onEventError?: (error: unknown, event: string) => void;
  hasher?: Hasher;
  now?: () => Date;
}

On this page