Skip to content

Usability upgrades to V4 Migration Guide #2095

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 19, 2025
20 changes: 10 additions & 10 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ In middleware, the `getAccessToken(req, res)` helper can be used to get an acces
```tsx
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function middleware(request: NextRequest) {
const authRes = await auth0.middleware(request)
Expand Down Expand Up @@ -486,7 +486,7 @@ If you are using the Pages Router and are calling the `getAccessToken` method in
```ts
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function middleware(request: NextRequest) {
const authRes = await auth0.middleware(request)
Expand Down Expand Up @@ -589,7 +589,7 @@ You can wrap your components in an `<Auth0Provider />` and pass an initial user
```tsx
import { Auth0Provider } from "@auth0/nextjs-auth0"

import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export default async function RootLayout({
children,
Expand Down Expand Up @@ -913,7 +913,7 @@ Additionally to the ability to initialize the interactive login process by redir
the `startInteractiveLogin` method can also be called programmatically.

```typescript
import { auth0 } from "./lib/auth0";
import { auth0 } from "./lib/auth0"; // Adjust path if your auth0 client is elsewhere
import { NextRequest } from "next/server";

export const GET = async (req: NextRequest) => {
Expand All @@ -937,7 +937,7 @@ export const auth0 = new Auth0Client({
The second option is by configuring `authorizationParams` when calling `startInteractiveLogin`:

```ts
import { auth0 } from "./lib/auth0";
import { auth0 } from "./lib/auth0"; // Adjust path if your auth0 client is elsewhere
import { NextRequest } from "next/server";

export const GET = async (req: NextRequest) => {
Expand All @@ -958,7 +958,7 @@ export const GET = async (req: NextRequest) => {
When calling `startInteractiveLogin`, the `returnTo` parameter can be configured to specify where you would like to redirect the user to after they have completed their authentication and have returned to your application.

```ts
import { auth0 } from "./lib/auth0";
import { auth0 } from "./lib/auth0"; // Adjust path if your auth0 client is elsewhere
import { NextRequest } from "next/server";

export const GET = async (req: NextRequest) => {
Expand Down Expand Up @@ -991,7 +991,7 @@ For example:
```ts
import { NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function GET() {
try {
Expand All @@ -1016,7 +1016,7 @@ On the server, the `getAccessTokenForConnection({}, req, res)` helper can be use
```ts
import type { NextApiRequest, NextApiResponse } from "next"

import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export default async function handler(
req: NextApiRequest,
Expand All @@ -1039,7 +1039,7 @@ In middleware, the `getAccessTokenForConnection({}, req, res)` helper can be use
```tsx
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function middleware(request: NextRequest) {
const authRes = await auth0.middleware(request)
Expand Down Expand Up @@ -1070,7 +1070,7 @@ If you are using the Pages Router and are calling the `getAccessTokenForConnecti
```ts
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function middleware(request: NextRequest) {
const authRes = await auth0.middleware(request)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Create a `middleware.ts` file in the root of your project's directory:
```ts
import type { NextRequest } from "next/server"

import { auth0 } from "./lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function middleware(request: NextRequest) {
return await auth0.middleware(request)
Expand All @@ -97,7 +97,7 @@ export const config = {
You can now begin to authenticate your users by redirecting them to your application's `/auth/login` route:

```tsx
import { auth0 } from "@/lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export default async function Home() {
const session = await auth0.getSession()
Expand Down
114 changes: 80 additions & 34 deletions V4_MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ In v4, the routes are now mounted automatically by the middleware:
```ts
import type { NextRequest } from "next/server"

import { auth0 } from "./lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function middleware(request: NextRequest) {
return await auth0.middleware(request)
Expand All @@ -57,7 +57,7 @@ Additionally, in v4, the mounted routes drop the `/api` prefix. For example, the

The complete list of routes mounted by the SDK can be found [here](https://github.com/auth0/nextjs-auth0/tree/main?tab=readme-ov-file#routes).

## Auth0 middleware
## The Auth0 middleware

In v4, the Auth0 middleware is a central component of the SDK. It serves a number of core functions such as registering the required authentication endpoints, providing rolling sessions functionality, keeping access tokens fresh, etc.

Expand All @@ -68,10 +68,10 @@ When configuring your application to use v4 of the SDK, it is now **required** t

import type { NextRequest } from "next/server"

import { auth0 } from "./lib/auth0"
import { auth0 } from "./lib/auth0" // Adjust path if your auth0 client is elsewhere

export async function middleware(request: NextRequest) {
return await auth0.middleware(request)
return await auth0.middleware(request) // Returns a NextResponse object
}

export const config = {
Expand All @@ -86,6 +86,8 @@ export const config = {
],
}
```
> [!NOTE]
> The above middleware is a basic setup. It passes incoming requests to the Auth0 SDK's request handler, which in turn manages the [default auto-mounted authentication routes](https://github.com/auth0/nextjs-auth0/blob/main/README.md#routes), user sessions, and the overall authentication flow. It does **not** protect any routes by default, in order to protect routes from unauthenticated users, read the section below on [protecting routes](https://github.com/auth0/nextjs-auth0/blob/main/V4_MIGRATION_GUIDE.md#protecting-routes).

See [the Getting Started section](https://github.com/auth0/nextjs-auth0/tree/main?tab=readme-ov-file#getting-started) for details on how to configure the middleware.

Expand All @@ -94,56 +96,73 @@ See [the Getting Started section](https://github.com/auth0/nextjs-auth0/tree/mai
By default, **the middleware does not protect any routes**. To protect a page, you can use the `getSession()` handler in the middleware, like so:

```ts
export async function middleware(request: NextRequest) {
const authRes = await auth0.middleware(request)

// authentication routes — let the middleware handle it
if (request.nextUrl.pathname.startsWith("/auth")) {
export async function middleware(request) {
const authRes = await auth0.middleware(request); // Returns a NextResponse object

// Ensure your own middleware does not handle the `/auth` routes, auto-mounted and handled by the SDK
if (request.nextUrl.pathname.startsWith("/auth")) {
return authRes;
}

// Allow access to public routes without requiring a session
if (request.nextUrl.pathname === ("/")) {
return authRes;
}

// Any route that gets to this point will be considered a protected route, and require the user to be logged-in to be able to access it
const { origin } = new URL(request.url)
const session = await auth0.getSession()

// If the user does not have a session, redirect to login
if (!session) {
return NextResponse.redirect(`${origin}/auth/login`)
}

// If a valid session exists, continue with the response from Auth0 middleware
// You can also add custom logic here...
return authRes
}

const { origin } = new URL(request.url)
const session = await auth0.getSession()

// user does not have a session — redirect to login
if (!session) {
return NextResponse.redirect(`${origin}/auth/login`)
}

return authRes
}
```

> [!NOTE]
> We recommend keeping the security checks as close as possible to the data source you're accessing. This is also in-line with [the recommendations from the Next.js team](https://nextjs.org/docs/app/building-your-application/authentication#optimistic-checks-with-middleware-optional).

## `<UserProvider />`

### Combining with other middleware

For scenarios where you need to combine the Auth0 middleware with other Next.js middleware, please refer to the [Combining middleware](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#combining-middleware) guide for examples and best practices.

## Migrating `<UserProvider />` to `<Auth0Provider />`

The `<UserProvider />` has been renamed to `<Auth0Provider />`.

Previously, when setting up your application to use v3 of the SDK, it was required to wrap your layout in the `<UserProvider />`. **This is no longer required by default.**

If you would like to pass an initial user during server rendering to be available to the `useUser()` hook, you can wrap your components with the new `<Auth0Provider />` ([see example](https://github.com/auth0/nextjs-auth0/tree/main?tab=readme-ov-file#auth0provider-)).
If you would like to pass an initial user during server rendering to be available to the `useUser()` hook, you can wrap your components with the new `<Auth0Provider />` ([see example](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#auth0provider-)).

## Rolling sessions

In v4, rolling sessions are enabled by default and are handled automatically by the middleware with no additional configuration required.

See the [session configuration section](https://github.com/auth0/nextjs-auth0/tree/main?tab=readme-ov-file#session-configuration) for additional details on how to configure it.
See the [session configuration section](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#session-configuration) for additional details on how to configure it.

## `withPageAuthRequired` and `withApiAuthRequired`
## Migrating from `withPageAuthRequired` and `withApiAuthRequired`

`withPageAuthRequired` and `withApiAuthRequired` have been removed from v4 of the SDK. Instead, we recommend adding a `getSession()` check or relying on `useUser()` hook where you would have previously used the helpers.

On the server-side, the `getSession()` method can be used to check if the user is authenticated:

```tsx
function Page() {
const session = await getSession()
// Example for an App Router Server Component
import { redirect } from 'next/navigation'
import { auth0 } from './lib/auth0' // Adjust path if your auth0 client is elsewhere

export default async function Page() {
const session = await auth0.getSession()

if (!session) {
// the user will be redirected to authenticate and then taken to the
// /dashboard route after successfully being authenticated
// The user will be redirected to authenticate and then taken to the
// /dashboard route after successfully being authenticated.
return redirect('/auth/login?returnTo=/dashboard')
}

Expand All @@ -155,7 +174,7 @@ The `getSession()` method can be used in the App Router in Server Components, Se

In the Pages Router, the `getSession(req)` method takes a request object and can be used in `getServerSideProps`, API routes, and middleware.

Read more about [accessing the authenticated user here](https://github.com/guabu/nextjs-auth0/tree/main?tab=readme-ov-file#accessing-the-authenticated-user).
Read more about [accessing the authenticated user in various contexts (browser, server, middleware) in the Examples guide](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#accessing-the-authenticated-user).

In the browser, you can rely on the `useUser()` hook to check if the user is authenticated. For example:

Expand Down Expand Up @@ -212,7 +231,7 @@ export const auth0 = new Auth0Client({
})
```

Read more about [passing authorization parameters](https://github.com/auth0/nextjs-auth0/tree/main?tab=readme-ov-file#passing-authorization-parameters).
Read more about [passing authorization parameters](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#passing-authorization-parameters).

## ID token claims

Expand All @@ -231,11 +250,38 @@ In v4, by default, the only claims that are persisted in the `user` object of se
- `org_id`

If you'd like to customize the `user` object to include additional custom claims from the ID token, you can use the `beforeSessionSaved` hook (see [beforeSessionSaved hook](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#beforesessionsaved))
For a list of default claims included in the user object, refer to the [ID Token claims and the user object section in the Examples guide](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#id-token-claims-and-the-user-object).

## Handling Dynamic Base URLs (e.g. Vercel Preview Deployments)
When deploying to platforms like Vercel with dynamic preview URLs, it's important to set the correct appBaseUrl and redirect_uri at runtime — especially in preview environments where URLs change per deployment.
1. Set `APP_BASE_URL` dynamically in `next.config.js`:
```ts
// next.config.js
module.exports = {
env: {
APP_BASE_URL:
process.env.VERCEL_ENV === "preview"
? `https://${process.env.VERCEL_BRANCH_URL}`
: process.env.APP_BASE_URL,
},
};
```
2. Use the `APP_BASE_URL` in your Auth0 configuration:
```ts
export const auth0 = new Auth0Client({
appBaseUrl: process.env.APP_BASE_URL,
authorizationParameters: {
redirect_uri: `${process.env.APP_BASE_URL}/auth/callback`,
audience: "YOUR_API_AUDIENCE_HERE", // optional
},
});
```
3. Ensure your Auth0 application settings include the dynamic URL in the **Allowed Callback URLs** and **Allowed Logout URLs** fields. For example, `https://*.vercel.app/auth/callback`.

## Additional changes

- By default, v4 is edge-compatible and as such there is no longer a `@auth0/nextjs-auth0/edge` export.
- All cookies set by the SDK default to `SameSite=Lax`
- `touchSession` method was removed. The middleware enables rolling sessions by default and can be configured via the [session configuration](https://github.com/auth0/nextjs-auth0/tree/main?tab=readme-ov-file#session-configuration).
- `getAccessToken` can now be called in React Server Components.
- 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.
- All cookies set by the SDK default to `SameSite=Lax`. For details on how to customize cookie attributes, see the [Cookie Configuration section in the Examples guide](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#cookie-configuration).
- `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.