How to: Here's how I implemented Auth0 on the api side

Here’s how I implemented getCurrentUser with Auth0. I used Auth0’s sub as the User id, but I wouldn’t recommend that.

// api/src/lib/auth.js
import { AuthenticationError } from '@redwoodjs/api'
import { AuthenticationClient } from 'auth0'

import { db } from './db'

const auth0 = new AuthenticationClient({
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
})

export const getCurrentUser = async (jwt, accessToken) => {
  // try to find the user
  let user = await db.user.findOne({
    where: {
      id: jwt.sub,
    },
  })

  if (!user && accessToken) {
    const auth0User = await auth0.getProfile(accessToken)

    // otherwise create a new user
    user = await db.user.create({
      data: {
        id: auth0User.sub,
        email: auth0User.email,
        // you could add more values
      },
    })
  }

  return user
}

If you notice, getCurrentUser in this implementation actually takes two arguments. The new one is accessToken. accessToken is required in order to call out to the Auth0 API to get the profile information. Unfortunately, accessToken is not in context. How did I get the accessToken? I’m glad you asked. I dug around in the RedwoodJS source code and I found out that createGraphQLHandler takes a context argument.

export const handler = wrapFunction(
  createGraphQLHandler({
    schema: makeMergedSchema({
      schemas,
      services: makeServices({ services }),
    }),
    db,
    async context({ event, context }) {
      const accessToken = event.headers?.authorization?.split(' ')?.[1]
      const jwt = context.currentUser
      const currentUser = await getCurrentUser(jwt, accessToken)

      return {
        ...context,
        accessToken,
        currentUser,
      }
    },
  })
)
3 Likes

I’m guessing it would be super helpful to provide this as a second argument in the getCurrentUser function?

Something like getCurrentUser(decoded, { token, authType }) => {}

2 Likes

Yeah that would be useful.