Free preview

Configure Auth0

Part of End to End React with Prisma 2

Objective: Let's set up Auth0 so we have a way to verify who is making the request so we can tell if they have access to or not.

Create an account with Auth0 and then create a new single page application. Go to settings and copy the Domain, Client ID, Client Secret to the following environmental variables in the .env file in the project root (not the prisma folder).

.env

DATABASE_URL=postgresql://user:password@endpoint:5432/database_name
AUTH0_CLIENTID=your_auth0_client_id
AUTH0_DOMAIN=your_auth0_domain
AUTH0_CLIENT_SECRET=your_auth0_client_secret
AUTH0_SCOPE='openid profile'
AUTH0_COOKIE_SECRET='some_greater_than_40_character_string_can_be_anything'
BACKEND_ADDRESS=http://localhost:3000

Install the Auth0 Nextjs package:

npm install --save @auth0/nextjs-auth0 dotenv @apollo/client graphql

utils/apolloClient.ts

import { useMemo } from 'react'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
import getConfig from 'next/config'

const { publicRuntimeConfig } = getConfig()
const { BACKEND_URL } = publicRuntimeConfig

let apolloClient

function createApolloClient() {
  return new ApolloClient({
    ssrMode: typeof window === 'undefined',
    link: new HttpLink({
      uri: BACKEND_URL, // Server URL (must be absolute)
      credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
    }),
    cache: new InMemoryCache({}),
  })
}

export function initializeApollo(initialState = null) {
  const _apolloClient = apolloClient ?? createApolloClient()

  // If your page has Next.js data fetching methods that use Apollo Client, the initial state
  // gets hydrated here
  if (initialState) {
    // Get existing cache, loaded during client side data fetching
    const existingCache = _apolloClient.extract()
    // Restore the cache using the data passed from getStaticProps/getServerSideProps
    // combined with the existing cached data
    _apolloClient.cache.restore({ ...existingCache, ...initialState })
  }
  // For SSG and SSR always create a new Apollo Client
  if (typeof window === 'undefined') return _apolloClient
  // Create the Apollo Client once in the client
  if (!apolloClient) apolloClient = _apolloClient

  return _apolloClient
}

export function useApollo(initialState) {
  const store = useMemo(() => initializeApollo(initialState), [initialState])
  return store
}

utils/auth0.ts

import { initAuth0 } from '@auth0/nextjs-auth0'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()
const { auth, cookieSecret } = serverRuntimeConfig

export default initAuth0({
  ...auth,
  session: {
    cookieSecret,
    cookieLifetime: 60 * 60 * 8,
    storeIdToken: false,
    storeAccessToken: false,
    storeRefreshToken: false,
  },
  oidcClient: {
    httpTimeout: 2500,
    clockTolerance: 10000,
  },
})

next.config.js

require('dotenv').config()

const {
  AUTH0_CLIENTID,
  AUTH0_DOMAIN,
  AUTH0_CLIENT_SECRET,
  AUTH0_SCOPE,
  AUTH0_COOKIE_SECRET,
  BACKEND_ADDRESS,
} = process.env

module.exports = {
  publicRuntimeConfig: {
    BACKEND_URL: `${BACKEND_ADDRESS}/api/graphql`,
  },
  serverRuntimeConfig: {
    auth: {
      domain: AUTH0_DOMAIN,
      clientId: AUTH0_CLIENTID,
      clientSecret: AUTH0_CLIENT_SECRET,
      scope: AUTH0_SCOPE,
      redirectUri: `${BACKEND_ADDRESS}/api/callback`,
      postLogoutRedirectUri: `${BACKEND_ADDRESS}/`,
    },
    cookieSecret: AUTH0_COOKIE_SECRET,
  },
}

Make sure to restart the server by pressing ctrl+c on the terminal window and starting it back up again with npm run dev.

pages/api/login.ts

import auth0 from '../../utils/api/auth0'

export default async function login(req, res) {
  try {
    await auth0.handleLogin(req, res, {})
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}

pages/api/logout.ts

import auth0 from '../../utils/api/auth0'

export default async function logout(req, res) {
  try {
    await auth0.handleLogout(req, res)
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}

pages/api/me.ts

import auth0 from '../../utils/api/auth0'

export default async function me(req, res) {
  try {
    await auth0.handleProfile(req, res)
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}

pages/callback.ts

import auth0 from '../../utils/api/auth0'

export default async function callback(req, res) {
  try {
    await auth0.handleCallback(req, res, { redirectTo: '/' })
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}