Skip to content

Commit

Permalink
4.0.0-beta.11 (#1845)
Browse files Browse the repository at this point in the history
  • Loading branch information
guabu authored Dec 17, 2024
1 parent fed3bf4 commit 1e482a4
Show file tree
Hide file tree
Showing 15 changed files with 565 additions and 50 deletions.
106 changes: 89 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
### 1. Install the SDK

```shell
npm i @auth0/[email protected].10
npm i @auth0/[email protected].11
```

### 2. Add the environment variables
Expand Down Expand Up @@ -109,21 +109,24 @@ export default async function Home() {

You can customize the client by using the options below:

| Option | Type | Description |
| --------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| domain | `string` | The Auth0 domain for the tenant (e.g.: `example.us.auth0.com`). If it's not specified, it will be loaded from the `AUTH0_DOMAIN` environment variable. |
| clientId | `string` | The Auth0 client ID. If it's not specified, it will be loaded from the `AUTH0_CLIENT_ID` environment variable. |
| clientSecret | `string` | The Auth0 client secret. If it's not specified, it will be loaded from the `AUTH0_CLIENT_SECRET` environment variable. |
| authorizationParameters | `AuthorizationParameters` | The authorization parameters to pass to the `/authorize` endpoint. See [Passing authorization parameters](#passing-authorization-parameters) for more details. |
| appBaseUrl | `string` | The URL of your application (e.g.: `http://localhost:3000`). If it's not specified, it will be loaded from the `APP_BASE_URL` environment variable. |
| secret | `string` | A 32-byte, hex-encoded secret used for encrypting cookies. If it's not specified, it will be loaded from the `AUTH0_SECRET` environment variable. |
| signInReturnToPath | `string` | The path to redirect the user to after successfully authenticating. Defaults to `/`. |
| session | `SessionConfiguration` | Configure the session timeouts and whether to use rolling sessions or not. See [Session configuration](#session-configuration) for additional details. |
| beforeSessionSaved | `BeforeSessionSavedHook` | A method to manipulate the session before persisting it. See [beforeSessionSaved](#beforesessionsaved) for additional details. |
| onCallback | `OnCallbackHook` | A method to handle errors or manage redirects after attempting to authenticate. See [onCallback](#oncallback) for additional details. |
| sessionStore | `SessionStore` | A custom session store implementation used to persist sessions to a data store. See [Database sessions](#database-sessions) for additional details. |
| pushedAuthorizationRequests | `boolean` | Configure the SDK to use the Pushed Authorization Requests (PAR) protocol when communicating with the authorization server. |
| routes | `Routes` | Configure the paths for the authentication routes. See [Custom routes](#custom-routes) for additional details. |
| Option | Type | Description |
| --------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| domain | `string` | The Auth0 domain for the tenant (e.g.: `example.us.auth0.com` or `https://example.us.auth0.com`). If it's not specified, it will be loaded from the `AUTH0_DOMAIN` environment variable. |
| clientId | `string` | The Auth0 client ID. If it's not specified, it will be loaded from the `AUTH0_CLIENT_ID` environment variable. |
| clientSecret | `string` | The Auth0 client secret. If it's not specified, it will be loaded from the `AUTH0_CLIENT_SECRET` environment variable. |
| authorizationParameters | `AuthorizationParameters` | The authorization parameters to pass to the `/authorize` endpoint. See [Passing authorization parameters](#passing-authorization-parameters) for more details. |
| clientAssertionSigningKey | `string` or `CryptoKey` | Private key for use with `private_key_jwt` clients. |
| clientAssertionSigningAlg | `string` | The algorithm used to sign the client assertion JWT. |
| appBaseUrl | `string` | The URL of your application (e.g.: `http://localhost:3000`). If it's not specified, it will be loaded from the `APP_BASE_URL` environment variable. |
| secret | `string` | A 32-byte, hex-encoded secret used for encrypting cookies. If it's not specified, it will be loaded from the `AUTH0_SECRET` environment variable. |
| signInReturnToPath | `string` | The path to redirect the user to after successfully authenticating. Defaults to `/`. |
| session | `SessionConfiguration` | Configure the session timeouts and whether to use rolling sessions or not. See [Session configuration](#session-configuration) for additional details. |
| beforeSessionSaved | `BeforeSessionSavedHook` | A method to manipulate the session before persisting it. See [beforeSessionSaved](#beforesessionsaved) for additional details. |
| onCallback | `OnCallbackHook` | A method to handle errors or manage redirects after attempting to authenticate. See [onCallback](#oncallback) for additional details. |
| sessionStore | `SessionStore` | A custom session store implementation used to persist sessions to a data store. See [Database sessions](#database-sessions) for additional details. |
| pushedAuthorizationRequests | `boolean` | Configure the SDK to use the Pushed Authorization Requests (PAR) protocol when communicating with the authorization server. |
| routes | `Routes` | Configure the paths for the authentication routes. See [Custom routes](#custom-routes) for additional details. |
| allowInsecureRequests | `boolean` | Allow insecure requests to be made to the authorization server. This can be useful when testing with a mock OIDC provider that does not support TLS, locally. This option can only be used when `NODE_ENV` is not set to `production`. |

## Passing authorization parameters

Expand Down Expand Up @@ -246,6 +249,75 @@ export default function Page({
}
```

## 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.

> [!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:

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

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

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

if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}

await auth0.updateSession({
...session,
updatedAt: Date.now(),
})

return NextResponse.json(null, { status: 200 })
}
```

> [!NOTE]
> The `updateSession()` method is not usable in Server Components as it is not possible to write cookies.
### 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:

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

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

type ResponseData =
| {}
| {
error: string
}

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
const session = await auth0.getSession(req)

if (!session) {
return res.status(401).json({ error: "Unauthorized" })
}

await auth0.updateSession(req, res, {
...session,
updatedAt: Date.now(),
})

res.status(200).json({})
}
```

## 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 All @@ -262,7 +334,7 @@ import { getAccessToken } from "@auth0/nextjs-auth0"
export default function Component() {
async function fetchData() {
try {
const token = await auth0.getAccessToken()
const token = await getAccessToken()
// call external API with token...
} catch (err) {
// err will be an instance of AccessTokenError if an access token could not be obtained
Expand Down
4 changes: 3 additions & 1 deletion V4_MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Of the required variables, the following have changed from v3:

All other configuration must be specified via the `Auth0Client` constructor.

> [!NOTE]
> In v3 the `audience` parameter could be specified via the `AUTH0_AUDIENCE` environment variable. In v4, the `audience` parameter must be specified as a query parameter or via the `authorizationParamaters` configuration option. For more information on how to pass custom parameters in v4, please see [Passing custom authorization parameters](#passing-custom-authorization-parameters).
## Routes

Previously, it was required to set up a dynamic Route Handler to mount the authentication endpoints to handle requests.
Expand Down Expand Up @@ -237,5 +240,4 @@ If you'd like to customize the `user` object to include additional custom claims
- To store large session data, please use a [custom data store](https://github.com/auth0/nextjs-auth0/tree/v4?tab=readme-ov-file#database-sessions) with a SessionStore implementation
- 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/v4?tab=readme-ov-file#session-configuration).
- `updateSession` method was removed.
- `getAccessToken` can now be called in React Server Components.
32 changes: 31 additions & 1 deletion e2e/app-router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ test("getAccessToken()", async ({ page }) => {
expect(tokenRequest).toHaveProperty("expires_at")
})

test("protected server route", async ({ page, request, context }) => {
test("protected server route", async ({ page, context }) => {
// before establishing a session, we should receive a 401
const unauthedRes = await context.request.fetch("/app-router/api/test")
expect(unauthedRes.status()).toBe(401)
Expand Down Expand Up @@ -101,3 +101,33 @@ test("protected server action", async ({ page }) => {
await page.getByText("Call server action").click()
await expect(page.locator("#status")).toHaveValue("authenticated")
})

test("updateSession()", async ({ page, context }) => {
const now = Date.now()

await page.goto("/auth/login?returnTo=/app-router/server")

// fill out Auth0 form
await page.fill('input[id="username"]', "[email protected]")
await page.fill('input[id="password"]', process.env.TEST_USER_PASSWORD!)
await page.getByText("Continue", { exact: true }).click()

// check that the page says "Welcome, [email protected]!"
expect(await page.getByRole("heading", { level: 1 }).textContent()).toBe(
"Welcome, [email protected]!"
)

// the session should not have an `updatedAt` field initially
let getSessionRes = await context.request.fetch("/app-router/api/get-session")
let getSessionJson = await getSessionRes.json()
expect(getSessionJson.updatedAt).toBeUndefined()

// now update the session and check that the `updatedAt` field is updated
const updateSessionRes = await context.request.fetch(
"/app-router/api/update-session"
)
expect(updateSessionRes.status()).toBe(200)
getSessionRes = await context.request.fetch("/app-router/api/get-session")
getSessionJson = await getSessionRes.json()
expect(getSessionJson.updatedAt).toBeGreaterThan(now)
})
32 changes: 32 additions & 0 deletions e2e/pages-router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,35 @@ test("protected API route", async ({ page, request, context }) => {
expect(authedRes.status()).toBe(200)
expect(await authedRes.json()).toEqual({ email: "[email protected]" })
})

test("updateSession()", async ({ page, context }) => {
const now = Date.now()

await page.goto("/auth/login?returnTo=/pages-router/server")

// fill out Auth0 form
await page.fill('input[id="username"]', "[email protected]")
await page.fill('input[id="password"]', process.env.TEST_USER_PASSWORD!)
await page.getByText("Continue", { exact: true }).click()

// check that the page says "Welcome, [email protected]!"
expect(await page.getByRole("heading", { level: 1 }).textContent()).toBe(
"Welcome, [email protected]!"
)

// the session should not have an `updatedAt` field initially
let getSessionRes = await context.request.fetch(
"/api/pages-router/get-session"
)
let getSessionJson = await getSessionRes.json()
expect(getSessionJson.updatedAt).toBeUndefined()

// now update the session and check that the `updatedAt` field is updated
const updateSessionRes = await context.request.fetch(
"/api/pages-router/update-session"
)
expect(updateSessionRes.status()).toBe(200)
getSessionRes = await context.request.fetch("/api/pages-router/get-session")
getSessionJson = await getSessionRes.json()
expect(getSessionJson.updatedAt).toBeGreaterThan(now)
})
13 changes: 13 additions & 0 deletions e2e/test-app/app/app-router/api/get-session/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { NextResponse } from "next/server"

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

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

if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}

return NextResponse.json(session, { status: 200 })
}
18 changes: 18 additions & 0 deletions e2e/test-app/app/app-router/api/update-session/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextResponse } from "next/server"

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

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

if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
}

await auth0.updateSession({
...session,
updatedAt: Date.now(),
})

return NextResponse.json(null, { status: 200 })
}
1 change: 1 addition & 0 deletions e2e/test-app/app/app-router/server/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default async function Home() {
return (
<main>
<h1>Welcome, {session.user.email}!</h1>
<p id="updated-at">{session.user.updatedAt}</p>
</main>
)
}
19 changes: 19 additions & 0 deletions e2e/test-app/pages/api/pages-router/get-session/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { NextApiRequest, NextApiResponse } from "next"
import { SessionData } from "@auth0/nextjs-auth0/types"

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

type ResponseData = SessionData | { error: string }

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
const session = await auth0.getSession(req)

if (!session) {
return res.status(401).json({ error: "Unauthorized" })
}

res.status(200).json(session)
}
27 changes: 27 additions & 0 deletions e2e/test-app/pages/api/pages-router/update-session/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { NextApiRequest, NextApiResponse } from "next"

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

type ResponseData =
| {}
| {
error: string
}

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
const session = await auth0.getSession(req)

if (!session) {
return res.status(401).json({ error: "Unauthorized" })
}

await auth0.updateSession(req, res, {
...session,
updatedAt: Date.now(),
})

res.status(200).json({})
}
Loading

0 comments on commit 1e482a4

Please sign in to comment.