Skip to content

Commit

Permalink
[SDK-4319] Add support for Edge runtime (#1269)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamjmcgrath authored Jun 28, 2023
2 parents 5f33ac4 + 149ae09 commit 3adc5d0
Show file tree
Hide file tree
Showing 84 changed files with 8,571 additions and 8,284 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
build:
docker:
- image: cimg/node:lts-browsers
resource_class: xlarge
resource_class: 2xlarge
steps:
- checkout
- restore_cache:
Expand Down
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,16 @@ For other comprehensive examples, see the [EXAMPLES.md](https://github.com/auth0

## API Reference

### Server (for Node.js)
### Server

#### For Node

`import * from @auth0/nextjs-auth0`

#### For Edge runtime

`import * from @auth0/nextjs-auth0/edge`

- [Configuration Options and Environment variables](https://auth0.github.io/nextjs-auth0/modules/config.html)
- [initAuth0](https://auth0.github.io/nextjs-auth0/modules/index.html#initauth0)
- [handleAuth](https://auth0.github.io/nextjs-auth0/modules/handlers_auth.html)
Expand All @@ -254,15 +260,7 @@ For other comprehensive examples, see the [EXAMPLES.md](https://github.com/auth0
- [getSession](https://auth0.github.io/nextjs-auth0/modules/session_get_session.html)
- [updateSession](https://auth0.github.io/nextjs-auth0/modules/session_update_session.html)
- [getAccessToken](https://auth0.github.io/nextjs-auth0/modules/session_get_access_token.html)

### Edge (for Middleware and the Edge runtime)

`import * from @auth0/nextjs-auth0/edge`

- [Configuration Options and Environment variables](https://auth0.github.io/nextjs-auth0/modules/config.html)
- [initAuth0](https://auth0.github.io/nextjs-auth0/modules/edge.html#initauth0-1)
- [withMiddlewareAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_middleware_auth_required.html)
- [getSession](https://auth0.github.io/nextjs-auth0/modules/edge.html#getsession-1)
- [withMiddlewareAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_middleware_auth_required.html) (Edge only)

### Client (for the Browser)

Expand Down
38 changes: 38 additions & 0 deletions cypress/e2e/smoke.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,42 @@ describe('smoke tests', () => {
cy.get('[data-testid=login]').should('exist');
});
});
describe('app router (edge)', () => {
it('should render an app route', () => {
cy.visit('/edge-profile');
login();
cy.url().should('eq', `${Cypress.config().baseUrl}/edge-profile`);
cy.get('[data-testid=profile]').contains(EMAIL);
cy.get('[data-testid=logout-edge]').click();
});

it('should protect an api', () => {
cy.request({ url: '/api/edge-profile', failOnStatusCode: false }).as('unauthorized-edge');

cy.get('@unauthorized-edge').should((response: any) => {
expect(response.status).to.eq(401);
expect(response.body.error).to.eq('not_authenticated');
});
});

it('should access an api', () => {
cy.visit('/edge-profile-api');
login();

cy.url().should('eq', `${Cypress.config().baseUrl}/edge-profile-api`);
cy.get('[data-testid=profile-api]').contains(EMAIL);
});

it('should logout and return to the index page', () => {
cy.visit('/edge-profile');
login();
cy.url().should('eq', `${Cypress.config().baseUrl}/edge-profile`);
cy.get('[data-testid=logout-edge]').click();
if (!useAuth0) {
cy.get('[name=logout]').click();
}
cy.url().should('eq', `${Cypress.config().baseUrl}/`);
cy.get('[data-testid=login-edge]').should('exist');
});
});
});
17 changes: 17 additions & 0 deletions example-app/app/api/edge-auth/[auth0]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { handleAuth, handleLogin, handleCallback } from '@auth0/nextjs-auth0/edge';

const redirectUri = `${process.env.AUTH0_BASE_URL}/api/edge-auth/callback`;

export const GET = handleAuth({
login: handleLogin({
authorizationParams: { redirect_uri: redirectUri }
}),
callback: handleCallback({ redirectUri }),
onError(req: Request, error: Error) {
console.error(error);
}
});

export const runtime = 'edge';
//https://github.com/vercel/next.js/issues/51642
export const fetchCache = 'force-no-store';
12 changes: 12 additions & 0 deletions example-app/app/api/edge-profile/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getSession, withApiAuthRequired } from '@auth0/nextjs-auth0/edge';
import { NextResponse } from 'next/server';

const GET = withApiAuthRequired(async () => {
const session = await getSession();

return NextResponse.json(session?.user);
});

export { GET };

export const runtime = 'edge';
23 changes: 23 additions & 0 deletions example-app/app/edge-profile-api/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

import React, { useState, useEffect } from 'react';
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client';

export default withPageAuthRequired(function ProfileApi() {
const [user, setUser] = useState();

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

return (
<main>
<h1>Profile (fetched from API)</h1>
<h3>User</h3>
<pre data-testid="profile-api">{JSON.stringify(user, null, 2)}</pre>
</main>
);
});
22 changes: 22 additions & 0 deletions example-app/app/edge-profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { getSession, withPageAuthRequired } from '@auth0/nextjs-auth0/edge';

export default withPageAuthRequired(
async function Page() {
const session = await getSession();

return (
<main>
<h1>Profile</h1>
<h2>Page:</h2>
<h3>Access Token</h3>
<pre>{JSON.stringify({ accessToken: session?.accessToken }, null, 2)}</pre>
<h3>User</h3>
<pre data-testid="profile">{JSON.stringify(session?.user, null, 2)}</pre>
</main>
);
},
{ returnTo: '/edge-profile' }
);

export const runtime = 'edge';
12 changes: 7 additions & 5 deletions example-app/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,20 @@ ul {
li {
margin-right: 1rem;
}
.header.secondary li:nth-last-child(2) {
.header.secondary li:nth-last-child(3) {
margin-right: auto;
}
.header a {
color: #fff;
text-decoration: none;
}
.header.home a[href='/'],
.header.page-router a[href$='page-router'],
.header.profile a[href$='profile'],
.header.profile-middleware a[href$='profile-middleware'],
.header.profile-api a[href$='profile-api'],
.header.page-router a[href$='/page-router'],
.header.profile a[href$='/profile'],
.header.edge-profile a[href$='/edge-profile'],
.header.profile-middleware a[href$='/profile-middleware'],
.header.profile-api a[href$='/profile-api'],
.header.edge-profile-api a[href$='/edge-profile-api'],
a.active {
color: #888;
}
Expand Down
44 changes: 34 additions & 10 deletions example-app/app/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,52 @@ export default function Nav() {
<a>Profile</a>
</Link>
</li>
<li>
<Link href="/edge-profile" legacyBehavior>
<a>Profile (Edge)</a>
</Link>
</li>
<li>
<Link href="/profile-api" legacyBehavior>
<a>Profile (API)</a>
</Link>
</li>
<li>
<Link href="/edge-profile-api" legacyBehavior>
<a>Profile (API / Edge)</a>
</Link>
</li>
<li>
<Link href="/profile-middleware" legacyBehavior>
<a>Profile (Middleware)</a>
</Link>
</li>{' '}
{user ? (
<li>
<a href="/api/auth/logout" data-testid="logout">
Logout
</a>
</li>
<>
<li>
<a href="/api/auth/logout" data-testid="logout">
Logout
</a>
</li>
<li>
<a href="/api/edge-auth/logout" data-testid="logout-edge">
Logout (Edge)
</a>
</li>
</>
) : (
<li>
<a href="/api/auth/login" data-testid="login">
Login
</a>
</li>
<>
<li>
<a href="/api/auth/login" data-testid="login">
Login
</a>
</li>
<li>
<a href="/api/edge-auth/login" data-testid="login-edge">
Login (Edge)
</a>
</li>
</>
)}
</ul>
</nav>
Expand Down
2 changes: 1 addition & 1 deletion example-app/app/profile-api/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default withPageAuthRequired(function ProfileApi() {

useEffect(() => {
(async () => {
const res = await fetch(`${window.location.origin}/api/profile`);
const res = await fetch(`${window.location.origin}/api/edge-profile`);
setUser(await res.json());
})();
}, []);
Expand Down
6 changes: 2 additions & 4 deletions example-app/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { initAuth0 } from '@auth0/nextjs-auth0/edge';
import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge';

const auth0 = initAuth0({ routes: { login: '/api/page-router-auth/login' } });

export default auth0.withMiddlewareAuthRequired();
export default withMiddlewareAuthRequired();

export const config = {
matcher: ['/page-router/profile-middleware', '/profile-middleware']
Expand Down
27 changes: 5 additions & 22 deletions example-app/pages/api/page-router-auth/[...auth0].ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { Session, LoginOptions } from '@auth0/nextjs-auth0';
import { pageRouterAuth } from '../../../lib/auth0';
import { pageRouterAuth } from '@/lib/auth0';

const redirectUri = `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback`;

export default pageRouterAuth.handleAuth({
login: pageRouterAuth.handleLogin({
authorizationParams: { redirect_uri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback` },
getLoginState(req: NextApiRequest, options: LoginOptions) {
return {
returnTo: options.returnTo,
foo: 'bar'
};
}
}),
callback: pageRouterAuth.handleCallback({
redirectUri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback`,
afterCallback(_req: NextApiRequest, _res: NextApiResponse, session: Session) {
return { ...session, foo: 'bar' };
}
}),
me: pageRouterAuth.handleProfile({
refetch: true,
afterRefetch(req: NextApiRequest, res: NextApiResponse, session: Session) {
return { ...session, foo: 'bar' };
}
authorizationParams: { redirect_uri: redirectUri }
}),
callback: pageRouterAuth.handleCallback({ redirectUri }),
logout: pageRouterAuth.handleLogout({ returnTo: `${process.env.AUTH0_BASE_URL}/page-router` })
});
3 changes: 2 additions & 1 deletion example-app/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ const port = +(process.env.PORT || 3000);
const app = next({ dev: true, hostname: 'localhost', port });
const handle = app.getRequestHandler();

process.env.AUTH0_ISSUER_BASE_URL = `http://localhost:${port}/oidc`;
process.env.AUTH0_ISSUER_BASE_URL = `http://localhost:${port}/oidc/`;
process.env.AUTH0_CLIENT_ID = 'testing';
process.env.AUTH0_CLIENT_SECRET = 'testing';
process.env.AUTH0_SCOPE = 'openid profile email offline_access';

app
.prepare()
Expand Down
9 changes: 9 additions & 0 deletions jest-base.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/** @type {import('jest').Config} */
module.exports = {
rootDir: '.',
moduleFileExtensions: ['ts', 'tsx', 'js'],
preset: 'ts-jest/presets/js-with-ts',
globalSetup: './tests/global-setup.ts',
setupFilesAfterEnv: ['./tests/setup.ts'],
transformIgnorePatterns: ['/node_modules/(?!oauth4webapi)']
};
18 changes: 18 additions & 0 deletions jest-edge.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const base = require('./jest-base.config');

/** @type {import('jest').Config} */
module.exports = {
...base,
displayName: 'edge',
testEnvironment: '@edge-runtime/jest-environment',
testMatch: [
'**/tests/handlers/login.test.ts',
'**/tests/handlers/logout.test.ts',
'**/tests/handlers/callback.test.ts',
'**/tests/handlers/profile.test.ts',
'**/tests/http/auth0-next-request.test.ts',
'**/tests/http/auth0-next-response.test.ts',
'**/tests/helpers/with-middleware-auth-required.test.ts',
'**/tests/session/get-access-token.test.ts'
]
};
8 changes: 8 additions & 0 deletions jest-node.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const base = require('./jest-base.config');

/** @type {import('jest').Config} */
module.exports = {
...base,
displayName: 'node',
testEnvironment: 'jest-environment-node-single-context'
};
Loading

0 comments on commit 3adc5d0

Please sign in to comment.