Technology handbook
Authentication with Auth.js & Kinde Auth
Providers, callbacks, sessions, JWTs, route protection and the hosted alternative.
- Topics
- 24
- Sections
- 4
Auth.js setup
Configuration
Declare providers, secret, session strategy and callbacks in one config.
- One exported config object is the whole contract: providers, secret, strategy and callbacks.
- The same object feeds the route handler and the server-side session helpers.
- Splitting it into an edge-safe half is what lets middleware run without the database adapter.
Route handlers
Mount the sign-in, callback and sign-out endpoints the library expects.
- A single catch-all route serves sign-in, callback, sign-out and the CSRF endpoint.
- The provider's redirect URI must match this path exactly or the callback 404s.
- Exporting GET and POST from the same file is required; a missing POST breaks sign-out.
Environment secrets
Set the signing secret and provider credentials per environment.
- The signing secret derives the session encryption key, so rotating it invalidates every cookie.
- Provider client secrets differ per environment because each has its own redirect URI.
- A missing secret fails closed at boot in production and silently generates one in development.
Trusted host and URL
Configure the canonical URL so OAuth redirects resolve correctly.
- The canonical URL is what the library builds OAuth redirect targets from.
- Behind a proxy the forwarded host must be trusted, or callbacks resolve to the internal address.
- A preview deployment on a generated domain needs the host allowed or sign-in loops back.
Sign-in and sign-out
Trigger flows from the client or server and handle the redirect.
- Sign-in takes a provider id and a callback URL to return to afterwards.
- Sign-out must clear the cookie server-side; clearing it client-side leaves the session readable.
- The redirect flag lets the caller handle the result instead of the browser navigating.
Custom pages
Replace the default sign-in and error pages with your own.
- Pointing the config at your own routes replaces the built-in sign-in and error screens.
- The error page receives the failure reason as a query parameter to render.
- A custom page still posts to the library's endpoints, so the CSRF token must be included.
Providers
OAuth providers
Add GitHub, Google or another provider and register the redirect URI.
- Each provider needs its redirect URI registered on the provider's side before it will return.
- Scopes requested at sign-in decide what the profile response contains.
- A provider added later does not link to existing users unless account linking is enabled.
Credentials provider
Accept a password only with your own hashing and lockout rules.
- The library verifies nothing here: hashing, comparison and lockout are yours to write.
- It forces the JWT strategy, because there is no provider account to persist.
- Returning null is the only way to signal failure; throwing leaks the reason to the client.
Email and magic links
Sign in with a one-time link and an expiry the user cannot extend.
- The link carries a single-use token stored server-side and deleted on redemption.
- An expiry the user cannot extend is what stops a forwarded mail granting access days later.
- It needs a database adapter, because the pending token has nowhere else to live.
Account linking
Attach a second provider to an existing user without duplicating accounts.
- Linking attaches a second provider account to one user row rather than creating a duplicate.
- Automatic linking on a matching email trusts the provider to have verified that address.
- Unlinking the last provider leaves a user who cannot sign in at all.
Provider profiles
Map a provider's profile fields onto your own user shape.
- The profile callback maps a provider's response onto your own user columns.
- Providers disagree on field names, so the mapping is per provider rather than shared.
- Whatever it returns is what the adapter writes, including the id the user is keyed by.
OIDC specifics
Handle scopes, the id token and provider-specific claims.
- The id token carries claims already, so a userinfo request is often an avoidable round trip.
- Requesting offline access is what returns a refresh token; without it the access token simply expires.
- Clock skew between your server and the issuer makes valid tokens look expired.
Sessions & authorization
JWT versus database sessions
Trade a stateless cookie against a revocable server-side record.
- A JWT costs no read per request but cannot be revoked before it expires.
- A database session is revocable immediately and costs one query on every request.
- The choice decides whether middleware can authorize without touching the database.
Session and JWT callbacks
Add roles or a user id to the token and expose them on the session.
- The JWT callback runs on sign-in and refresh; the session callback runs on every read.
- Anything the client must see has to be copied across both, not just into the token.
- A token that grows with every callback eventually exceeds the cookie size limit.
Reading the session
Get the session on the server and in a client component correctly.
- The server helper reads the cookie directly and needs no fetch to your own API.
- A client hook re-renders on change, which a one-off server read cannot do.
- Reading it in a layout opts the whole route into dynamic rendering.
Middleware protection
Guard route groups before they render.
- Middleware runs before the route renders, so an unauthorized user never reaches the page.
- It cannot use a database adapter, which is why it pairs with the JWT strategy.
- A matcher that catches static assets makes every image request pay for an auth check.
Role-based access
Authorize by role or permission rather than by mere presence of a session.
- Presence of a session answers who, not what they may do.
- The role must come from the token or a lookup, and only one of those is free.
- Checking the role in the page as well as the middleware is what survives a matcher mistake.
Cookie security
Set httpOnly, secure and sameSite so the cookie is not readable or forgeable.
- httpOnly keeps the session out of reach of any script on the page.
- secure and sameSite together are what stop it riding along on a cross-site request.
- A cookie scoped to the parent domain is readable by every subdomain under it.
Kinde Auth
Hosted sign-in
Redirect to Kinde's pages and receive the user back with tokens.
- The browser leaves your application entirely and returns with tokens on a callback.
- Branding and password policy live in Kinde's dashboard rather than your codebase.
- An outage at the provider is a full sign-in outage you cannot work around locally.
SDK setup
Wire the client and server helpers and the callback route.
- The callback route exchanges the returned code for tokens and sets the session.
- Server and client helpers read the same session but from different places.
- The configured site URL must match the application's real origin or the exchange fails.
Organizations
Model multi-tenant users where a person belongs to several organizations.
- A person can belong to several organizations, so identity alone does not decide access.
- The active organization travels in the token and scopes every query behind it.
- Switching organization issues a new token rather than mutating the current one.
Permissions and roles
Define authorization in Kinde and read it from the token.
- Authorization is declared in Kinde and arrives as claims, not computed in your code.
- Reading a permission from the token avoids a round trip on every protected action.
- A permission changed in the dashboard does not apply until the token refreshes.
Feature flags
Read per-organization flags alongside identity.
- Flags arrive on the same token as identity, so no second service is queried.
- Per-organization values let one tenant get a feature before the rest.
- A flag read from a cached token is as stale as that token is.
Choosing hosted or self-hosted
Weigh vendor ownership of the user store against what you must operate.
- Hosting the user store means migrations, breach exposure and password policy are yours.
- A hosted provider owns the user records, so leaving means exporting and re-verifying them.
- Cost scales with users on a hosted plan and with engineering time on a self-hosted one.
Sources
- Authentication with Auth.js & Kinde Auth official documentation