Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
e9df6f0
feat: add my-account and my-org proxy
frederikprijck Oct 23, 2025
e8f0a10
add comment for search params
frederikprijck Oct 24, 2025
0a4d50b
Use fetcher for proxy
frederikprijck Oct 25, 2025
0a4aa37
Update proxy implementation
frederikprijck Oct 27, 2025
0ed472c
Add test for POST
frederikprijck Oct 27, 2025
748836e
Update comment
frederikprijck Oct 27, 2025
4f179a3
chore: add local dev files to gitignore
tusharpandey13 Oct 27, 2025
a262c5b
Merge branch 'main' of https://github.com/auth0/nextjs-auth0 into fea…
tusharpandey13 Oct 27, 2025
c5d4680
chore: add local dev files to gitignore
tusharpandey13 Oct 27, 2025
b625525
Merge remote-tracking branch 'origin/chore/proxy' into feat/proxy-dpop
tusharpandey13 Oct 27, 2025
48ad253
debug: /me/ working, /org/ fails with dpop
tusharpandey13 Oct 30, 2025
1f65c29
feat: add my-account and my-org proxy
frederikprijck Oct 23, 2025
758cd25
add comment for search params
frederikprijck Oct 24, 2025
3655c4b
Use fetcher for proxy
frederikprijck Oct 25, 2025
c7b3db0
Update proxy implementation
frederikprijck Oct 27, 2025
d0c7414
Add test for POST
frederikprijck Oct 27, 2025
1c1f970
Update comment
frederikprijck Oct 27, 2025
3373af9
fix: use correct audience for my-account
frederikprijck Oct 30, 2025
43c086c
chore: pass scope to handleProxy
frederikprijck Oct 30, 2025
3b62aa8
chore: fix linter
frederikprijck Oct 30, 2025
1a4b79c
chore: fix linter
frederikprijck Oct 30, 2025
5e23701
fix: update cache in proxy handler
frederikprijck Oct 30, 2025
a970d6d
chore: move proxy tests to seperate file
frederikprijck Oct 30, 2025
b610c64
chore: revert changes
frederikprijck Oct 30, 2025
2285a9f
chore: simplify default test data
frederikprijck Oct 30, 2025
72a38c5
feat: add error handling and unit tests
frederikprijck Oct 30, 2025
a17bf17
fix: incorrect merge
frederikprijck Oct 30, 2025
09459db
chore: fix linter
frederikprijck Oct 30, 2025
13da0b2
chore: fix linter
frederikprijck Oct 30, 2025
b36eb86
chore: migrate tests to use msw
frederikprijck Oct 31, 2025
4251af6
chore: add additional tests
frederikprijck Oct 31, 2025
04113eb
chore: fix linter
frederikprijck Oct 31, 2025
af9c185
chore: add additional comment about removing authorization header
frederikprijck Oct 31, 2025
707354c
chore: update test description
frederikprijck Oct 31, 2025
b27ba54
chore: update comment about how we handle session updates
frederikprijck Oct 31, 2025
de5f8a8
chore: fix incorrect merge
frederikprijck Oct 31, 2025
1c53b69
chore: make handleProxy private
frederikprijck Oct 31, 2025
cd3a1c0
chore: use a single fetcher per audience
frederikprijck Oct 31, 2025
f9a2e3b
chore: explicitly define a list of headers to forward
frederikprijck Oct 31, 2025
c8e5ce4
chore: fix linter
frederikprijck Oct 31, 2025
e81c65c
Merge branch 'main' of https://github.com/auth0/nextjs-auth0 into fea…
tusharpandey13 Nov 1, 2025
7b8db87
Merge branch 'chore/proxy' of https://github.com/auth0/nextjs-auth0 i…
tusharpandey13 Nov 1, 2025
6af9e40
chore: update some docs
tusharpandey13 Nov 1, 2025
bd623e7
feat: fix cors, request body consumption and origin edge cases; make …
tusharpandey13 Nov 2, 2025
2f92754
chore: add exhaustive tests for handleProxy
tusharpandey13 Nov 2, 2025
7151a19
chore: reset examples/with-dpop to match main
tusharpandey13 Nov 2, 2025
f44dbb9
chore: remove files from examples/with-dpop that don't exist on main
tusharpandey13 Nov 2, 2025
42ce231
chore: update gitignore
tusharpandey13 Nov 2, 2025
302428d
fix: update path matching for proxy matcher
tusharpandey13 Nov 3, 2025
cdce2d8
chore: only clone req if dpop is on; fix redundant code
tusharpandey13 Nov 3, 2025
1e9698e
chore: lint
tusharpandey13 Nov 3, 2025
529ed74
chore: remove my-acc and my-org logic (will be added in a seperate PR)
tusharpandey13 Nov 3, 2025
3f416ef
feat: add back proxy test files
tusharpandey13 Nov 3, 2025
03e1bd6
feat: restore handleMyAccount and handleMyOrg methods and tests
tusharpandey13 Nov 3, 2025
10060ab
feat: add My Account and My Org proxy handlers with bug fixes
tusharpandey13 Nov 3, 2025
a1c483e
feat: restore comprehensive proxy handler tests using /me endpoint
tusharpandey13 Nov 3, 2025
304a536
fix: resolve proxy handler test failures for /me endpoint
tusharpandey13 Nov 3, 2025
ba940f1
chore: add docs; remove redundant header from allowlist; update tests
tusharpandey13 Nov 3, 2025
f77293d
chore: make handlePreflight fwd response status from upstream; use sc…
tusharpandey13 Nov 3, 2025
c62b8bd
fix: ensure fetcher's getAccessToken can access the tokenSetSideEffec…
frederikprijck Nov 3, 2025
d6e5719
chore: no need to bind the getAccessToken handler
frederikprijck Nov 3, 2025
047a270
chore: revert unnecessary changes to simplify diff
frederikprijck Nov 3, 2025
4a77cd4
chore: update tests
tusharpandey13 Nov 3, 2025
c4bda8c
chore: update tests; fix buggy targeturl logic
tusharpandey13 Nov 3, 2025
25b1669
chore: avoid including test files in dist directory (#2396)
frederikprijck Nov 3, 2025
bd93260
Merge branch 'main' into feat/my-account-my-org-clean
tusharpandey13 Nov 3, 2025
048550a
remove unnecessary duplex options
frederikprijck Nov 4, 2025
6d34679
Replace auth0-scope header with standard scope header
tusharpandey13 Nov 4, 2025
53871e1
Add tests for basePath handling in auth handler
tusharpandey13 Nov 4, 2025
d3ca61a
fix: remove exact equality checks for matcher for /me and /my-org
tusharpandey13 Nov 4, 2025
6f8d7be
Merge branch 'main' into feat/my-account-my-org-clean
tusharpandey13 Nov 6, 2025
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
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ web_modules/
.env.test.local
.env.production.local
.env.local
.auth0-credentials

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down Expand Up @@ -132,3 +133,12 @@ dist
/playwright-report/
/blob-report/
/playwright/.cache/

# local development files
.memory/
.dev-files/
*.env.*
*.tmp
*PLAN*.md
.yalc/
yalc.lock
352 changes: 352 additions & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@
- [Troubleshooting](#troubleshooting)
- [Common Issues](#common-issues)
- [Debug Logging](#debug-logging)
- [Proxy Handler for My Account and My Organization APIs](#proxy-handler-for-my-account-and-my-organization-apis)
- [Overview](#overview)
- [How It Works](#how-it-works)
- [My Account API Proxy](#my-account-api-proxy)
- [Configuration](#configuration)
- [Client-Side Usage](#client-side-usage)
- [`scope` Header](#scope-header)
- [My Organization API Proxy](#my-organization-api-proxy)
- [Configuration](#configuration-1)
- [Client-Side Usage](#client-side-usage-1)
- [Integration with UI Components](#integration-with-ui-components)
- [HTTP Methods](#http-methods)
- [CORS Handling](#cors-handling)
- [Error Handling](#error-handling-1)
- [Token Management](#token-management)
- [Security Considerations](#security-considerations)
- [Debugging](#debugging)
- [`<Auth0Provider />`](#auth0provider-)
- [Passing an initial user from the server](#passing-an-initial-user-from-the-server)
- [Hooks](#hooks)
Expand Down Expand Up @@ -1550,6 +1567,341 @@ const fetcher = await auth0.createFetcher(req, {
});
```

## Proxy Handler for My Account and My Organization APIs

The SDK provides built-in proxy handler support for Auth0's My Account and My Organization Management APIs. This enables browser-initiated requests to these APIs while maintaining server-side DPoP authentication and token management.

### Overview

The proxy handler implements a Backend-for-Frontend (BFF) pattern that transparently forwards client requests to Auth0 APIs through the Next.js server. This architecture ensures:

- DPoP private keys and tokens remain on the server, inaccessible to client-side JavaScript
- Automatic token retrieval and refresh based on requested audience and scope
- DPoP proof generation for each proxied request
- Session updates when tokens are refreshed
- Proper CORS handling for cross-origin requests

The proxy handler is automatically enabled when using the SDK's middleware and requires no additional configuration.

### How It Works

When a client makes a request to `/me/*` or `/my-org/*` on your Next.js application:

1. The SDK's middleware intercepts the request
2. Validates the user's session exists
3. Retrieves or refreshes the appropriate access token for the requested audience
4. Generates DPoP proof if DPoP is enabled
5. Forwards the request to the upstream Auth0 API with proper authentication headers
6. Returns the response to the client
7. Updates the session if tokens were refreshed

### My Account API Proxy

The My Account API proxy handles all requests to Auth0's My Account API at `/me/v1/*`.

#### Configuration

Enable My Account API access by configuring the audience and scopes:

```ts
import { Auth0Client } from "@auth0/nextjs-auth0/server";

export const auth0 = new Auth0Client({
useDPoP: true,
authorizationParameters: {
audience: "urn:your-api-identifier",
scope: {
[`https://${process.env.AUTH0_DOMAIN}/me/`]: "profile:read profile:write factors:manage"
}
}
});
```

#### Client-Side Usage

Make requests to the My Account API through the `/me/*` path:

```tsx
"use client";

import { useState } from "react";

export default function MyAccountProfile() {
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(false);

const fetchProfile = async () => {
setLoading(true);
try {
const response = await fetch("/me/v1/profile", {
method: "GET",
headers: {
"scope": "profile:read"
}
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

const data = await response.json();
setProfile(data);
} catch (error) {
console.error("Failed to fetch profile:", error);
} finally {
setLoading(false);
}
};

const updateProfile = async (updates) => {
Copy link
Contributor

@nandan-bhat nandan-bhat Nov 4, 2025

Choose a reason for hiding this comment

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

If the page makes a My Account request (updateProfile in this case) during page render, and that page is linked using the Next.js <Link> component, it could trigger an actual API request during prefetch in production.

I think we should either fix this or document this.

try {
const response = await fetch("/me/v1/profile", {
method: "PATCH",
headers: {
"content-type": "application/json",
"scope": "profile:write"
},
body: JSON.stringify(updates)
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

return await response.json();
} catch (error) {
console.error("Failed to update profile:", error);
throw error;
}
};

return (
<div>
<button onClick={fetchProfile} disabled={loading}>
{loading ? "Loading..." : "Load Profile"}
</button>
{profile && (
<pre>{JSON.stringify(profile, null, 2)}</pre>
)}
</div>
);
}
```

#### `scope` Header

The `scope` header specifies the scope required for the request. The SDK uses this to retrieve an access token with the appropriate scope for the My Account API audience.

Format: `"scope": "scope1 scope2 scope3"`

Common scopes for My Account API:
- `profile:read` - Read user profile information
- `profile:write` - Update user profile information
- `factors:read` - Read enrolled MFA factors
- `factors:manage` - Manage MFA factors
- `identities:read` - Read linked identities
- `identities:manage` - Link and unlink identities

### My Organization API Proxy

The My Organization API proxy handles all requests to Auth0's My Organization Management API at `/my-org/*`.

#### Configuration

Enable My Organization API access by configuring the audience and scopes:

```ts
import { Auth0Client } from "@auth0/nextjs-auth0/server";

export const auth0 = new Auth0Client({
useDPoP: true,
authorizationParameters: {
audience: "urn:your-api-identifier",
scope: {
[`https://${process.env.AUTH0_DOMAIN}/my-org/`]: "org:read org:write members:read"
}
}
});
```

#### Client-Side Usage

Make requests to the My Organization API through the `/my-org/*` path:

```tsx
"use client";

import { useState, useEffect } from "react";

export default function MyOrganization() {
const [organizations, setOrganizations] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetchOrganizations();
}, []);

const fetchOrganizations = async () => {
setLoading(true);
try {
const response = await fetch("/my-org/organizations", {
method: "GET",
headers: {
"scope": "org:read"
}
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

const data = await response.json();
setOrganizations(data.organizations || []);
} catch (error) {
console.error("Failed to fetch organizations:", error);
} finally {
setLoading(false);
}
};

const updateOrganization = async (orgId, updates) => {
try {
const response = await fetch(`/my-org/organizations/${orgId}`, {
method: "PATCH",
headers: {
"content-type": "application/json",
"scope": "org:write"
},
body: JSON.stringify(updates)
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

return await response.json();
} catch (error) {
console.error("Failed to update organization:", error);
throw error;
}
};

if (loading) return <div>Loading organizations...</div>;

return (
<div>
<h1>My Organizations</h1>
<ul>
{organizations.map((org) => (
<li key={org.id}>{org.display_name}</li>
))}
</ul>
</div>
);
}
```

Common scopes for My Organization API:
- `org:read` - Read organization information
- `org:write` - Update organization information
- `members:read` - Read organization members
- `members:manage` - Manage organization members
- `roles:read` - Read organization roles
- `roles:manage` - Manage organization roles

### Integration with UI Components

When using Auth0 UI Components with the proxy handler, configure the client to target the proxy endpoints:

```tsx
import { MyAccountClient } from "@auth0/my-account-js";

const myAccountClient = new MyAccountClient({
domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN,
baseUrl: "/me",
fetcher: (url, init, authParams) => {
return fetch(url, {
...init,
headers: {
...init?.headers,
"scope": authParams?.scope?.join(" ") || ""
}
});
}
});
```

This configuration:
- Sets `baseUrl` to `/me` to route requests through the proxy
- Passes the required scope via the `scope` header
- Ensures the SDK middleware handles authentication transparently

### HTTP Methods

The proxy handler supports all standard HTTP methods:

- `GET` - Retrieve resources
- `POST` - Create resources
- `PUT` - Replace resources
- `PATCH` - Update resources
- `DELETE` - Remove resources
- `OPTIONS` - CORS preflight requests (handled without authentication)
- `HEAD` - Retrieve headers only

### CORS Handling

The proxy handler correctly handles CORS preflight requests (OPTIONS with `access-control-request-method` header) by forwarding them to the upstream API without authentication headers, as required by RFC 7231 §4.3.1.

CORS headers from the upstream API are forwarded to the client transparently.

### Error Handling

The proxy handler returns appropriate HTTP status codes:

- `401 Unauthorized` - No active session or token refresh failed
- `4xx Client Error` - Forwarded from upstream API
- `5xx Server Error` - Forwarded from upstream API or proxy internal error

Error responses from the upstream API are forwarded to the client with their original status code, headers, and body.

### Token Management

The proxy handler automatically:

- Retrieves access tokens from the session for the requested audience
- Refreshes expired tokens using the refresh token
- Updates the session with new tokens after refresh
- Caches tokens per audience to minimize token endpoint calls
- Generates DPoP proofs for each request when DPoP is enabled

### Security Considerations

The proxy handler implements secure forwarding:

- HTTP-only session cookies are not forwarded to upstream APIs
- Authorization headers from the client are replaced with server-generated tokens
- Hop-by-hop headers are stripped per RFC 2616 §13.5.1
- Only allow-listed request headers are forwarded
- Response headers are filtered before returning to the client
- Host header is updated to match the upstream API

### Debugging

Enable debug logging to troubleshoot proxy requests:

```ts
export const auth0 = new Auth0Client({
// ... other config
enableDebugLogs: true
});
```

This will log:
- Request proxying flow
- Token retrieval and refresh operations
- DPoP proof generation
- Session updates
- Errors and warnings

## `<Auth0Provider />`

Expand Down
Loading