From 4ba073c2c076f1c084d39b7baab779898c439af7 Mon Sep 17 00:00:00 2001 From: Tushar Pandey Date: Fri, 4 Jul 2025 11:37:33 +0530 Subject: [PATCH 1/4] uddate docs for customizing auth handlers --- EXAMPLES.md | 673 ++++++++++++++++++++++++++++++++++++++++++ README.md | 15 + V4_MIGRATION_GUIDE.md | 163 ++++++++++ 3 files changed, 851 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 009418cd..d68d64cc 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -43,6 +43,24 @@ - [On the server (App Router)](#on-the-server-app-router-3) - [On the server (Pages Router)](#on-the-server-pages-router-3) - [Middleware](#middleware-3) +- [Customizing Auth Handlers](#customizing-auth-handlers) + - [Common Customization Patterns](#common-customization-patterns) + - [Custom authentication tracking with proper error handling](#custom-authentication-tracking-with-proper-error-handling) + - [Custom session data with user metadata](#custom-session-data-with-user-metadata) + - [Middleware-based auth route customization with logout tracking](#middleware-based-auth-route-customization-with-logout-tracking) + - [Dynamic authorization parameters](#dynamic-authorization-parameters) + - [Custom error handling with specific error types](#custom-error-handling-with-specific-error-types) + - [Custom logout flow with confirmation](#custom-logout-flow-with-confirmation) + - [Advanced session management with proper error handling](#advanced-session-management-with-proper-error-handling) + - [Migration Examples](#migration-examples) +- [Troubleshooting Auth Handler Customization](#troubleshooting-auth-handler-customization) + - [Common Errors and Solutions](#common-errors-and-solutions) + - [Performance Troubleshooting](#performance-troubleshooting) + - [Security Checklist](#security-checklist) + - [Debugging Tips](#debugging-tips) + - [Environment-Specific Issues](#environment-specific-issues) + - [Getting Help](#getting-help) + - [Testing Your Implementation](#testing-your-implementation) ## Passing authorization parameters @@ -1225,3 +1243,658 @@ export async function middleware(request: NextRequest) { return resWithCombinedHeaders; } ``` + +## Customizing Auth Handlers + +In v4, the authentication routes are handled automatically by the middleware. While you can no longer customize individual route handlers directly like in v3, you can achieve similar functionality through hooks, middleware logic, and configuration options. + +### Common Customization Patterns + +#### Custom authentication tracking with proper error handling + +```ts +// lib/auth0.ts +import { NextResponse } from 'next/server'; +import { Auth0Client } from '@auth0/nextjs-auth0/server'; + +export const auth0 = new Auth0Client({ + async onCallback(error, context, session) { + if (error) { + // Log authentication errors + console.error('Authentication failed:', error); + return NextResponse.redirect( + new URL(`/error?error=${encodeURIComponent(error.message)}`, process.env.APP_BASE_URL) + ); + } + + // Track successful logins + if (session) { + console.log(`User ${session.user.sub} logged in at ${new Date().toISOString()}`); + + // You could also send this to your analytics service + // await analytics.track('user_login', { userId: session.user.sub }); + } + + // Security: Validate returnTo URL to prevent open redirects + const returnTo = context.returnTo || "/"; + const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; + + return NextResponse.redirect( + new URL(validReturnTo, process.env.APP_BASE_URL) + ); + } +}); +``` + +#### Custom session data with user metadata + +> [!WARNING] +> The `beforeSessionSaved` hook runs on every authentication. Avoid expensive operations like external API calls here as they can significantly impact authentication performance. + +```ts +// lib/auth0.ts +import { Auth0Client, filterDefaultIdTokenClaims } from '@auth0/nextjs-auth0/server'; +import type { SessionData } from '@auth0/nextjs-auth0/server'; + +// Define custom user interface for better type safety +interface CustomUser { + sub: string; + name?: string; + email?: string; + loginTime: string; + role: string; + permissions: string[]; + [key: string]: any; +} + +export const auth0 = new Auth0Client({ + async beforeSessionSaved(session, idToken) { + // Add custom metadata to the session + return { + ...session, + user: { + ...session.user, + loginTime: new Date().toISOString(), + // Add any custom claims you need + role: session.user['https://example.com/roles']?.[0] || 'user', + permissions: session.user['https://example.com/permissions'] || [] + } as CustomUser + }; + } +}); +``` + +#### Middleware-based auth route customization with logout tracking + +```ts +// middleware.ts +import type { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; +import { auth0 } from './lib/auth0'; + +export async function middleware(request: NextRequest) { + const authResponse = await auth0.middleware(request); + + // Custom logic for logout - runs BEFORE the actual logout + if (request.nextUrl.pathname === '/auth/logout') { + // Add custom logout tracking + const session = await auth0.getSession(request); + if (session) { + console.log(`User ${session.user.sub} is logging out at ${new Date().toISOString()}`); + + // You could track this in your analytics + // await analytics.track('user_logout', { userId: session.user.sub }); + } + } + + // Custom logic for login + if (request.nextUrl.pathname === '/auth/login') { + console.log('User attempting to login'); + + // You can add custom headers or modify the request + // but the actual login is handled by the auth middleware + } + + // Custom logic for callback + if (request.nextUrl.pathname === '/auth/callback') { + console.log('Processing authentication callback'); + } + + return authResponse; +} + +export const config = { + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico, sitemap.xml, robots.txt (metadata files) + */ + "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", + ], +}; +``` + +#### Dynamic authorization parameters + +```ts +// utils/auth.ts +interface LoginParams { + audience?: string; + prompt?: string; + scope?: string; + [key: string]: string | undefined; +} + +// For dynamic parameters, you can construct login URLs programmatically +function getLoginUrl(customParams: LoginParams = {}): string { + const params = new URLSearchParams(); + + // Add custom parameters + Object.entries(customParams).forEach(([key, value]) => { + if (value) { + params.append(key, value); + } + }); + + return `/auth/login?${params.toString()}`; +} + +// Usage in your components +export function LoginButton() { + return ( + + Login with Custom Params + + ); +} +``` + +#### Custom error handling with specific error types + +```ts +// lib/auth0.ts +import { NextResponse } from 'next/server'; +import { Auth0Client } from '@auth0/nextjs-auth0/server'; + +export const auth0 = new Auth0Client({ + async onCallback(error, context, session) { + if (error) { + // Handle specific error types + if (error.message.includes('access_denied')) { + return NextResponse.redirect( + new URL('/access-denied', process.env.APP_BASE_URL) + ); + } + + if (error.message.includes('invalid_grant')) { + return NextResponse.redirect( + new URL('/login-expired', process.env.APP_BASE_URL) + ); + } + + if (error.message.includes('invalid_request')) { + return NextResponse.redirect( + new URL('/invalid-request', process.env.APP_BASE_URL) + ); + } + + // Default error handling + return NextResponse.redirect( + new URL(`/error?error=${encodeURIComponent(error.message)}`, process.env.APP_BASE_URL) + ); + } + + // Security: Validate returnTo URL + const returnTo = context.returnTo || "/"; + const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; + + return NextResponse.redirect( + new URL(validReturnTo, process.env.APP_BASE_URL) + ); + } +}); +``` + +#### Custom logout flow with confirmation + +```tsx +// components/LogoutConfirmation.tsx +'use client'; + +import { useState } from 'react'; + +interface LogoutConfirmationProps { + returnTo?: string; +} + +export default function LogoutConfirmation({ returnTo }: LogoutConfirmationProps) { + const [isLoggingOut, setIsLoggingOut] = useState(false); + + const handleLogout = () => { + // Add any custom logout logic here + console.log('User confirmed logout'); + setIsLoggingOut(true); + + // Construct logout URL with proper encoding + const logoutUrl = `/auth/logout${returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : ''}`; + + // Redirect to the actual logout endpoint + window.location.href = logoutUrl; + }; + + return ( +
+

Are you sure you want to logout?

+
+ + +
+
+ ); +} +``` + +#### Advanced session management with proper error handling + +> [!WARNING] +> The `beforeSessionSaved` hook runs on every authentication and token refresh. Expensive operations like external API calls can significantly impact performance. Consider using caching strategies or moving expensive operations to a background job. + +```ts +// lib/auth0.ts +import { Auth0Client } from '@auth0/nextjs-auth0/server'; +import type { SessionData } from '@auth0/nextjs-auth0/server'; + +interface UserMetadata { + department?: string; + role?: string; + permissions?: string[]; + lastLogin?: string; +} + +export const auth0 = new Auth0Client({ + async beforeSessionSaved(session, idToken) { + // Fetch additional user data from your API with proper error handling + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout + + const response = await fetch(`https://your-api.com/users/${session.user.sub}`, { + headers: { + 'Authorization': `Bearer ${session.tokenSet.accessToken}`, + 'Content-Type': 'application/json' + }, + signal: controller.signal + }); + + clearTimeout(timeoutId); + + if (response.ok) { + const userData: UserMetadata = await response.json(); + return { + ...session, + user: { + ...session.user, + ...userData, // Merge additional user data + lastFetch: new Date().toISOString() + } + }; + } else { + console.warn(`Failed to fetch user data: ${response.status} ${response.statusText}`); + } + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + console.warn('User data fetch timed out'); + } else { + console.error('Failed to fetch user data:', error); + } + } + + // Return original session if fetch fails + return session; + } +}); +``` + +### Migration Examples + +Here are some common v3 patterns and their v4 equivalents: + +#### v3: Custom logout with redirect + +```ts +// v3 - No longer available +export const GET = handleAuth({ + async logout(req, res) { + console.log('User logging out'); + return await handleLogout(req, res, { + returnTo: 'https://example.com/goodbye' + }); + } +}); +``` + +```ts +// v4 - Equivalent approach +// Use query parameters for the redirect +Logout + +// Or add logging in middleware +import type { NextRequest } from 'next/server'; +import { auth0 } from './lib/auth0'; + +export async function middleware(request: NextRequest) { + const authResponse = await auth0.middleware(request); + + if (request.nextUrl.pathname === '/auth/logout') { + const session = await auth0.getSession(request); + if (session) { + console.log(`User ${session.user.sub} is logging out`); + } + } + + return authResponse; +} +``` + +> [!IMPORTANT] +> Always validate `returnTo` URLs to prevent open redirect attacks. Use relative URLs when possible or implement proper URL validation. + +#### v3: Custom login with parameters + +```ts +// v3 - No longer available +export const GET = handleAuth({ + async login(req, res) { + return await handleLogin(req, res, { + authorizationParams: { + audience: 'https://my-api.com', + prompt: 'consent' + } + }); + } +}); +``` + +```ts +// v4 - Equivalent approaches +// Option 1: Query parameters +Login + +// Option 2: Static configuration +import { Auth0Client } from '@auth0/nextjs-auth0/server'; + +export const auth0 = new Auth0Client({ + authorizationParameters: { + audience: 'https://my-api.com', + prompt: 'consent' + } +}); +``` + +#### v3: Custom callback handling + +```ts +// v3 - No longer available +export const GET = handleAuth({ + async callback(req, res) { + try { + await handleCallback(req, res, { + async afterCallback(req, res, session) { + console.log('User authenticated:', session.user.sub); + return session; + } + }); + } catch (error) { + console.error('Auth error:', error); + res.redirect('/error'); + } + } +}); +``` + +```ts +// v4 - Equivalent approach +import { NextResponse } from 'next/server'; +import { Auth0Client } from '@auth0/nextjs-auth0/server'; + +export const auth0 = new Auth0Client({ + async onCallback(error, context, session) { + if (error) { + console.error('Auth error:', error); + return NextResponse.redirect( + new URL('/error', process.env.APP_BASE_URL) + ); + } + + if (session) { + console.log('User authenticated:', session.user.sub); + } + + // Security: Validate returnTo URL + const returnTo = context.returnTo || "/"; + const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; + + return NextResponse.redirect( + new URL(validReturnTo, process.env.APP_BASE_URL) + ); + } +}); +``` + +These patterns provide the same level of customization as v3 while working with v4's middleware-based architecture and following modern security best practices. + +## Troubleshooting Auth Handler Customization + +### Common Errors and Solutions + +#### Error: "Cannot resolve module 'next/server'" +**Problem**: Missing Next.js server imports in your code. +**Solution**: Ensure you're using Next.js 13.4+ and add the correct import: +```ts +import { NextRequest, NextResponse } from 'next/server'; +``` + +#### Error: "Property 'middleware' does not exist on type 'Auth0Client'" +**Problem**: Incorrect Auth0Client usage or version mismatch. +**Solution**: Verify you're using v4 of the SDK and the correct import: +```ts +import { Auth0Client } from '@auth0/nextjs-auth0/server'; +``` + +#### Error: "Session is null in middleware" +**Problem**: Trying to access session before authentication. +**Solution**: Always check if session exists before using it: +```ts +const session = await auth0.getSession(request); +if (session) { + // Use session safely +} +``` + +#### Error: "Infinite redirect loop" +**Problem**: Improper middleware configuration or redirect validation. +**Solution**: Ensure proper middleware matcher and URL validation: +```ts +export const config = { + matcher: [ + "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", + ], +}; +``` + +#### Error: "Hook not being called" +**Problem**: Hooks configured incorrectly or not triggered. +**Solution**: Verify hook configuration and ensure they're being called: +```ts +// onCallback only runs during authentication flow +// beforeSessionSaved runs on every session save +``` + +### Performance Troubleshooting + +#### Slow Authentication +- **Check**: Are you making expensive API calls in `beforeSessionSaved`? +- **Fix**: Move expensive operations to background jobs or cache results + +#### High Memory Usage +- **Check**: Are you storing large objects in session? +- **Fix**: Limit session data and use external storage for large objects + +#### Token Refresh Issues +- **Check**: Are refresh tokens being stored properly? +- **Fix**: Verify token storage and refresh token configuration + +### Security Checklist + +- [ ] Validate all redirect URLs to prevent open redirects +- [ ] Sanitize user inputs and URL parameters +- [ ] Use HTTPS in production +- [ ] Implement proper CSRF protection +- [ ] Validate session data before using it +- [ ] Use secure session storage +- [ ] Implement proper error handling to avoid information leakage + +### Debugging Tips + +#### Enable Debug Logging +```ts +// Add to your auth0 client configuration +export const auth0 = new Auth0Client({ + // ... other config + httpTimeout: 10000, // Increase timeout for debugging +}); +``` + +#### Check Network Requests +- Use browser dev tools to inspect auth requests +- Verify callback URLs are correct +- Check for CORS issues + +#### Validate Configuration +```ts +// Add configuration validation +console.log('Auth0 Config:', { + domain: process.env.AUTH0_DOMAIN, + clientId: process.env.AUTH0_CLIENT_ID, + baseUrl: process.env.APP_BASE_URL +}); +``` + +### Environment-Specific Issues + +#### Development +- Use `http://localhost:3000` for `APP_BASE_URL` +- Ensure all environment variables are set +- Check that callback URLs match your dev environment + +#### Production +- Use HTTPS for all URLs +- Verify environment variables are properly set +- Check that production callback URLs are registered + +#### Deployment Platforms +- **Vercel**: Use `VERCEL_URL` for dynamic URLs +- **Netlify**: Configure build environment properly +- **Docker**: Ensure environment variables are passed correctly + +### Getting Help + +If you encounter issues not covered here: + +1. **Check the GitHub Issues**: Search for similar problems in the repository +2. **Review the Official Documentation**: Refer to the Auth0 docs for tenant-specific configuration +3. **Community Forums**: Ask questions in the Auth0 Community +4. **Debug Systematically**: Enable logging and trace the authentication flow step by step + +### Testing Your Implementation + +#### Unit Tests +```ts +// Example test for custom hook +describe('Auth0 Custom Hook', () => { + it('should handle callback errors', async () => { + const mockError = new Error('Test error'); + const result = await auth0.onCallback(mockError, {}, null); + expect(result.status).toBe(302); + }); +}); +``` + +#### Integration Tests +- Test the full authentication flow +- Verify session management +- Check error handling paths +- Validate security measures + +These patterns provide the same level of customization as v3 while working with v4's middleware-based architecture and following modern security best practices. + +### TypeScript Support + +All examples include proper TypeScript definitions for better type safety: + +```ts +// types/auth.ts +import type { SessionData, User } from '@auth0/nextjs-auth0/server'; + +export interface CustomUser extends User { + loginTime: string; + role: string; + permissions: string[]; + department?: string; + lastLogin?: string; +} + +export interface CustomSessionData extends SessionData { + user: CustomUser; +} + +// Hook type definitions +export type CustomOnCallbackHook = ( + error: Error | null, + context: { returnTo?: string }, + session: SessionData | null +) => Promise; + +export type CustomBeforeSessionSavedHook = ( + session: SessionData, + idToken: string | null +) => Promise; +``` + +#### Using Custom Types + +```ts +// lib/auth0.ts +import { Auth0Client } from '@auth0/nextjs-auth0/server'; +import type { CustomUser, CustomSessionData } from '../types/auth'; + +export const auth0 = new Auth0Client({ + async beforeSessionSaved(session, idToken): Promise { + return { + ...session, + user: { + ...session.user, + loginTime: new Date().toISOString(), + role: session.user['https://example.com/roles']?.[0] || 'user', + permissions: session.user['https://example.com/permissions'] || [] + } as CustomUser + }; + } +}); +``` + +These patterns provide the same level of customization as v3 while working with v4's middleware-based architecture and following modern security best practices. diff --git a/README.md b/README.md index f36ec88f..bb9bce28 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,21 @@ You can customize the client by using the options below: | httpTimeout | `number` | Integer value for the HTTP timeout in milliseconds for authentication requests. Defaults to `5000` milliseconds | | enableTelemetry | `boolean` | Boolean value to opt-out of sending the library name and version to your authorization server via the `Auth0-Client` header. Defaults to `true`. | +### Customizing Auth Handlers + +While the authentication routes are handled automatically by the middleware, you can still customize the authentication flow through hooks, middleware logic, and configuration options. Common customization patterns include: + +- **Custom login parameters**: Use query parameters or static configuration to pass custom authorization parameters +- **Custom logout behavior**: Add custom logic before logout or specify custom redirect URLs +- **Custom callback handling**: Use the `onCallback` hook to add custom logic after authentication +- **Session customization**: Use the `beforeSessionSaved` hook to modify session data +- **Middleware-based customization**: Add custom logic in your middleware for specific auth routes + +> [!IMPORTANT] +> When customizing auth handlers, always validate user inputs (especially redirect URLs) to prevent security vulnerabilities like open redirects. Use relative URLs when possible and implement proper input sanitization. + +**Quick Start**: For detailed examples and step-by-step migration patterns from v3, see [Customizing Auth Handlers](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#customizing-auth-handlers). + ## Session Cookie Configuration You can specify the following environment variables to configure the session cookie: diff --git a/V4_MIGRATION_GUIDE.md b/V4_MIGRATION_GUIDE.md index c667b119..be76f5a7 100644 --- a/V4_MIGRATION_GUIDE.md +++ b/V4_MIGRATION_GUIDE.md @@ -285,3 +285,166 @@ export const auth0 = new Auth0Client({ - `touchSession` method was removed. The middleware enables rolling sessions by default and can be configured via the [Session configuration section in the Examples guide](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#session-configuration). - `getAccessToken` can now be called in React Server Components. For examples on how to use `getAccessToken` in various environments (browser, App Router, Pages Router, Middleware), refer to the [Getting an access token section in the Examples guide](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#getting-an-access-token). - By default, v4 will use [OpenID Connect's RP-Initiated Logout](https://auth0.com/docs/authenticate/login/logout/log-users-out-of-auth0) if it's enabled on the tenant. Otherwise, it will fallback to the `/v2/logout` endpoint. + +## Customizing Auth Handlers + +In v3, you could customize individual auth handlers by providing custom implementations to the `handleAuth` function: + +```ts +// v3 approach (no longer available in v4) +export const GET = handleAuth({ + async logout(req: NextApiRequest, res: NextApiResponse) { + // Custom logout logic + console.log('User is logging out'); + + return await handleLogout(req, res); + }, + async login(req: NextApiRequest, res: NextApiResponse) { + // Custom login logic + return await handleLogin(req, res, { + authorizationParams: { + audience: 'https://my-api.com' + } + }); + } +}); +``` + +In v4, the auth routes are handled automatically by the middleware, but you can still customize the authentication flow through several approaches: + +### Customizing the login flow + +Use query parameters to pass custom authorization parameters: + +```html + +Login + + +Login with consent +``` + +Or configure them statically when initializing the client: + +```ts +export const auth0 = new Auth0Client({ + authorizationParameters: { + audience: 'https://my-api.com', + prompt: 'consent' + } +}); +``` + +### Customizing the logout flow + +Add custom logout parameters or redirect URLs: + +```html + +Logout +``` + +### Customizing the callback flow + +Use the `onCallback` hook to add custom logic after authentication: + +```ts +import { NextResponse } from 'next/server'; +import { Auth0Client } from '@auth0/nextjs-auth0/server'; + +export const auth0 = new Auth0Client({ + async onCallback(error, context, session) { + if (error) { + console.error('Authentication error:', error); + return NextResponse.redirect( + new URL(`/error?error=${error.message}`, process.env.APP_BASE_URL) + ); + } + + // Custom callback logic + console.log('User authenticated successfully'); + + // Log user login + if (session) { + console.log(`User ${session.user.sub} logged in`); + } + + // Security: Validate returnTo URL to prevent open redirects + const returnTo = context.returnTo || "/"; + const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; + + return NextResponse.redirect( + new URL(validReturnTo, process.env.APP_BASE_URL) + ); + } +}); +``` + +### Customizing the session + +Use the `beforeSessionSaved` hook to modify session data: + +```ts +import { Auth0Client } from '@auth0/nextjs-auth0/server'; + +export const auth0 = new Auth0Client({ + async beforeSessionSaved(session, idToken) { + // Add custom session data + return { + ...session, + user: { + ...session.user, + loginTime: new Date().toISOString() + } + }; + } +}); +``` + +### Adding custom logic to auth routes + +You can add custom logic in your middleware by intercepting requests to auth routes: + +> [!WARNING] +> The middleware logic shown below runs on every request that matches your middleware configuration. Be mindful of performance implications, especially for expensive operations like database queries or external API calls. + +```ts +import type { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; +import { auth0 } from './lib/auth0'; + +export async function middleware(request: NextRequest) { + const authResponse = await auth0.middleware(request); + + // Add custom logic for specific auth routes + if (request.nextUrl.pathname === '/auth/logout') { + // Custom logout logic - this runs BEFORE the actual logout + const session = await auth0.getSession(request); + if (session) { + console.log(`User ${session.user.sub} is logging out`); + } + } + + if (request.nextUrl.pathname === '/auth/login') { + // Custom login logic - this runs BEFORE the actual login + console.log('User is attempting to login'); + } + + return authResponse; +} +``` + +### Customizing logout with security considerations + +Add custom logout parameters or redirect URLs: + +```html + + +Logout +``` + +> [!IMPORTANT] +> Always validate `returnTo` URLs to prevent open redirect attacks. The SDK does not automatically validate these URLs, so you should implement validation in your application code or use relative URLs when possible. + +For more advanced customization patterns, see the [Examples guide](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#hooks). From 6ab65d71d68d2f43c17963ffad24fba389e8525b Mon Sep 17 00:00:00 2001 From: Tushar Pandey Date: Fri, 4 Jul 2025 18:05:12 +0530 Subject: [PATCH 2/4] update examples.md --- EXAMPLES.md | 683 ++++------------------------------------------------ 1 file changed, 45 insertions(+), 638 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index d68d64cc..a3ccff33 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1246,655 +1246,62 @@ export async function middleware(request: NextRequest) { ## Customizing Auth Handlers -In v4, the authentication routes are handled automatically by the middleware. While you can no longer customize individual route handlers directly like in v3, you can achieve similar functionality through hooks, middleware logic, and configuration options. +In v4, authentication routes (`/auth/login`, `/auth/logout`, `/auth/callback`) are handled automatically by the middleware. While you can no longer customize individual route handlers directly like in v3, you can intercept these routes in your middleware to run custom logic before the auth handlers execute. -### Common Customization Patterns +This approach allows you to: +- Run custom code before authentication actions (logging, analytics, validation) +- Modify the response (set cookies, headers, etc.) +- Implement custom redirects or early returns when needed +- Add business logic around authentication flows +- Maintain compatibility with existing tracking and analytics systems -#### Custom authentication tracking with proper error handling +The middleware-based approach provides the same level of control as v3's custom handlers while working seamlessly with v4's automatic route handling. -```ts -// lib/auth0.ts -import { NextResponse } from 'next/server'; -import { Auth0Client } from '@auth0/nextjs-auth0/server'; - -export const auth0 = new Auth0Client({ - async onCallback(error, context, session) { - if (error) { - // Log authentication errors - console.error('Authentication failed:', error); - return NextResponse.redirect( - new URL(`/error?error=${encodeURIComponent(error.message)}`, process.env.APP_BASE_URL) - ); - } - - // Track successful logins - if (session) { - console.log(`User ${session.user.sub} logged in at ${new Date().toISOString()}`); - - // You could also send this to your analytics service - // await analytics.track('user_login', { userId: session.user.sub }); - } - - // Security: Validate returnTo URL to prevent open redirects - const returnTo = context.returnTo || "/"; - const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; - - return NextResponse.redirect( - new URL(validReturnTo, process.env.APP_BASE_URL) - ); - } -}); -``` - -#### Custom session data with user metadata - -> [!WARNING] -> The `beforeSessionSaved` hook runs on every authentication. Avoid expensive operations like external API calls here as they can significantly impact authentication performance. - -```ts -// lib/auth0.ts -import { Auth0Client, filterDefaultIdTokenClaims } from '@auth0/nextjs-auth0/server'; -import type { SessionData } from '@auth0/nextjs-auth0/server'; - -// Define custom user interface for better type safety -interface CustomUser { - sub: string; - name?: string; - email?: string; - loginTime: string; - role: string; - permissions: string[]; - [key: string]: any; -} - -export const auth0 = new Auth0Client({ - async beforeSessionSaved(session, idToken) { - // Add custom metadata to the session - return { - ...session, - user: { - ...session.user, - loginTime: new Date().toISOString(), - // Add any custom claims you need - role: session.user['https://example.com/roles']?.[0] || 'user', - permissions: session.user['https://example.com/permissions'] || [] - } as CustomUser - }; - } -}); -``` - -#### Middleware-based auth route customization with logout tracking - -```ts -// middleware.ts -import type { NextRequest } from 'next/server'; -import { NextResponse } from 'next/server'; -import { auth0 } from './lib/auth0'; - -export async function middleware(request: NextRequest) { - const authResponse = await auth0.middleware(request); - - // Custom logic for logout - runs BEFORE the actual logout - if (request.nextUrl.pathname === '/auth/logout') { - // Add custom logout tracking - const session = await auth0.getSession(request); - if (session) { - console.log(`User ${session.user.sub} is logging out at ${new Date().toISOString()}`); - - // You could track this in your analytics - // await analytics.track('user_logout', { userId: session.user.sub }); - } - } - - // Custom logic for login - if (request.nextUrl.pathname === '/auth/login') { - console.log('User attempting to login'); - - // You can add custom headers or modify the request - // but the actual login is handled by the auth middleware - } - - // Custom logic for callback - if (request.nextUrl.pathname === '/auth/callback') { - console.log('Processing authentication callback'); - } - - return authResponse; -} - -export const config = { - matcher: [ - /* - * Match all request paths except for the ones starting with: - * - _next/static (static files) - * - _next/image (image optimization files) - * - favicon.ico, sitemap.xml, robots.txt (metadata files) - */ - "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", - ], -}; -``` - -#### Dynamic authorization parameters - -```ts -// utils/auth.ts -interface LoginParams { - audience?: string; - prompt?: string; - scope?: string; - [key: string]: string | undefined; -} - -// For dynamic parameters, you can construct login URLs programmatically -function getLoginUrl(customParams: LoginParams = {}): string { - const params = new URLSearchParams(); - - // Add custom parameters - Object.entries(customParams).forEach(([key, value]) => { - if (value) { - params.append(key, value); - } - }); - - return `/auth/login?${params.toString()}`; -} - -// Usage in your components -export function LoginButton() { - return ( - - Login with Custom Params - - ); -} -``` - -#### Custom error handling with specific error types +### Run custom code before Auth Handlers +Following example shows how to run custom logic before the `logout` handler: ```ts -// lib/auth0.ts -import { NextResponse } from 'next/server'; -import { Auth0Client } from '@auth0/nextjs-auth0/server'; - -export const auth0 = new Auth0Client({ - async onCallback(error, context, session) { - if (error) { - // Handle specific error types - if (error.message.includes('access_denied')) { - return NextResponse.redirect( - new URL('/access-denied', process.env.APP_BASE_URL) - ); - } - - if (error.message.includes('invalid_grant')) { - return NextResponse.redirect( - new URL('/login-expired', process.env.APP_BASE_URL) - ); - } - - if (error.message.includes('invalid_request')) { - return NextResponse.redirect( - new URL('/invalid-request', process.env.APP_BASE_URL) - ); - } - - // Default error handling - return NextResponse.redirect( - new URL(`/error?error=${encodeURIComponent(error.message)}`, process.env.APP_BASE_URL) - ); - } - - // Security: Validate returnTo URL - const returnTo = context.returnTo || "/"; - const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; - - return NextResponse.redirect( - new URL(validReturnTo, process.env.APP_BASE_URL) - ); - } -}); -``` - -#### Custom logout flow with confirmation - -```tsx -// components/LogoutConfirmation.tsx -'use client'; - -import { useState } from 'react'; - -interface LogoutConfirmationProps { - returnTo?: string; -} - -export default function LogoutConfirmation({ returnTo }: LogoutConfirmationProps) { - const [isLoggingOut, setIsLoggingOut] = useState(false); - - const handleLogout = () => { - // Add any custom logout logic here - console.log('User confirmed logout'); - setIsLoggingOut(true); - - // Construct logout URL with proper encoding - const logoutUrl = `/auth/logout${returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : ''}`; - - // Redirect to the actual logout endpoint - window.location.href = logoutUrl; - }; - - return ( -
-

