500 Error on User Table

Hey all :wave:,

Loving Redwood. I’m having a little difficulty getting it running on Netlify though. I’ve got a User table that is 1-1 with Netlify Auth. It should create the User if it’s not in the User table.

The question is, do you have any idea why User isn’t being defined?

That’s defined here: https://github.com/csellis/grocery/blob/master/api/src/lib/auth.js

export const getCurrentUser = async ({ name, email }) => {
  const user =
    (await db.user.findOne({
      where: { email },
    })) || (await createUser(name, email))
  return user
}

export const createUser = async (name, email) => {
  return await db.user.create({
    data: { name, email }
  })
}

This is @rob 's trick I found in a post somewhere.
Error Message:

Context creation failed: 
Invalid `prisma.user.findOne()` invocation in
  /var/task/src/api/dist/lib/auth.js:44:35

Error occurred during query execution:
ConnectorError(
  ConnectorError { 
    user_facing_error: None, kind: QueryError(Error { 
     kind: Db, cause: Some(DbError { severity: "ERROR", 
     parsed_severity: Some(Error), code: SqlState("42P01"), 
     message: "relation \"public.User\" does not exist", 
     detail: None, hint: None, position: Some(Original(83)), 
     where_: None, schema: None, table: None, column: None, 
     datatype: None, constraint: None, file: Some("parse_relation.c"), 
     line: Some(1173), routine: Some("parserOpenTable")
    }) 
  })
})

Hi @csellis.

Are you using the same database in dev and in prod (ie, Netlify) or different ones?

I see some references to that error like

and in each they either have people check:

  1. the tables do exists and if not migrate
  2. check the the table/columns are all same case
1 Like

Thanks for the quick response David! Yes, they’re different databases. Dev is the local, and Netlify is on a Heroku instance

Maybe to do with this https://github.com/redwoodjs/redwood/issues/934

I’ve released a canary version yarn rw upgrade --canary that updates the Prisma dependency. Hopefully that resolves this issue.

I believe it’s yarn rw upgrade --tag canary

1 Like

Lol, you’re right, thanks Tobbe!

It’s come up again :man_facepalming:

Looks like currentUser context isn’t being set

Uh, oh. Can you share a bit more information so we can try to debug this?

@csellis you shared this with me on the Twitters (might have been after you wrote the comment above):

Netlify Auth seems to return the email whereas Auth0 returns the jwt without the email to match the local User. I just need to figure how to get the User

Is this what you’ve diagnosed as the problem?

If I understand correctly, this might be the problem/solution:

And, Chris, oh boy oh boy could we use help with Auth0 docs! Would really like to help take what you’ve learned and make it accessible for others – just let me know how I/we can help if and when you’re up for it.

The hash links here do not seem to go to the section:

https://redwoodjs.com/docs/authentication#for-auth0

But in here I describe how to setup rules to pull in the user metadata.

https://redwoodjs.com/docs/authentication#auth0-rules-for-app-metadata

There is an Auth0 rule template to set the email:

function (user, context, callback) {
  // This rule adds the authenticated user's email address to the access token.

  var namespace = 'https://example.com/';

  context.accessToken[namespace + 'email'] = user.email;
  return callback(null, user, context);
}

Probably should add this to the docs as well, but it is good practive not to have personal info in a token because anybody can get at the data.

Instead, you can use Auth0’s admin API and fetch the user profile info using their accessToken and get that.

Or you can use a Auth0 Hook to send the profile to a function and update your profile.

That’s what I do.

I can share an example of each if need.

1 Like

Also, this video at around the 40 minute mark shows some Auth0 setup – with the API that needs to setup as well as the rule to add the email to token.

1 Like

@peterp @thedavid @dthyresson Thanks all!

I watched a few videos last night, and I came to the same conclusion. Sharing this would be really helpful.

This video is awesome!

1 Like

So I went with passing the email through the token even though it’s not ideal. I guess I’m satisficing. I’m just stuck figuring out environment variables on Vercel. They do something funny with runtime versus build time. Thank you all for the help!

2 Likes

Your other options to get the complete user profile are to in a service like userManager.js and use Auth0’s two clients - AuthenticationClient (works on a bearer token and can access that users’ info) and ManagementClient (which is the “admin” client and can access any user).

You’ll have to create a new Machine to Machine client with authorized access to the API Client you have already setup in Auth0 for a ManagementClient to get the client_id and secrets needed.

// api/src/services/userManager/userManager.js

import { AuthenticationClient, ManagementClient } from 'auth0'

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

const management = new ManagementClient({
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_MANAGEMENT_CLIENT_ID,
  clientSecret: process.env.AUTH0_MANAGEMENT_CLIENT_SECRET,
  scope: 'read:users update:users',
})

// fetches the user's profile specified by their accessToken using AuthenticationClient
export const fetchUserProfileByToken = async (token) => {
  try {
    const auth0User = await auth0.getProfile(token)
    return {
      email: auth0User.email,
      emailVerified: auth0User.email_verified,
      name: auth0User.name,
      nickname: auth0User.nickname,
      picture: auth0User.picture,
      userId: auth0User.user_id,
    }
  } catch (error) {
    console.log(error)
    throw new Error('Could not fetch profile')
  }
}

// fetches any user by their id using the ManagementClient
export const fetchUserProfileByUserId = async (userId) => {
  try {
    const auth0User = await management.users.get({ id: userId })

    return {
      email: auth0User.email,
      emailVerified: auth0User.email_verified,
      lastIp: auth0User.last_ip,
      lastLogin: auth0User.last_login,
      loginsCount: auth0User.logins_count,
      name: auth0User.name,
      nickname: auth0User.nickname,
      picture: auth0User.picture,
      userId: auth0User.user_id,
    }
  } catch (error) {
    console.log(error)
    throw new Error('Could not fetch user')
  }
}

Note: you do not want to call these per each request – so not a good idea to use them in getCurrentUser but rather on a signup or some profile refresh.

I use them via a rule (on sign in) and a hook (signup) in Auth0 that posts to a function that then updates the profile.