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

[SDK-4319] Add support for Edge runtime #1269

Merged
merged 3 commits into from
Jun 28, 2023
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
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>
Copy link
Contributor

Choose a reason for hiding this comment

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

Is legacyBehavior still needed here (and in other links)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was just moving this over from the old example app, I could change them - but I'd rather do that in another PR

<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