Skip to content
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

4.0.0-beta.14 #1858

Merged
merged 3 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 228 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export default function Profile() {

### On the server (App Router)

On the server, the `getSession()` helper can be used in Server Components, Server Routes, Server Actions, and middleware to get the session of the currently authenticated user and to protect resources, like so:
On the server, the `getSession()` helper can be used in Server Components, Server Routes, and Server Actions to get the session of the currently authenticated user and to protect resources, like so:

```tsx
import { auth0 } from "@/lib/auth0"
Expand All @@ -215,7 +215,7 @@ export default async function Home() {

### On the server (Pages Router)

On the server, the `getSession(req)` helper can be used in `getServerSideProps`, API routes, and middleware to get the session of the currently authenticated user and to protect resources, like so:
On the server, the `getSession(req)` helper can be used in `getServerSideProps` and API routes to get the session of the currently authenticated user and to protect resources, like so:

```tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from "next"
Expand Down Expand Up @@ -249,16 +249,47 @@ export default function Page({
}
```

### Middleware

In middleware, the `getSession(req)` helper can be used to get the session of the currently authenticated user and to protect resources, like so:

```ts
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"

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

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

const session = await auth0.getSession(request)

if (!session) {
// user is not authenticated, redirect to login page
return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin))
}

// the headers from the auth middleware should always be returned
return authRes
}
```

> [!IMPORTANT]
> The `request` object must be passed as a parameter to the `getSession(request)` method when called from a middleware to ensure that any updates to the session can be read within the same request.

## Updating the session

The `updateSession` method could be used to update the session of the currently authenticated user in both the App Router and Pages Router. If the user does not have a session, an error will be thrown.
The `updateSession` method could be used to update the session of the currently authenticated user in the App Router, Pages Router, and middleware. If the user does not have a session, an error will be thrown.

> [!NOTE]
> Any updates to the session will be overwritten when the user re-authenticates and obtains a new session.

### On the server (App Router)

On the server, the `updateSession()` helper can be used in Server Routes, Server Actions, and middleware to update the session of the currently authenticated user, like so:
On the server, the `updateSession()` helper can be used in Server Routes and Server Actions to update the session of the currently authenticated user, like so:

```tsx
import { NextResponse } from "next/server"
Expand Down Expand Up @@ -286,7 +317,7 @@ export async function GET() {

### On the server (Pages Router)

On the server, the `updateSession(req, res, session)` helper can be used in `getServerSideProps`, API routes, and middleware to update the session of the currently authenticated user, like so:
On the server, the `updateSession(req, res, session)` helper can be used in `getServerSideProps` and API routes to update the session of the currently authenticated user, like so:

```tsx
import type { NextApiRequest, NextApiResponse } from "next"
Expand Down Expand Up @@ -318,6 +349,93 @@ export default async function handler(
}
```

### Middleware

In middleware, the `updateSession(req, res, session)` helper can be used to update the session of the currently authenticated user, like so:

```ts
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"

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

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

const session = await auth0.getSession(request)

if (!session) {
// user is not authenticated, redirect to login page
return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin))
}

await auth0.updateSession(request, authRes, {
...session,
user: {
...session.user,
// add custom user data
updatedAt: Date.now(),
},
})

// the headers from the auth middleware should always be returned
return authRes
}
```

> [!IMPORTANT]
> The `request` and `response` objects must be passed as a parameters to the `updateSession(request, response, session)` method when called from a middleware to ensure that any updates to the session can be read within the same request.

If you are using the Pages Router and need to read updates to the session made in the middleware within the same request, you will need to ensure that any updates to the session are propagated on the request object, like so:

```ts
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"

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

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

const session = await auth0.getSession(request)

if (!session) {
// user is not authenticated, redirect to login page
return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin))
}

await auth0.updateSession(request, authRes, {
...session,
user: {
...session.user,
// add custom user data
updatedAt: Date.now(),
},
})

// create a new response with the updated request headers
const resWithCombinedHeaders = NextResponse.next({
request: {
headers: request.headers,
},
})

// set the response headers (set-cookie) from the auth response
authRes.headers.forEach((value, key) => {
resWithCombinedHeaders.headers.set(key, value)
})

// the headers from the auth middleware should always be returned
return resWithCombinedHeaders
}
```

## Getting an access token

The `getAccessToken()` helper can be used both in the browser and on the server to obtain the access token to call external APIs. If the access token has expired and a refresh token is available, it will automatically be refreshed and persisted.
Expand Down Expand Up @@ -351,14 +469,16 @@ export default function Component() {

### On the server (App Router)

On the server, the `getAccessToken()` helper can be used in Server Routes, Server Actions, Server Components, and middleware to get an access token to call external APIs.
On the server, the `getAccessToken()` helper can be used in Server Routes, Server Actions and Server Components to get an access token to call external APIs.

> [!IMPORTANT]
> Server Components cannot set cookies. Calling `getAccessToken()` in a Server Component will cause the access token to be refreshed, if it is expired, and the updated token set will not to be persisted.
>
> It is recommended to call `getAccessToken(req, res)` in the middleware if you need to use the refresh token in a Server Component as this will ensure the token is refreshed and correctly persisted.

For example:

```tsx
```ts
import { NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"
Expand All @@ -379,9 +499,9 @@ export async function GET() {

### On the server (Pages Router)

On the server, the `getAccessToken(req, res)` helper can be used in `getServerSideProps`, API routes, and middleware to get an access token to call external APIs, like so:
On the server, the `getAccessToken(req, res)` helper can be used in `getServerSideProps` and API routes to get an access token to call external APIs, like so:

```tsx
```ts
import type { NextApiRequest, NextApiResponse } from "next"

import { auth0 } from "@/lib/auth0"
Expand All @@ -401,6 +521,79 @@ export default async function handler(
}
```

### Middleware

In middleware, the `getAccessToken(req, res)` helper can be used to get an access token to call external APIs, like so:

```tsx
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"

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

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

const session = await auth0.getSession(request)

if (!session) {
// user is not authenticated, redirect to login page
return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin))
}

const accessToken = await auth0.getAccessToken(request, authRes)

// the headers from the auth middleware should always be returned
return authRes
}
```

> [!IMPORTANT]
> The `request` and `response` objects must be passed as a parameters to the `getAccessToken(request, response)` method when called from a middleware to ensure that the refreshed access token can be accessed within the same request.

If you are using the Pages Router and are calling the `getAccessToken` method in both the middleware and an API Route or `getServerSideProps`, it's recommended to propagate the headers from the middleware, as shown below. This will ensure that calling `getAccessToken` in the API Route or `getServerSideProps` will not result in the access token being refreshed again.

```ts
import { NextRequest, NextResponse } from "next/server"

import { auth0 } from "@/lib/auth0"

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

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

const session = await auth0.getSession(request)

if (!session) {
// user is not authenticated, redirect to login page
return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin))
}

