0AuthKit is designed with security in mind. This page explains the threat model and the protections in place.
Your clientSecret must never reach the browser.
- The
0authkit/cliententry point exports onlygetAuthUrl— it never asks for or touches aclientSecret. - Token exchange (the step that uses
clientSecret) always happens server-side viahandleCallback. - For client-only PKCE flows (Google), no
clientSecretis required — the PKCE verifier provides the security guarantee instead.
Rule: Import from 0authkit/client in any code that runs in the browser. Import from 0authkit or 0authkit/server only in server-side code.
Every call to getAuthUrl generates a cryptographically random state value (16 bytes via crypto.getRandomValues).
What you must do:
- Save
statein a server-side session or anHttpOnlycookie immediately aftergetAuthUrl. - On callback, pass the saved value as
expectedStatetohandleCallback. - 0AuthKit will throw
'Invalid OAuth state'if the values don't match.
// Step 1 — save state
const { url, state } = await kit.getAuthUrl()
req.session.oauthState = state
res.redirect(url)
// Step 2 — validate state on callback
const result = await kit.handleCallback(
String(req.query.code),
String(req.query.state),
// OAuthKit class automatically uses the saved lastState as expectedState
)The
OAuthKitclass handles this automatically if you callgetAuthUrlandhandleCallbackon the same instance. For stateless/functional use, passexpectedStateexplicitly.
What happens without state validation?
A malicious site could trick a user into completing an OAuth flow that logs them into the attacker's account. The state parameter prevents this (CSRF attack). Always validate it.
PKCE protects against authorization code interception attacks, particularly in single-page apps and mobile clients.
How it works:
getAuthUrlgenerates a randomcodeVerifierand derives acodeChallengefrom it (SHA-256, base64url-encoded).- The challenge is sent in the auth URL.
- On callback, the original
codeVerifieris sent during token exchange. The provider verifies thatSHA256(codeVerifier) === codeChallenge.
What you must do:
- Save
codeVerifieralongsidestateaftergetAuthUrl. - Pass it to
handleCallback(the class does this automatically, or pass it viaoptions.codeVerifier).
const { url, state, codeVerifier } = await kit.getAuthUrl()
req.session.oauthState = state
req.session.oauthCodeVerifier = codeVerifierPKCE provider support:
- Google — ✅ supported (recommended — pass
codeVerifierfor best security) - GitHub — ❌ not supported by GitHub
All provider URLs in 0AuthKit are hardcoded https:// — there is no HTTP fallback. Your redirect URI should also use HTTPS in production.
0AuthKit never logs, caches, or persists credentials. clientId, clientSecret, code, and tokens exist only in memory during the request.
| Value | Where to store |
|---|---|
state |
Server session or HttpOnly cookie |
codeVerifier |
Server session or HttpOnly cookie |
accessToken |
Server session (never expose to browser unless necessary) |
refreshToken |
Server session, encrypted at rest if possible |
| Threat | Mitigation |
|---|---|
| CSRF via forged callback | State parameter validation |
| Authorization code interception | PKCE (Google) |
| Secret leakage to browser | Client/server entry point split |
| Token leakage | Server-side token exchange, HttpOnly cookie storage |
| Man-in-the-middle | HTTPS-only provider URLs |