Are you sure you want to logout?

-
- - -
-
- ); -} -``` +export async function middleware(request) { -#### Advanced session management with proper error handling + // prepare NextResponse object from auth0 middleware + const authRes = await auth0.middleware(request); -> [!WARNING] -> The `beforeSessionSaved` hook runs on every authentication and token refresh. Expensive operations like external API calls can significantly impact performance. Consider using caching strategies or moving expensive operations to a background job. + // The following interceptUrls can be used: + // "/auth/login" : intercept login auth handler + // "/auth/logout" : intercept logout auth handler + // "/auth/callback" : intercept callback auth handler + // "/your/login/returnTo/url" : intercept redirect after login + // "/your/logout/returnTo/url" : intercept redirect after logout -```ts -// lib/auth0.ts -import { Auth0Client } from '@auth0/nextjs-auth0/server'; -import type { SessionData } from '@auth0/nextjs-auth0/server'; - -interface UserMetadata { - department?: string; - role?: string; - permissions?: string[]; - lastLogin?: string; -} - -export const auth0 = new Auth0Client({ - async beforeSessionSaved(session, idToken) { - // Fetch additional user data from your API with proper error handling - try { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout - - const response = await fetch(`https://your-api.com/users/${session.user.sub}`, { - headers: { - 'Authorization': `Bearer ${session.tokenSet.accessToken}`, - 'Content-Type': 'application/json' - }, - signal: controller.signal - }); - - clearTimeout(timeoutId); - - if (response.ok) { - const userData: UserMetadata = await response.json(); - return { - ...session, - user: { - ...session.user, - ...userData, // Merge additional user data - lastFetch: new Date().toISOString() - } - }; - } else { - console.warn(`Failed to fetch user data: ${response.status} ${response.statusText}`); - } - } catch (error) { - if (error instanceof Error && error.name === 'AbortError') { - console.warn('User data fetch timed out'); - } else { - console.error('Failed to fetch user data:', error); - } - } + const interceptUrl = "/auth/logout"; - // Return original session if fetch fails - return session; - } -}); -``` - -### Migration Examples - -Here are some common v3 patterns and their v4 equivalents: - -#### v3: Custom logout with redirect - -```ts -// v3 - No longer available -export const GET = handleAuth({ - async logout(req, res) { - console.log('User logging out'); - return await handleLogout(req, res, { - returnTo: 'https://example.com/goodbye' - }); - } -}); -``` - -```ts -// v4 - Equivalent approach -// Use query parameters for the redirect -Logout - -// Or add logging in middleware -import type { NextRequest } from 'next/server'; -import { auth0 } from './lib/auth0'; - -export async function middleware(request: NextRequest) { - const authResponse = await auth0.middleware(request); - - if (request.nextUrl.pathname === '/auth/logout') { - const session = await auth0.getSession(request); - if (session) { - console.log(`User ${session.user.sub} is logging out`); - } - } - - return authResponse; -} -``` - -> [!IMPORTANT] -> Always validate `returnTo` URLs to prevent open redirect attacks. Use relative URLs when possible or implement proper URL validation. - -#### v3: Custom login with parameters - -```ts -// v3 - No longer available -export const GET = handleAuth({ - async login(req, res) { - return await handleLogin(req, res, { - authorizationParams: { - audience: 'https://my-api.com', - prompt: 'consent' - } - }); - } -}); -``` - -```ts -// v4 - Equivalent approaches -// Option 1: Query parameters -Login - -// Option 2: Static configuration -import { Auth0Client } from '@auth0/nextjs-auth0/server'; - -export const auth0 = new Auth0Client({ - authorizationParameters: { - audience: 'https://my-api.com', - prompt: 'consent' - } -}); -``` - -#### v3: Custom callback handling - -```ts -// v3 - No longer available -export const GET = handleAuth({ - async callback(req, res) { - try { - await handleCallback(req, res, { - async afterCallback(req, res, session) { - console.log('User authenticated:', session.user.sub); - return session; - } - }); - } catch (error) { - console.error('Auth error:', error); - res.redirect('/error'); - } - } -}); -``` - -```ts -// v4 - Equivalent approach -import { NextResponse } from 'next/server'; -import { Auth0Client } from '@auth0/nextjs-auth0/server'; - -export const auth0 = new Auth0Client({ - async onCallback(error, context, session) { - if (error) { - console.error('Auth error:', error); - return NextResponse.redirect( - new URL('/error', process.env.APP_BASE_URL) - ); - } - - if (session) { - console.log('User authenticated:', session.user.sub); + // intercept auth handler + if (request.nextUrl.pathname === interceptUrl) { + // do custom stuff + console.log("Pre-logout code") + + // Example: Set a cookie + authRes.cookies.set('myCustomCookie', 'cookieValue', { path: '/' }); + // Example: Set another cookie with options + authRes.cookies.set({ + name: 'anotherCookie', + value: 'anotherValue', + httpOnly: true, + path: '/', + }); + + // Example: Delete a cookie + // authRes.cookies.delete('cookieNameToDelete'); + + // you can also do an early return here with your own NextResponse object + // return NextResponse.redirect(new URL('/custom-logout-page')); } - // Security: Validate returnTo URL - const returnTo = context.returnTo || "/"; - const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; - - return NextResponse.redirect( - new URL(validReturnTo, process.env.APP_BASE_URL) - ); - } -}); -``` - -These patterns provide the same level of customization as v3 while working with v4's middleware-based architecture and following modern security best practices. - -## Troubleshooting Auth Handler Customization - -### Common Errors and Solutions - -#### Error: "Cannot resolve module 'next/server'" -**Problem**: Missing Next.js server imports in your code. -**Solution**: Ensure you're using Next.js 13.4+ and add the correct import: -```ts -import { NextRequest, NextResponse } from 'next/server'; -``` - -#### Error: "Property 'middleware' does not exist on type 'Auth0Client'" -**Problem**: Incorrect Auth0Client usage or version mismatch. -**Solution**: Verify you're using v4 of the SDK and the correct import: -```ts -import { Auth0Client } from '@auth0/nextjs-auth0/server'; -``` - -#### Error: "Session is null in middleware" -**Problem**: Trying to access session before authentication. -**Solution**: Always check if session exists before using it: -```ts -const session = await auth0.getSession(request); -if (session) { - // Use session safely -} -``` - -#### Error: "Infinite redirect loop" -**Problem**: Improper middleware configuration or redirect validation. -**Solution**: Ensure proper middleware matcher and URL validation: -```ts -export const config = { - matcher: [ - "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", - ], -}; -``` - -#### Error: "Hook not being called" -**Problem**: Hooks configured incorrectly or not triggered. -**Solution**: Verify hook configuration and ensure they're being called: -```ts -// onCallback only runs during authentication flow -// beforeSessionSaved runs on every session save -``` - -### Performance Troubleshooting - -#### Slow Authentication -- **Check**: Are you making expensive API calls in `beforeSessionSaved`? -- **Fix**: Move expensive operations to background jobs or cache results - -#### High Memory Usage -- **Check**: Are you storing large objects in session? -- **Fix**: Limit session data and use external storage for large objects - -#### Token Refresh Issues -- **Check**: Are refresh tokens being stored properly? -- **Fix**: Verify token storage and refresh token configuration - -### Security Checklist - -- [ ] Validate all redirect URLs to prevent open redirects -- [ ] Sanitize user inputs and URL parameters -- [ ] Use HTTPS in production -- [ ] Implement proper CSRF protection -- [ ] Validate session data before using it -- [ ] Use secure session storage -- [ ] Implement proper error handling to avoid information leakage - -### Debugging Tips - -#### Enable Debug Logging -```ts -// Add to your auth0 client configuration -export const auth0 = new Auth0Client({ - // ... other config - httpTimeout: 10000, // Increase timeout for debugging -}); -``` - -#### Check Network Requests -- Use browser dev tools to inspect auth requests -- Verify callback URLs are correct -- Check for CORS issues - -#### Validate Configuration -```ts -// Add configuration validation -console.log('Auth0 Config:', { - domain: process.env.AUTH0_DOMAIN, - clientId: process.env.AUTH0_CLIENT_ID, - baseUrl: process.env.APP_BASE_URL -}); -``` - -### Environment-Specific Issues - -#### Development -- Use `http://localhost:3000` for `APP_BASE_URL` -- Ensure all environment variables are set -- Check that callback URLs match your dev environment - -#### Production -- Use HTTPS for all URLs -- Verify environment variables are properly set -- Check that production callback URLs are registered - -#### Deployment Platforms -- **Vercel**: Use `VERCEL_URL` for dynamic URLs -- **Netlify**: Configure build environment properly -- **Docker**: Ensure environment variables are passed correctly - -### Getting Help - -If you encounter issues not covered here: - -1. **Check the GitHub Issues**: Search for similar problems in the repository -2. **Review the Official Documentation**: Refer to the Auth0 docs for tenant-specific configuration -3. **Community Forums**: Ask questions in the Auth0 Community -4. **Debug Systematically**: Enable logging and trace the authentication flow step by step - -### Testing Your Implementation - -#### Unit Tests -```ts -// Example test for custom hook -describe('Auth0 Custom Hook', () => { - it('should handle callback errors', async () => { - const mockError = new Error('Test error'); - const result = await auth0.onCallback(mockError, {}, null); - expect(result.status).toBe(302); - }); -}); -``` - -#### Integration Tests -- Test the full authentication flow -- Verify session management -- Check error handling paths -- Validate security measures - -These patterns provide the same level of customization as v3 while working with v4's middleware-based architecture and following modern security best practices. - -### TypeScript Support - -All examples include proper TypeScript definitions for better type safety: - -```ts -// types/auth.ts -import type { SessionData, User } from '@auth0/nextjs-auth0/server'; - -export interface CustomUser extends User { - loginTime: string; - role: string; - permissions: string[]; - department?: string; - lastLogin?: string; + // return the original auth0-handled NextResponse object + return authRes } - -export interface CustomSessionData extends SessionData { - user: CustomUser; -} - -// Hook type definitions -export type CustomOnCallbackHook = ( - error: Error | null, - context: { returnTo?: string }, - session: SessionData | null -) => Promise; - -export type CustomBeforeSessionSavedHook = ( - session: SessionData, - idToken: string | null -) => Promise; -``` - -#### Using Custom Types - -```ts -// lib/auth0.ts -import { Auth0Client } from '@auth0/nextjs-auth0/server'; -import type { CustomUser, CustomSessionData } from '../types/auth'; - -export const auth0 = new Auth0Client({ - async beforeSessionSaved(session, idToken): Promise { - return { - ...session, - user: { - ...session.user, - loginTime: new Date().toISOString(), - role: session.user['https://example.com/roles']?.[0] || 'user', - permissions: session.user['https://example.com/permissions'] || [] - } as CustomUser - }; - } -}); ``` -These patterns provide the same level of customization as v3 while working with v4's middleware-based architecture and following modern security best practices. +### Run code after callback +Please refer to [onCallback](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#oncallback) +for details on how to run code after callback. \ No newline at end of file From 333c975ec1dadc34b16e83a40f10b6700a2799ab Mon Sep 17 00:00:00 2001 From: Tushar Pandey Date: Fri, 4 Jul 2025 18:08:11 +0530 Subject: [PATCH 3/4] update examples.md --- EXAMPLES.md | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index a3ccff33..dcab9ae1 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -44,23 +44,8 @@ - [On the server (Pages Router)](#on-the-server-pages-router-3) - [Middleware](#middleware-3) - [Customizing Auth Handlers](#customizing-auth-handlers) - - [Common Customization Patterns](#common-customization-patterns) - - [Custom authentication tracking with proper error handling](#custom-authentication-tracking-with-proper-error-handling) - - [Custom session data with user metadata](#custom-session-data-with-user-metadata) - - [Middleware-based auth route customization with logout tracking](#middleware-based-auth-route-customization-with-logout-tracking) - - [Dynamic authorization parameters](#dynamic-authorization-parameters) - - [Custom error handling with specific error types](#custom-error-handling-with-specific-error-types) - - [Custom logout flow with confirmation](#custom-logout-flow-with-confirmation) - - [Advanced session management with proper error handling](#advanced-session-management-with-proper-error-handling) - - [Migration Examples](#migration-examples) -- [Troubleshooting Auth Handler Customization](#troubleshooting-auth-handler-customization) - - [Common Errors and Solutions](#common-errors-and-solutions) - - [Performance Troubleshooting](#performance-troubleshooting) - - [Security Checklist](#security-checklist) - - [Debugging Tips](#debugging-tips) - - [Environment-Specific Issues](#environment-specific-issues) - - [Getting Help](#getting-help) - - [Testing Your Implementation](#testing-your-implementation) + - [Run custom code before Auth Handlers](#run-custom-code-before-auth-handlers) + - [Run code after callback](#run-code-after-callback) ## Passing authorization parameters From d99db96c72198583983c70a0ead8aff18d754336 Mon Sep 17 00:00:00 2001 From: Tushar Pandey Date: Fri, 4 Jul 2025 21:06:09 +0530 Subject: [PATCH 4/4] update docs --- README.md | 14 +++-- V4_MIGRATION_GUIDE.md | 140 +++++++++++------------------------------- 2 files changed, 44 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index bb9bce28..7f0cde2d 100644 --- a/README.md +++ b/README.md @@ -150,13 +150,15 @@ You can customize the client by using the options below: ### Customizing Auth Handlers -While the authentication routes are handled automatically by the middleware, you can still customize the authentication flow through hooks, middleware logic, and configuration options. Common customization patterns include: +While the authentication routes are handled automatically by the middleware, you can still customize the authentication flow through two main approaches: -- **Custom login parameters**: Use query parameters or static configuration to pass custom authorization parameters -- **Custom logout behavior**: Add custom logic before logout or specify custom redirect URLs -- **Custom callback handling**: Use the `onCallback` hook to add custom logic after authentication -- **Session customization**: Use the `beforeSessionSaved` hook to modify session data -- **Middleware-based customization**: Add custom logic in your middleware for specific auth routes +- **Run custom code before auth handlers**: Intercept auth routes in your middleware to add custom logic before authentication actions +- **Run code after authentication**: Use the `onCallback` hook to add custom logic after authentication completes + +Additional customization options include: +- Login parameters via query parameters or static configuration +- Session data modification using the `beforeSessionSaved` hook +- Logout redirects using query parameters > [!IMPORTANT] > When customizing auth handlers, always validate user inputs (especially redirect URLs) to prevent security vulnerabilities like open redirects. Use relative URLs when possible and implement proper input sanitization. diff --git a/V4_MIGRATION_GUIDE.md b/V4_MIGRATION_GUIDE.md index be76f5a7..187c3f2c 100644 --- a/V4_MIGRATION_GUIDE.md +++ b/V4_MIGRATION_GUIDE.md @@ -310,43 +310,40 @@ export const GET = handleAuth({ }); ``` -In v4, the auth routes are handled automatically by the middleware, but you can still customize the authentication flow through several approaches: +In v4, the auth routes are handled automatically by the middleware, but you can achieve similar customization through two main approaches: -### Customizing the login flow +### 1. Run custom code before auth handlers (Middleware Interception) -Use query parameters to pass custom authorization parameters: - -```html - -Login - - -Login with consent -``` - -Or configure them statically when initializing the client: +You can intercept auth routes in your middleware to run custom logic before the auth handlers execute: ```ts -export const auth0 = new Auth0Client({ - authorizationParameters: { - audience: 'https://my-api.com', - prompt: 'consent' - } -}); -``` - -### Customizing the logout flow - -Add custom logout parameters or redirect URLs: +import type { NextRequest } from 'next/server'; +import { auth0 } from './lib/auth0'; -```html - -Logout +export async function middleware(request: NextRequest) { + const authRes = await auth0.middleware(request); + + // Intercept specific auth routes + if (request.nextUrl.pathname === '/auth/logout') { + // Custom logout logic runs BEFORE the actual logout + console.log('User is logging out'); + + // Example: Set custom cookies + authRes.cookies.set('logoutTime', new Date().toISOString()); + } + + if (request.nextUrl.pathname === '/auth/login') { + // Custom login logic runs BEFORE the actual login + console.log('User is attempting to login'); + } + + return authRes; +} ``` -### Customizing the callback flow +### 2. Run code after authentication (Callback Hook) -Use the `onCallback` hook to add custom logic after authentication: +Use the `onCallback` hook to add custom logic after authentication completes: ```ts import { NextResponse } from 'next/server'; @@ -357,94 +354,29 @@ export const auth0 = new Auth0Client({ if (error) { console.error('Authentication error:', error); return NextResponse.redirect( - new URL(`/error?error=${error.message}`, process.env.APP_BASE_URL) + new URL('/error', process.env.APP_BASE_URL) ); } - // Custom callback logic - console.log('User authenticated successfully'); - - // Log user login + // Custom logic after successful authentication if (session) { - console.log(`User ${session.user.sub} logged in`); + console.log(`User ${session.user.sub} logged in successfully`); } - // Security: Validate returnTo URL to prevent open redirects - const returnTo = context.returnTo || "/"; - const validReturnTo = returnTo.startsWith('/') ? returnTo : "/"; - return NextResponse.redirect( - new URL(validReturnTo, process.env.APP_BASE_URL) + new URL(context.returnTo || "/", process.env.APP_BASE_URL) ); } }); ``` -### Customizing the session - -Use the `beforeSessionSaved` hook to modify session data: - -```ts -import { Auth0Client } from '@auth0/nextjs-auth0/server'; +### Additional Customization Options -export const auth0 = new Auth0Client({ - async beforeSessionSaved(session, idToken) { - // Add custom session data - return { - ...session, - user: { - ...session.user, - loginTime: new Date().toISOString() - } - }; - } -}); -``` - -### Adding custom logic to auth routes - -You can add custom logic in your middleware by intercepting requests to auth routes: - -> [!WARNING] -> The middleware logic shown below runs on every request that matches your middleware configuration. Be mindful of performance implications, especially for expensive operations like database queries or external API calls. - -```ts -import type { NextRequest } from 'next/server'; -import { NextResponse } from 'next/server'; -import { auth0 } from './lib/auth0'; - -export async function middleware(request: NextRequest) { - const authResponse = await auth0.middleware(request); - - // Add custom logic for specific auth routes - if (request.nextUrl.pathname === '/auth/logout') { - // Custom logout logic - this runs BEFORE the actual logout - const session = await auth0.getSession(request); - if (session) { - console.log(`User ${session.user.sub} is logging out`); - } - } - - if (request.nextUrl.pathname === '/auth/login') { - // Custom login logic - this runs BEFORE the actual login - console.log('User is attempting to login'); - } - - return authResponse; -} -``` - -### Customizing logout with security considerations - -Add custom logout parameters or redirect URLs: - -```html - - -Logout -``` +- **Login parameters**: Use query parameters (`/auth/login?audience=...`) or static configuration +- **Session data**: Use the `beforeSessionSaved` hook to modify session data +- **Logout redirects**: Use query parameters (`/auth/logout?returnTo=...`) > [!IMPORTANT] -> Always validate `returnTo` URLs to prevent open redirect attacks. The SDK does not automatically validate these URLs, so you should implement validation in your application code or use relative URLs when possible. +> Always validate redirect URLs to prevent open redirect attacks. Use relative URLs when possible. -For more advanced customization patterns, see the [Examples guide](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#hooks). +For detailed examples and implementation patterns, see [Customizing Auth Handlers](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#customizing-auth-handlers) in the Examples guide.