Skip to content

Commit 141ec7b

Browse files
committed
Add support for Edge runtime
1 parent 5f33ac4 commit 141ec7b

File tree

84 files changed

+8531
-8283
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+8531
-8283
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ jobs:
55
build:
66
docker:
77
- image: cimg/node:lts-browsers
8-
resource_class: xlarge
8+
resource_class: 2xlarge
99
steps:
1010
- checkout
1111
- restore_cache:

README.md

+8-10
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,16 @@ For other comprehensive examples, see the [EXAMPLES.md](https://github.com/auth0
238238

239239
## API Reference
240240

241-
### Server (for Node.js)
241+
### Server
242+
243+
#### For Node
242244

243245
`import * from @auth0/nextjs-auth0`
244246

247+
#### For Edge runtime
248+
249+
`import * from @auth0/nextjs-auth0/edge`
250+
245251
- [Configuration Options and Environment variables](https://auth0.github.io/nextjs-auth0/modules/config.html)
246252
- [initAuth0](https://auth0.github.io/nextjs-auth0/modules/index.html#initauth0)
247253
- [handleAuth](https://auth0.github.io/nextjs-auth0/modules/handlers_auth.html)
@@ -254,15 +260,7 @@ For other comprehensive examples, see the [EXAMPLES.md](https://github.com/auth0
254260
- [getSession](https://auth0.github.io/nextjs-auth0/modules/session_get_session.html)
255261
- [updateSession](https://auth0.github.io/nextjs-auth0/modules/session_update_session.html)
256262
- [getAccessToken](https://auth0.github.io/nextjs-auth0/modules/session_get_access_token.html)
257-
258-
### Edge (for Middleware and the Edge runtime)
259-
260-
`import * from @auth0/nextjs-auth0/edge`
261-
262-
- [Configuration Options and Environment variables](https://auth0.github.io/nextjs-auth0/modules/config.html)
263-
- [initAuth0](https://auth0.github.io/nextjs-auth0/modules/edge.html#initauth0-1)
264-
- [withMiddlewareAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_middleware_auth_required.html)
265-
- [getSession](https://auth0.github.io/nextjs-auth0/modules/edge.html#getsession-1)
263+
- [withMiddlewareAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_middleware_auth_required.html) (Edge only)
266264

267265
### Client (for the Browser)
268266

cypress/e2e/smoke.cy.ts

+38
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,42 @@ describe('smoke tests', () => {
120120
cy.get('[data-testid=login]').should('exist');
121121
});
122122
});
123+
describe('app router (edge)', () => {
124+
it('should render an app route', () => {
125+
cy.visit('/edge-profile');
126+
login();
127+
cy.url().should('eq', `${Cypress.config().baseUrl}/edge-profile`);
128+
cy.get('[data-testid=profile]').contains(EMAIL);
129+
cy.get('[data-testid=logout-edge]').click();
130+
});
131+
132+
it('should protect an api', () => {
133+
cy.request({ url: '/api/edge-profile', failOnStatusCode: false }).as('unauthorized-edge');
134+
135+
cy.get('@unauthorized-edge').should((response: any) => {
136+
expect(response.status).to.eq(401);
137+
expect(response.body.error).to.eq('not_authenticated');
138+
});
139+
});
140+
141+
it('should access an api', () => {
142+
cy.visit('/edge-profile-api');
143+
login();
144+
145+
cy.url().should('eq', `${Cypress.config().baseUrl}/edge-profile-api`);
146+
cy.get('[data-testid=profile-api]').contains(EMAIL);
147+
});
148+
149+
it('should logout and return to the index page', () => {
150+
cy.visit('/edge-profile');
151+
login();
152+
cy.url().should('eq', `${Cypress.config().baseUrl}/edge-profile`);
153+
cy.get('[data-testid=logout-edge]').click();
154+
if (!useAuth0) {
155+
cy.get('[name=logout]').click();
156+
}
157+
cy.url().should('eq', `${Cypress.config().baseUrl}/`);
158+
cy.get('[data-testid=login-edge]').should('exist');
159+
});
160+
});
123161
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { handleAuth, handleLogin, handleCallback } from '@auth0/nextjs-auth0/edge';
2+
3+
const redirectUri = `${process.env.AUTH0_BASE_URL}/api/edge-auth/callback`;
4+
5+
export const GET = handleAuth({
6+
login: handleLogin({
7+
authorizationParams: { redirect_uri: redirectUri }
8+
}),
9+
callback: handleCallback({ redirectUri }),
10+
onError(req: Request, error: Error) {
11+
console.error(error);
12+
}
13+
});
14+
15+
export const runtime = 'edge';
16+
//https://github.com/vercel/next.js/issues/51642
17+
export const fetchCache = 'force-no-store';
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { getSession, withApiAuthRequired } from '@auth0/nextjs-auth0/edge';
2+
import { NextResponse } from 'next/server';
3+
4+
const GET = withApiAuthRequired(async () => {
5+
const session = await getSession();
6+
7+
return NextResponse.json(session?.user);
8+
});
9+
10+
export { GET };
11+
12+
export const runtime = 'edge';
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client';
2+
3+
import React, { useState, useEffect } from 'react';
4+
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client';
5+
6+
export default withPageAuthRequired(function ProfileApi() {
7+
const [user, setUser] = useState();
8+
9+
useEffect(() => {
10+
(async () => {
11+
const res = await fetch(`${window.location.origin}/api/edge-profile`);
12+
setUser(await res.json());
13+
})();
14+
}, []);
15+
16+
return (
17+
<main>
18+
<h1>Profile (fetched from API)</h1>
19+
<h3>User</h3>
20+
<pre data-testid="profile-api">{JSON.stringify(user, null, 2)}</pre>
21+
</main>
22+
);
23+
});

example-app/app/edge-profile/page.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import { getSession, withPageAuthRequired } from '@auth0/nextjs-auth0/edge';
3+
4+
export default withPageAuthRequired(
5+
async function Page() {
6+
const session = await getSession();
7+
8+
return (
9+
<main>
10+
<h1>Profile</h1>
11+
<h2>Page:</h2>
12+
<h3>Access Token</h3>
13+
<pre>{JSON.stringify({ accessToken: session?.accessToken }, null, 2)}</pre>
14+
<h3>User</h3>
15+
<pre data-testid="profile">{JSON.stringify(session?.user, null, 2)}</pre>
16+
</main>
17+
);
18+
},
19+
{ returnTo: '/edge-profile' }
20+
);
21+
22+
export const runtime = 'edge';

example-app/app/globals.css

+7-5
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@ ul {
2424
li {
2525
margin-right: 1rem;
2626
}
27-
.header.secondary li:nth-last-child(2) {
27+
.header.secondary li:nth-last-child(3) {
2828
margin-right: auto;
2929
}
3030
.header a {
3131
color: #fff;
3232
text-decoration: none;
3333
}
3434
.header.home a[href='/'],
35-
.header.page-router a[href$='page-router'],
36-
.header.profile a[href$='profile'],
37-
.header.profile-middleware a[href$='profile-middleware'],
38-
.header.profile-api a[href$='profile-api'],
35+
.header.page-router a[href$='/page-router'],
36+
.header.profile a[href$='/profile'],
37+
.header.edge-profile a[href$='/edge-profile'],
38+
.header.profile-middleware a[href$='/profile-middleware'],
39+
.header.profile-api a[href$='/profile-api'],
40+
.header.edge-profile-api a[href$='/edge-profile-api'],
3941
a.active {
4042
color: #888;
4143
}

example-app/app/nav.tsx

+34-10
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,52 @@ export default function Nav() {
4141
<a>Profile</a>
4242
</Link>
4343
</li>
44+
<li>
45+
<Link href="/edge-profile" legacyBehavior>
46+
<a>Profile (Edge)</a>
47+
</Link>
48+
</li>
4449
<li>
4550
<Link href="/profile-api" legacyBehavior>
4651
<a>Profile (API)</a>
4752
</Link>
4853
</li>
54+
<li>
55+
<Link href="/edge-profile-api" legacyBehavior>
56+
<a>Profile (API / Edge)</a>
57+
</Link>
58+
</li>
4959
<li>
5060
<Link href="/profile-middleware" legacyBehavior>
5161
<a>Profile (Middleware)</a>
5262
</Link>
5363
</li>{' '}
5464
{user ? (
55-
<li>
56-
<a href="/api/auth/logout" data-testid="logout">
57-
Logout
58-
</a>
59-
</li>
65+
<>
66+
<li>
67+
<a href="/api/auth/logout" data-testid="logout">
68+
Logout
69+
</a>
70+
</li>
71+
<li>
72+
<a href="/api/edge-auth/logout" data-testid="logout-edge">
73+
Logout (Edge)
74+
</a>
75+
</li>
76+
</>
6077
) : (
61-
<li>
62-
<a href="/api/auth/login" data-testid="login">
63-
Login
64-
</a>
65-
</li>
78+
<>
79+
<li>
80+
<a href="/api/auth/login" data-testid="login">
81+
Login
82+
</a>
83+
</li>
84+
<li>
85+
<a href="/api/edge-auth/login" data-testid="login-edge">
86+
Login (Edge)
87+
</a>
88+
</li>
89+
</>
6690
)}
6791
</ul>
6892
</nav>

example-app/app/profile-api/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default withPageAuthRequired(function ProfileApi() {
88

99
useEffect(() => {
1010
(async () => {
11-
const res = await fetch(`${window.location.origin}/api/profile`);
11+
const res = await fetch(`${window.location.origin}/api/edge-profile`);
1212
setUser(await res.json());
1313
})();
1414
}, []);

example-app/middleware.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { initAuth0 } from '@auth0/nextjs-auth0/edge';
1+
import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge';
22

3-
const auth0 = initAuth0({ routes: { login: '/api/page-router-auth/login' } });
4-
5-
export default auth0.withMiddlewareAuthRequired();
3+
export default withMiddlewareAuthRequired();
64

75
export const config = {
86
matcher: ['/page-router/profile-middleware', '/profile-middleware']
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,11 @@
1-
import { NextApiRequest, NextApiResponse } from 'next';
2-
import { Session, LoginOptions } from '@auth0/nextjs-auth0';
3-
import { pageRouterAuth } from '../../../lib/auth0';
1+
import { pageRouterAuth } from '@/lib/auth0';
2+
3+
const redirectUri = `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback`;
44

55
export default pageRouterAuth.handleAuth({
66
login: pageRouterAuth.handleLogin({
7-
authorizationParams: { redirect_uri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback` },
8-
getLoginState(req: NextApiRequest, options: LoginOptions) {
9-
return {
10-
returnTo: options.returnTo,
11-
foo: 'bar'
12-
};
13-
}
14-
}),
15-
callback: pageRouterAuth.handleCallback({
16-
redirectUri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback`,
17-
afterCallback(_req: NextApiRequest, _res: NextApiResponse, session: Session) {
18-
return { ...session, foo: 'bar' };
19-
}
20-
}),
21-
me: pageRouterAuth.handleProfile({
22-
refetch: true,
23-
afterRefetch(req: NextApiRequest, res: NextApiResponse, session: Session) {
24-
return { ...session, foo: 'bar' };
25-
}
7+
authorizationParams: { redirect_uri: redirectUri }
268
}),
9+
callback: pageRouterAuth.handleCallback({ redirectUri }),
2710
logout: pageRouterAuth.handleLogout({ returnTo: `${process.env.AUTH0_BASE_URL}/page-router` })
2811
});

example-app/server.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ const port = +(process.env.PORT || 3000);
66
const app = next({ dev: true, hostname: 'localhost', port });
77
const handle = app.getRequestHandler();
88

9-
process.env.AUTH0_ISSUER_BASE_URL = `http://localhost:${port}/oidc`;
9+
process.env.AUTH0_ISSUER_BASE_URL = `http://localhost:${port}/oidc/`;
1010
process.env.AUTH0_CLIENT_ID = 'testing';
1111
process.env.AUTH0_CLIENT_SECRET = 'testing';
12+
process.env.AUTH0_SCOPE = 'openid profile email offline_access';
1213

1314
app
1415
.prepare()

jest-base.config.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** @type {import('jest').Config} */
2+
module.exports = {
3+
rootDir: '.',
4+
moduleFileExtensions: ['ts', 'tsx', 'js'],
5+
preset: 'ts-jest/presets/js-with-ts',
6+
globalSetup: './tests/global-setup.ts',
7+
setupFilesAfterEnv: ['./tests/setup.ts'],
8+
transformIgnorePatterns: ['/node_modules/(?!oauth4webapi)']
9+
};

jest-edge.config.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const base = require('./jest-base.config');
2+
3+
/** @type {import('jest').Config} */
4+
module.exports = {
5+
...base,
6+
displayName: 'edge',
7+
testEnvironment: '@edge-runtime/jest-environment',
8+
testMatch: [
9+
'**/tests/handlers/login.test.ts',
10+
'**/tests/handlers/logout.test.ts',
11+
'**/tests/handlers/callback.test.ts',
12+
'**/tests/handlers/profile.test.ts',
13+
'**/tests/http/auth0-next-request.test.ts',
14+
'**/tests/http/auth0-next-response.test.ts',
15+
'**/tests/helpers/with-middleware-auth-required.test.ts',
16+
'**/tests/session/get-access-token.test.ts'
17+
]
18+
};

jest-node.config.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const base = require('./jest-base.config');
2+
3+
/** @type {import('jest').Config} */
4+
module.exports = {
5+
...base,
6+
displayName: 'node',
7+
testEnvironment: 'jest-environment-node-single-context'
8+
};

0 commit comments

Comments
 (0)