const accessToken = await auth0.getAccessToken(request, authRes)

// create a new response with the updated request headers
const resWithCombinedHeaders = NextResponse.next({
request: {
headers: request.headers,
},
})

// set the response headers (set-cookie) from the auth response
authRes.headers.forEach((value, key) => {
resWithCombinedHeaders.headers.set(key, value)
})

// the headers from the auth middleware should always be returned
return resWithCombinedHeaders
}
```

## `<Auth0Provider />`

### Passing an initial user from the server
Expand Down Expand Up @@ -628,3 +821,29 @@ NEXT_PUBLIC_ACCESS_TOKEN_ROUTE=/api/auth/token

> [!IMPORTANT]
> Updating the route paths will also require updating the **Allowed Callback URLs** and **Allowed Logout URLs** configured in the [Auth0 Dashboard](https://manage.auth0.com) for your client.

## Testing helpers

### `generateSessionCookie`

The `generateSessionCookie` helper can be used to generate a session cookie value for use during tests:

```ts
import { generateSessionCookie } from "@auth0/nextjs-auth0/testing"

const sessionCookieValue = await generateSessionCookie(
{
user: {
sub: "user_123",
},
tokenSet: {
accessToken: "at_123",
refreshToken: "rt_123",
expiresAt: 123456789,
},
},
{
secret: process.env.AUTH0_SECRET!,
}
)
```
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@auth0/nextjs-auth0",
"version": "4.0.0-beta.13",
"version": "4.0.0-beta.14",
"description": "Auth0 Next.js SDK",
"scripts": {
"build": "tsc",
Expand Down Expand Up @@ -57,6 +57,9 @@
},
"./types": {
"import": "./dist/types/index.d.ts"
},
"./testing": {
"import": "./dist/testing/index.js"
}
},
"dependencies": {
Expand All @@ -71,6 +74,9 @@
},
"typesVersions": {
"*": {
"testing": [
"./dist/testing/index.d.ts"
],
"types": [
"./dist/types/index.d.ts"
],
Expand Down
7 changes: 2 additions & 5 deletions src/server/auth-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@ import {
OAuth2Error,
SdkError,
} from "../errors"
import { SessionData, TokenSet } from "../types"
import {
AbstractSessionStore,
LogoutToken,
} from "./session/abstract-session-store"
import { LogoutToken, SessionData, TokenSet } from "../types"
import { AbstractSessionStore } from "./session/abstract-session-store"
import { TransactionState, TransactionStore } from "./transaction-store"
import { filterClaims } from "./user"

Expand Down
Loading