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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
14 changes: 7 additions & 7 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 @@ -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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
98 changes: 72 additions & 26 deletions V4_MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 @@ -71,7 +71,7 @@ import type { NextRequest } from "next/server"
import { auth0 } from "./lib/auth0"

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. Its primary function is to pass incoming requests to the Auth0 SDK's request handler, which in turn manages the [default auto-mounted authentication routes](../README.md#routes), user sessions, and the overall authentication flow.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> The above middleware is a basic setup. Its primary function is to pass incoming requests to the Auth0 SDK's request handler, which in turn manages the [default auto-mounted authentication routes](../README.md#routes), user sessions, and the overall authentication flow.
> 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](../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](#protecting-routes).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reading primary function, I wondered what the secondary function is of the above middleware. Maybe it makes sense to rephrase? But feel free to dismiss if u disagree.


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,30 +96,43 @@ 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)
export async function middleware(request) {
const authRes = await auth0.middleware(request); // Returns a NextResponse object

// authentication routes — let the middleware handle it
if (request.nextUrl.pathname.startsWith("/auth")) {
return authRes
}
// Ensure our own middleware does not handle the `/auth` routes, auto-mounted and handled by the SDK
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Ensure our own middleware does not handle the `/auth` routes, auto-mounted and handled by the SDK
// Ensure your own middleware does not handle the `/auth` routes, auto-mounted and handled by the SDK

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I felt like our read like the Auth0 middleware, but we are actually referring to the user's middleware.

if (request.nextUrl.pathname.startsWith("/auth")) {
return authRes;
}

const { origin } = new URL(request.url)
const session = await auth0.getSession()
// Allow access to public routes without requiring a session
if (request.nextUrl.pathname === ("/")) {
return authRes;
}

// user does not have a session — redirect to login
if (!session) {
return NextResponse.redirect(`${origin}/auth/login`)
}
const { origin } = new URL(request.url)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const { origin } = new URL(request.url)
// 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`)
}

return authRes
// If a valid session exists, continue with the response from Auth0 middleware
// You can also add custom logic here...
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 />`
For more examples on accessing user sessions in middleware, see [Accessing the authenticated user in Middleware in the Examples guide](./EXAMPLES.md#middleware).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I look at the link, it looks like this is the exact same code as just above this line. Is this expected? Feels like we aren't realy precented with more examples.


### 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](../EXAMPLES.md#combining-middleware) guide for examples and best practices.

## Changes to `<UserProvider />`
Copy link
Member

@frederikprijck frederikprijck May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Changes to `<UserProvider />`
## Migrating `<UserProvider />` to `<Auth0Provider />`


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

Expand All @@ -131,19 +146,23 @@ In v4, rolling sessions are enabled by default and are handled automatically by

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.

## `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](./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 @@ -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](./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](./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](./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](./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.