Skip to content

Commit

Permalink
Migrate auth0 example into Next.js repo (#8802)
Browse files Browse the repository at this point in the history
* Add auth0 example

* Apply suggestions from code review

Co-Authored-By: Joe Haddad <joe.haddad@zeit.co>

* Remove LICENSE

* Add create next-app section

* Update to latest @auth0/nextjs-auth0

* Update user handling

* Update profile link to use <Link>

* Update .env template to reflect guides

* Simplify example

* Update example to prefer API call (temporary hardcoded url)

* Simplify state and ensure rerenders don’t race

* Clear up import being commented

* Make code style consistent

* Update pages to reflect required auth on the client-side

* Memoize the user on window

* Update now.json instruction

* Remove meta fields

* Update docs with explanation

* Update UI for auth0 example
  • Loading branch information
ijjk authored and timneutkens committed Oct 1, 2019
1 parent 1ff5005 commit a73fb5d
Show file tree
Hide file tree
Showing 18 changed files with 547 additions and 0 deletions.
6 changes: 6 additions & 0 deletions examples/auth0/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
AUTH0_CLIENT_ID=
AUTH0_DOMAIN=
AUTH0_CLIENT_SECRET=
REDIRECT_URI=
POST_LOGOUT_REDIRECT_URI=
SESSION_COOKIE_SECRET=
3 changes: 3 additions & 0 deletions examples/auth0/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
.env
.next
98 changes: 98 additions & 0 deletions examples/auth0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Next.js and Auth0 Example

This example shows how you can use `@auth0/nextjs-auth` to easily add authentication support to your Next.js application.

### Using `create-next-app`

Execute [`create-next-app`](https://www.npmjs.com/package/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example:

```bash
npx create-next-app --example auth0 auth0
# or
yarn create next-app --example auth0 auth0
```

## Configuring Auth0

1. Go to the [Auth0 dashboard](https://manage.auth0.com/) and create a new application of type _Regular Web Applications_ and make sure to configure the following
2. Go to the settings page of the application
3. Configure the following settings:

- _Allowed Callback URLs_: Should be set to `http://localhost:3000/api/callback` when testing locally or typically to `https://myapp.com/api/callback` when deploying your application.
- _Allowed Logout URLs_: Should be set to `http://localhost:3000/` when testing locally or typically to `https://myapp.com/` when deploying your application.

4. Save the settings

### Configuring Next.js

In the Next.js configuration file (`next.config.js`) you'll see that different environment variables are being assigned.

### Local Development

For local development you'll want to create a `.env` file with the necessary settings.

The required settings can be found on the Auth0 application's settings page:

```
AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
AUTH0_CLIENT_ID=YOUR_AUTH0_CLIENT_ID
AUTH0_CLIENT_SECRET=YOUR_AUTH0_CLIENT_SECRET
SESSION_COOKIE_SECRET=viloxyf_z2GW6K4CT-KQD_MoLEA2wqv5jWuq4Jd0P7ymgG5GJGMpvMneXZzhK3sL (at least 32 characters, used to encrypt the cookie)
REDIRECT_URI=http://localhost:3000/api/callback
POST_LOGOUT_REDIRECT_URI=http://localhost:3000/
```

### Hosting on ZEIT Now

When deploying this example to ZEIT Now you'll want to update the `now.json` configuration file.

```json
{
"build": {
"env": {
"AUTH0_DOMAIN": "YOUR_AUTH0_DOMAIN",
"AUTH0_CLIENT_ID": "YOUR_AUTH0_CLIENT_ID",
"AUTH0_CLIENT_SECRET": "@auth0_client_secret",
"REDIRECT_URI": "https://my-website.now.sh/api/callback",
"POST_LOGOUT_REDIRECT_URI": "https://my-website.now.sh/",
"SESSION_COOKIE_SECRET": "@session_cookie_secret",
"SESSION_COOKIE_LIFETIME": 7200
}
}
}
```

- `AUTH0_DOMAIN` - Can be found in the Auth0 dashboard under `settings`.
- `AUTH0_CLIENT_ID` - Can be found in the Auth0 dashboard under `settings`.
- `AUTH0_CLIENT_SECRET` - Can be found in the Auth0 dashboard under `settings`.
- `REDIRECT_URI` - The url where Auth0 redirects back to, make sure a consistent url is used here.
- `POST_LOGOUT_REDIRECT_URI` - Where to redirect after logging out
- `SESSION_COOKIE_SECRET` - A unique secret used to encrypt the cookies, has to be at least 32 characters. You can use [this generator](https://generate-secret.now.sh/32) to generate a value.
- `SESSION_COOKIE_LIFETIME` - How long a session lasts in seconds. The default is 2 hours.

The `@auth0_client_secret` and `@session_cookie_secret` are [ZEIT Now environment secrets](https://zeit.co/docs/v2/environment-variables-and-secrets/)

You can create the `@auth0_client_secret` by running:

```
now secrets add auth0_client_secret PLACE_YOUR_AUTH0_CLIENT_SECRET
```

And create the `session_cookie_secret` by generating a value [here](https://generate-secret.now.sh/32) and running:

```
now secrets add session_cookie_secret PLACE_YOUR_SESSION_COOKIE_SECRET
```

## About this sample

This sample tries to cover a few topics:

- Signing in
- Signing out
- Loading the user on the server side and adding it as part of SSR (`/pages/advanced/srr-profile.js`)
- Loading the user on the client side and using fast/cached SSR pages (`/pages/index.js`)
- API Routes which can load the current user (`/pages/api/me.js`)
- Using hooks to make the user available throughout the application (`/lib/user.js`)
81 changes: 81 additions & 0 deletions examples/auth0/components/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import Link from 'next/link'

function Header ({ user, loading }) {
return (
<header>
<nav>
<ul>
<li>
<Link href='/'>
<a>Home</a>
</Link>
</li>
<li>
<Link href='/about'>
<a>About</a>
</Link>
</li>
{!loading &&
(user ? (
<>
<li>
<Link href='/profile'>
<a>Client-rendered profile</a>
</Link>
</li>
<li>
<Link href='/advanced/ssr-profile'>
<a>Server rendered profile (advanced)</a>
</Link>
</li>
<li>
<a href='/api/logout'>Logout</a>
</li>
</>
) : (
<li>
<a href='/api/login'>Login</a>
</li>
))}
</ul>
</nav>

<style jsx>{`
header {
padding: 0.2rem;
color: #fff;
background-color: #333;
}
nav {
max-width: 42rem;
margin: 1.5rem auto;
}
ul {
display: flex;
list-style: none;
margin-left: 0;
padding-left: 0;
}
li {
margin-right: 1rem;
}
li:nth-child(2) {
margin-right: auto;
}
a {
color: #fff;
text-decoration: none;
}
button {
font-size: 1rem;
color: #fff;
cursor: pointer;
border: none;
background: none;
}
`}</style>
</header>
)
}

export default Header
35 changes: 35 additions & 0 deletions examples/auth0/components/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Head from 'next/head'
import Header from './header'

function Layout ({ user, loading = false, children }) {
return (
<>
<Head>
<title>Next.js with Auth0</title>
</Head>

<Header user={user} loading={loading} />

<main>
<div className='container'>{children}</div>
</main>

<style jsx>{`
.container {
max-width: 42rem;
margin: 1.5rem auto;
}
`}</style>
<style jsx global>{`
body {
margin: 0;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
`}</style>
</>
)
}

export default Layout
14 changes: 14 additions & 0 deletions examples/auth0/lib/auth0.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useAuth0 } from '@auth0/nextjs-auth0'

export default useAuth0({
clientId: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
scope: process.env.AUTH0_SCOPE,
domain: process.env.AUTH0_DOMAIN,
redirectUri: process.env.REDIRECT_URI,
postLogoutRedirectUri: process.env.POST_LOGOUT_REDIRECT_URI,
session: {
cookieSecret: process.env.SESSION_COOKIE_SECRET,
cookieLifetime: process.env.SESSION_COOKIE_LIFETIME
}
})
70 changes: 70 additions & 0 deletions examples/auth0/lib/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState, useEffect } from 'react'
import fetch from 'isomorphic-unfetch'

export async function fetchUser (cookie = '') {
if (typeof window !== 'undefined' && window.__user) {
return window.__user
}

const res = await fetch(
'/api/me',
cookie
? {
headers: {
cookie
}
}
: {}
)

if (!res.ok) {
delete window.__user
return null
}

const json = await res.json()
if (typeof window !== 'undefined') {
window.__user = json
}
return json
}

export function useFetchUser ({ required } = {}) {
const [loading, setLoading] = useState(
() => !(typeof window !== 'undefined' && window.__user)
)
const [user, setUser] = useState(() => {
if (typeof window === 'undefined') {
return null
}

return window.__user || null
})

useEffect(() => {
if (!loading && user) {
return
}
setLoading(true)
let isMounted = true

fetchUser().then(user => {
// Only set the user if the component is still mounted
if (isMounted) {
// When the user is not logged in but login is required
if (required && !user) {
window.location.href = '/api/login'
return
}
setUser(user)
setLoading(false)
}
})

return () => {
isMounted = false
}
}, [])

return { user, loading }
}
17 changes: 17 additions & 0 deletions examples/auth0/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const dotenv = require('dotenv')
dotenv.config()

module.exports = {
env: {
AUTH0_DOMAIN: process.env.AUTH0_DOMAIN,
AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID,
AUTH0_CLIENT_SECRET: process.env.AUTH0_CLIENT_SECRET,
AUTH0_SCOPE: 'openid profile',
REDIRECT_URI:
process.env.REDIRECT_URI || 'http://localhost:3000/api/callback',
POST_LOGOUT_REDIRECT_URI:
process.env.POST_LOGOUT_REDIRECT_URI || 'http://localhost:3000/',
SESSION_COOKIE_SECRET: process.env.SESSION_COOKIE_SECRET,
SESSION_COOKIE_LIFETIME: 7200 // 2 hours
}
}
12 changes: 12 additions & 0 deletions examples/auth0/now.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"build": {
"env": {
"AUTH0_DOMAIN": "YOUR_AUTH0_DOMAIN",
"AUTH0_CLIENT_ID": "YOUR_AUTH0_CLIENT_ID",
"AUTH0_CLIENT_SECRET": "@auth0_client_secret",
"REDIRECT_URI": "https://my-website.now.sh/api/callback",
"POST_LOGOUT_REDIRECT_URI": "https://my-website.now.sh/",
"SESSION_COOKIE_SECRET": "@session_cookie_secret"
}
}
}
18 changes: 18 additions & 0 deletions examples/auth0/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "nextjs-auth0-example",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"author": "",
"license": "MIT",
"dependencies": {
"@auth0/nextjs-auth0": "0.2.0",
"dotenv": "^8.1.0",
"isomorphic-unfetch": "^3.0.0",
"next": "latest",
"react": "^16.9.0",
"react-dom": "^16.9.0"
}
}
21 changes: 21 additions & 0 deletions examples/auth0/pages/about.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'

import Layout from '../components/layout'
import { useFetchUser } from '../lib/user'

function About () {
const { user, loading } = useFetchUser()

return (
<Layout user={user} loading={loading}>
<h1>About</h1>
<p>
This is the about page, navigating between this page and <i>Home</i> is
always pretty fast. However, when you navigate to the <i>Profile</i>{' '}
page it takes more time because it uses SSR to fetch the user first;
</p>
</Layout>
)
}

export default About
Loading

0 comments on commit a73fb5d

Please sign in to comment.