auth.js
Authentication library with multiple provider support
$ npx docs2skills add authjs-authenticationAuth.js
Runtime-agnostic authentication library with comprehensive provider support
What this skill does
Auth.js is a framework-agnostic authentication library that provides secure, standards-based authentication for modern JavaScript applications. It abstracts complex OAuth flows, session management, and security concerns into a simple API that works across Next.js, SvelteKit, Qwik, Express, and other frameworks.
The library handles the complete authentication lifecycle: provider integration, callback processing, session creation, token management, and user data persistence. It supports over 80 OAuth providers out of the box, plus email-based magic links, traditional credentials, and modern WebAuthn/passkeys. Auth.js manages CSRF protection, PKCE flows, state validation, and secure cookie handling automatically.
Unlike framework-specific solutions, Auth.js provides consistent APIs across different runtime environments while leveraging each framework's native patterns. It can operate with or without a database, supports custom authentication pages, and provides TypeScript-first development experience with full type safety for user sessions and provider data.
Prerequisites
- Node.js 18+ or compatible runtime (Vercel Edge, Cloudflare Workers)
- Supported framework: Next.js 13+, SvelteKit, Qwik, or Express
- OAuth provider credentials for external authentication
- Database adapter (optional) for persistent sessions
- Environment variables for AUTH_SECRET and provider credentials
Quick start
npm install @auth/core @auth/sveltekit # or @next-auth for Next.js
// src/hooks.server.js (SvelteKit)
import { SvelteKitAuth } from '@auth/sveltekit'
import GitHub from '@auth/sveltekit/providers/github'
export const { handle } = SvelteKitAuth({
providers: [
GitHub({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET
})
]
})
<!-- src/routes/+page.svelte -->
<script>
import { signIn, signOut } from '@auth/sveltekit/client'
import { page } from '$app/stores'
</script>
{#if $page.data.session}
<p>Welcome {$page.data.session.user.name}!</p>
<button on:click={() => signOut()}>Sign Out</button>
{:else}
<button on:click={() => signIn('github')}>Sign in with GitHub</button>
{/if}
Core concepts
Providers are authentication sources that handle user verification. OAuth providers like GitHub and Google redirect users to external services, while credentials providers validate username/password locally. Email providers send magic links for passwordless authentication.
Sessions represent authenticated user state. Auth.js creates sessions after successful authentication and stores them in encrypted cookies by default. Sessions contain user information and can be extended with custom data through callbacks.
Adapters connect Auth.js to databases for persistent user accounts, sessions, and OAuth tokens. Without an adapter, Auth.js operates stateless with JWT sessions. Adapters enable user account linking across providers and persistent session storage.
Callbacks are lifecycle hooks that customize authentication behavior. The signIn callback controls who can authenticate, session customizes session data, and jwt modifies JWT tokens. Callbacks enable custom authorization logic and data transformation.
Pages can be customized to match your application design. Auth.js provides default signin, signout, error, and verification pages, but you can override them with custom implementations while maintaining security.
Key API surface
| Function | Purpose |
|---|---|
signIn(provider, options) | Initiate authentication with specific provider |
signOut(options) | End user session and clear authentication |
auth() | Get current session (server-side) |
useSession() | React hook for client-side session access |
getServerSession() | Access session in API routes |
unstable_update() | Update current session data |
redirect() | Protect routes with authentication redirect |
AuthConfig.providers | Array of authentication provider configurations |
AuthConfig.callbacks | Lifecycle hooks for customization |
AuthConfig.pages | Custom page URLs for auth flows |
AuthConfig.adapter | Database integration for persistence |
AuthConfig.session.strategy | 'jwt' or 'database' session storage |
Common patterns
OAuth with profile customization:
export const authOptions = {
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
})
],
callbacks: {
session: ({ session, token }) => ({
...session,
user: {
...session.user,
id: token.sub,
}
})
}
}
Protected API routes:
import { auth } from '@/auth'
export async function GET() {
const session = await auth()
if (!session) {
return new Response('Unauthorized', { status: 401 })
}
return Response.json({ data: 'protected' })
}
Multiple providers with credentials:
providers: [
GitHub({ clientId: '...', clientSecret: '...' }),
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' }
},
authorize: async (credentials) => {
const user = await validateUser(credentials)
return user ? { id: user.id, email: user.email } : null
}
})
]
Database integration:
import { PrismaAdapter } from '@auth/prisma-adapter'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
export const authOptions = {
adapter: PrismaAdapter(prisma),
session: { strategy: 'database' },
providers: [...]
}
Configuration
Environment variables:
AUTH_SECRET: Required 32+ character string for JWT encryptionAUTH_URL: Base URL for auth routes (auto-detected in most frameworks)AUTH_TRUST_HOST: Set to true when behind proxy- Provider-specific:
GITHUB_CLIENT_ID,GOOGLE_CLIENT_SECRET, etc.
Session configuration:
session: {
strategy: 'jwt', // 'jwt' | 'database'
maxAge: 30 * 24 * 60 * 60, // 30 days in seconds
updateAge: 24 * 60 * 60, // Update session every 24 hours
}
JWT configuration:
jwt: {
maxAge: 60 * 60 * 24 * 30, // 30 days
encode: async ({ secret, token }) => { /* custom encoding */ },
decode: async ({ secret, token }) => { /* custom decoding */ }
}
Cookie configuration:
cookies: {
sessionToken: {
name: 'next-auth.session-token',
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production'
}
}
}
Best practices
Always set AUTH_SECRET in production with a cryptographically secure random string. Use openssl rand -base64 32 to generate one.
Use database sessions for multi-device scenarios where users need consistent login state across devices or when you need to revoke sessions.
Implement proper error handling with custom error pages and callback error handling to gracefully manage authentication failures.
Validate sessions server-side for protected routes and API endpoints. Never trust client-side session state for authorization decisions.
Customize session data judiciously - only include necessary information in JWT tokens to keep them lightweight and avoid sensitive data exposure.
Configure CORS properly when using Auth.js with separate frontend/backend deployments. Set appropriate origins and credentials handling.
Use TypeScript augmentation to type custom session properties:
declare module 'next-auth' {
interface Session {
user: {
id: string
role: string
} & DefaultSession['user']
}
}
Gotchas and common mistakes
Missing AUTH_SECRET causes authentication failures - Auth.js requires this for JWT signing and session encryption. Set it in all environments.
OAuth redirect URI mismatches break authentication - Ensure your provider's redirect URI exactly matches your application's callback URL including protocol and port.
Database schema must match adapter requirements - When using adapters, your database schema must include all required tables and columns. Use adapter-specific migration scripts.
JWT size limits can cause cookie overflow - Browsers limit cookie size to 4KB. Keep JWT payloads minimal or switch to database sessions for large user objects.
Session strategy affects callback behavior - JWT strategy uses the jwt callback then session, while database strategy only uses session. Don't mix patterns.
Custom pages must handle all authentication states - When overriding default pages, ensure you handle signin, signout, error, and verification flows properly.
Credentials provider disables database sessions - The credentials provider forces JWT strategy. You cannot use database sessions with credentials authentication.
CSRF protection requires proper configuration - Auth.js validates CSRF tokens automatically, but custom implementations must maintain state validation.
Provider scope affects available user data - OAuth providers return different user information based on requested scopes. Configure scopes to match your data needs.
Session updates don't work with all strategies - unstable_update() only works with JWT sessions. Database sessions require manual updates through your database.
Callback URLs must be registered with providers - All OAuth providers require callback URL registration. Use [origin]/api/auth/callback/[provider] format.
Environment variable naming varies by framework - Some frameworks require NEXTAUTH_* prefixes while others use AUTH_*. Check framework-specific documentation.