OAuth (50 providers)
One generic OAuth 2.0 / OIDC engine powering 50 built-in providers — GitHub, Discord, Microsoft, Apple, and more. A Radon Pro feature.
Every provider runs on one generic OAuth2/OIDC engine — there are no bespoke per-provider classes. Each provider is just a preset (endpoints + scopes + how to read the profile). Adding GitHub is a one-liner; adding a provider that isn't built in is a small object.
1. Enable one or more providers
OAuth providers live under providers.oauth, keyed by a name you choose. Pass a
built-in preset id (like "github") plus your credentials.
export const auth = new Radon({
adapter: postgresAdapter(pool),
session: { secret: process.env.RADON_SECRET! },
licenseKey: process.env.RADON_LICENSE_KEY,
providers: {
oauth: {
github: { preset: "github", clientId, clientSecret, redirectUri },
discord: { preset: "discord", clientId, clientSecret, redirectUri },
},
},
});
await auth.init(); // verifies the license and unlocks Pro providers| Option | Type | Description |
|---|---|---|
preset | string | OAuthPreset | Built-in preset id, or a custom preset. |
clientId | string | Optional — falls back to an env var. |
clientSecret | string | Optional — falls back to an env var. |
redirectUri | string | Optional — falls back to an env var. |
scopes | string[] | Override the preset's default scopes. |
extraAuthParams | Record<string,string> | Extra params on the authorize URL. |
You can keep credentials out of code entirely
clientId, clientSecret, and redirectUri are optional in config — if you set
the matching environment variables, a
provider is just github: { preset: "github" }.
2. Drive the flow
Reach a configured provider with auth.oauth(name). Unlike Google, these return
an object from getAuthUrl (to support PKCE):
// 1. Build the authorize URL and redirect the user.
const { url, codeVerifier, state } = auth.oauth("github").getAuthUrl({ state: csrf });
// If `codeVerifier` is present (PKCE provider), persist it for the callback.
redirect(url);
// 2. On the callback route, exchange the code:
const { user, created, profile, tokens } = await auth.oauth("github").handleCallback({
code: codeFromQuery,
codeVerifier, // pass back the one from step 1, if any
});
const { token } = auth.createSessionToken(user.id);Return shapes
getAuthUrl({ state?, scopes?, params? }) → { url, codeVerifier?, state? }.
handleCallback({ code, codeVerifier? }) → { user, created, profile, tokens }.
List configured providers with auth.oauthProviders → string[].
The 50 built-in presets
github · gitlab · bitbucket · discord · facebook · twitter · linkedin · slack ·
spotify · twitch · reddit · tiktok · snapchat · dropbox · box · zoom · notion ·
figma · salesforce · hubspot · paypal · amazon · yahoo · epicgames · battlenet ·
roblox · patreon · strava · fitbit · coinbase · line · wechat · kakao · naver ·
vk · mailru · instagram · pinterest · dribbble · behance · zoho · digitalocean ·
microsoft · azuread · apple · wordpress · okta · auth0 · shopify · steamProviders that need per-instance params
A few providers are multi-tenant (a Microsoft tenant, an Okta domain, a Shopify shop). Use the builder presets to bake those in:
import { microsoftPreset, shopifyPreset, oktaPreset, auth0Preset } from "@radonsdk/auth/pro/oauth";
providers: {
oauth: {
work: { preset: microsoftPreset({ tenant: "your-tenant-id" }), clientId, clientSecret, redirectUri },
store: { preset: shopifyPreset({ shop: "acme" }), clientId, clientSecret, redirectUri },
sso: { preset: oktaPreset({ domain: "acme.okta.com" }), clientId, clientSecret, redirectUri },
},
}Any other provider
Not in the 50? Pass your own preset object — the same shape the built-ins use. No forking required.
providers: {
oauth: {
acme: {
preset: {
id: "acme",
name: "Acme",
authorizationUrl: "https://acme.com/oauth/authorize",
tokenUrl: "https://acme.com/oauth/token",
userInfoUrl: "https://acme.com/api/me",
defaultScopes: ["email"],
// optional: pkce, tokenAuthStyle, scopeSeparator, useIdToken,
// mapProfile(raw) => ({ id, email, name, ... })
},
clientId, clientSecret, redirectUri,
},
},
}Credentials from the environment
Every provider's clientId, clientSecret, and redirectUri fall back to
environment variables, so you can keep secrets out of source entirely:
RADON_GITHUB_CLIENT_ID="Iv1.xxxxxxxx"
RADON_GITHUB_CLIENT_SECRET="xxxxxxxx"
RADON_GITHUB_REDIRECT_URI="https://acme.com/api/auth/github/callback"// With the env vars above, the whole provider is one line:
providers: { oauth: { github: { preset: "github" } } }The variable names are RADON_<ID>_CLIENT_ID, RADON_<ID>_CLIENT_SECRET, and
RADON_<ID>_REDIRECT_URI.
The prefix comes from the preset id, not your key
<ID> is the preset's id (uppercased, non-alphanumerics → _) — not the
name you give the provider in config. For built-ins named after their preset
(github: { preset: "github" }) they're the same: RADON_GITHUB_*. But a
renamed provider follows the preset:
oauth: { work: { preset: microsoftPreset({ tenant }) } }
// reads RADON_MICROSOFT_* (preset id "microsoft") — NOT RADON_WORK_*For a custom preset, the id is whatever you set as
preset.id (e.g. id: "acme" → RADON_ACME_*).
Explicit values in config always win over the environment.
Next steps
Sign in with Google — free, no license required. Radon handles the OAuth exchange, fetches the profile, and merges by verified email.
Passkeys / WebAuthn
Phishing-resistant biometric sign-in with passkeys. Radon runs the server-side ceremonies; your frontend calls the browser WebAuthn APIs. A Radon Pro feature.