Auth.js Knowledge Patch
Use this patch to choose the maintained authentication path, avoid security-sensitive provider mistakes, and apply current Auth.js integration patterns. Read the topic reference before changing an affected flow; the quick reference below prioritizes breaking behavior, security updates, and common implementation work.
Reference index
| Reference | Topics | | --- | --- | | Better Auth transition | Project status, choosing Auth.js or Better Auth, migration direction | | Providers and authentication | Account linking, credentials errors, email providers, passkeys, OAuth customization, redirect proxies | | Sessions and frameworks | Session lifecycle, Qwik, SvelteKit, Express | | v5 migration | Next.js Pages Router limitations, Next.js 16 proxy protection | | Adapters, operations, and security | Adapter contracts, value normalization, logging, security upgrade floors |
Decide between Auth.js and Better Auth
- Prefer Better Auth for a new project.
- Keep Auth.js for an existing application that needs continued security and urgent fixes, or when Better Auth still lacks a required capability such as stateless sessions without a database.
- Use the NextAuth migration guide when moving an existing application to Better Auth.
- Read Better Auth transition before making the choice.
Apply urgent compatibility and security fixes
Audit installed package versions before debugging provider or adapter behavior.
| Package | Required action | Reason |
| --- | --- | --- |
| @auth/sveltekit | Upgrade to 1.11.1 or later | Pull in the Nodemailer security fix. |
| next-auth v4 | Upgrade to 4.24.14 or configure the GitHub issuer explicitly | Accept GitHub callbacks containing the now-validated iss parameter. |
| @auth/kysely-adapter | Upgrade to 1.11.2 and install kysely@^0.28.15 | Address CVE-2026-33468, an SQL-injection vulnerability. |
Read Adapters, operations, and security for exact upgrade implications.
Treat cross-provider linking as a security boundary
With a database and multiple authentication methods, a later sign-in can link to an existing user when emails match. Evaluate the email-verification guarantee of every enabled provider; the weakest provider can undermine linking safety.
Do not assume matching email text alone proves common ownership. Read Providers and authentication before enabling a provider alongside existing accounts.
Handle credentials failures by invocation style
Returning null from authorize has two observable forms:
- Expect built-in-page flows to redirect with
?error=CredentialsSignin&code=credentials. - Catch a thrown
CredentialsSigninin form actions and custom server-side flows. - Subclass
CredentialsSigninto replace the public, URL-visiblecode; keep the value generic enough to avoid leaking sensitive details.
class InvalidLoginError extends CredentialsSignin {
code = "invalid_credentials"
}
Credentials({
async authorize(credentials) {
const user = await authenticate(credentials)
if (!user) throw new InvalidLoginError()
return user
},
})
Configure experimental passkeys completely
Treat passkeys as experimental. Before enabling them:
- Run Node.js 20 or later.
- Use a compatible database adapter and apply its migration for the
Authenticatortable. - Install
@simplewebauthn/server@9.0.3. - Add the singular
Passkeyprovider and setexperimental.enableWebAuthntotrue. - For a custom page, also install
@simplewebauthn/browser@9.0.1and importsignInfromnext-auth/webauthn.
export default {
adapter: PrismaAdapter(prisma),
providers: [Passkey],
experimental: { enableWebAuthn: true },
}
Register a passkey only for an authenticated user; omit the action for ordinary sign-in.
import { signIn } from "next-auth/webauthn"
await signIn("passkey", { action: "register" })
await signIn("passkey")
Use the built-in sign-in page when possible because it exposes the configured passkey action automatically. Check the package floors in Providers and authentication.
Configure email authentication
- Provide a database adapter for every email-type provider so verification tokens can be stored and consumed.
- For Loops, create the transactional template in advance, use the case-sensitive
urltemplate variable, and configure both the API key and transactional ID. - For an arbitrary HTTP email service, define a raw provider with
type: "email", implementsendVerificationRequest, and use the provideridwhen initiating sign-in. - Read Providers and authentication for working configurations.
Customize OAuth providers without replacing defaults
- Return extra persisted
Userfields fromprofile(). - Add or omit persisted
Accountfields withaccount(). - Pass narrow nested overrides to a built-in provider; Auth.js deep-merges them with its defaults.
- Assign a fetch-compatible transport to the symbol-keyed
[customFetch]option to scope proxying to one provider. - Do not use
RedirectProxyUrlwith Apple; select another callback strategy.
Read Providers and authentication before changing profile persistence or transport behavior.
Implement only the adapter surface the deployment uses
A local adapter may implement the methods required by enabled flows. An officially distributed adapter must implement the complete Adapter interface.
| Enabled flow | Required methods |
| --- | --- |
| User/account management | createUser, getUser, getUserByAccount, updateUser, linkAccount |
| Database sessions | createSession, getSessionAndUser, updateSession, deleteSession |
| Passwordless email | getUserByEmail, createVerificationToken, useVerificationToken |
Do not build a local flow around deleteUser or unlinkAccount; Auth.js does not currently invoke them. Normalize database-native values to plain JavaScript objects in both directions, including values stored in custom fields. Read Adapters, operations, and security.
Preserve fresh session state
- Rely on the session endpoint's cache-prevention headers for its GET responses.
- Expect an expired database-backed
Sessionrow to be deleted when Auth.js reads it. - Populate a request-local session once in Express middleware when several handlers need it.
- Read Sessions and frameworks for framework-specific access patterns.
Access and mutate sessions by framework
| Framework | Read session | Sign in or out |
| --- | --- | --- |
| Qwik server | event.sharedMap.get("session") | Use the actions returned by useSignIn() and useSignOut() in <Form> or call .submit(). |
| Qwik client | useSession() | Submit providerId and options.redirectTo for sign-in; submit redirectTo for sign-out. |
| SvelteKit server | event.locals.auth() after installing the Auth.js handle | Wire signIn or signOut to matching default form actions. |
| SvelteKit client | Return the server session through page data | Import the client handlers from @auth/sveltekit/client. |
| Express | getSession(req) | Call signIn(req, res) or signOut(req, res) from application-owned routes. |
Protect Next.js routes with the correct file convention
For Next.js 16, export Auth.js auth as proxy from proxy.ts; keep middleware.ts and the middleware export on older Next.js versions. The proxy matcher chooses the covered routes, and the authorized callback decides whether the request proceeds.
// proxy.ts
export { auth as proxy } from "@/auth"
// auth.ts
export const { auth, handlers } = NextAuth({
callbacks: {
authorized: async ({ auth }) => !!auth,
},
})
Do not expect restored server-side session helpers in Pages Router API routes; fetch the session REST endpoint there. Continue to call auth(ctx) from getServerSideProps. Read v5 migration.
Configure logging deliberately
When supplying custom logger handlers, route debug output through logger.debug. The separate debug option is ignored once a custom logger is present.
NextAuth({
logger: {
error: (code, ...message) => log.error(code, message),
warn: (code, ...message) => log.warn(code, message),
debug: (code, ...message) => log.debug(code, message),
},
})
Work through a change safely
- Identify the provider, session strategy, adapter, and framework integration involved.
- Open the matching reference from the index before editing configuration.
- Check security floors and experimental prerequisites.
- Trace the invocation style: built-in page, form action, server handler, or client action.
- Test success, rejection, expiry, redirect, and account-linking paths as applicable.
- Verify that public error codes and logs reveal no sensitive authentication detail.