skip to Main Content

I want to store my user’s id and role in my nextAuth session, should I perform a lookup in my database for the user during the authOptions.callbacks?

The consensus I’ve found on Github discussions and in the Docs, is to pass information from the callback.jwt(token) into the callback.session(token). The problem is, when I use Google or Github OAuth providers, they only provide a jwt(token) argument with the fields: name, email, picture, sub, iat, exp, jti and the rest of the args (user, account, profile) values provided in the jwt callback are undefined.

since none of those provided args have the fields I need, should I use the returned unique email in my jwt token to perform a database lookup within callback.jwt(), append the returned mongodb document’s id and role to the token, and then append those to the following session return value?

// [...nextAuth].ts authOptions
  ...
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async jwt({ token, user, account, profile }) {
      // user, account, and profile are undefined when using OAuth
      // token returns { user: "a", email: "b", image: "c" }
      
      // should I perform a database lookup here, find the matching user via email
      // and then append the found user.id and user.role into token? 
      return token;
    },
    async session({ session, user, token }) {
      // then insert those id and role key/values into session here before returning? 
      return session;
    },
  },

Is performing a database lookup within my authOptions.callback a good idea? –is this convention?

Extra – why doesn’t nextAuth lookup the user by matching email automatically?

— Resolved

Since I am using MongoDB as my database, I have the MongoDB adapter setup. I was originally thinking that the adapter would perform lookups for the matching user via email in my database when signing in through OAuth and then surface the matching user in my callback.jwt(user) argument. Yet, my user argument is undefined when using OAuth but exists when using Credentials. I am a further confused by the intellisense message when hovering over callback.jwt(user) as it seems to indicate something contradictory?

// intellisense for callback.jwt(user)
Either the result of the OAuthConfig.profile or the CredentialsConfig.authorize callback.

Should I expect .profile to be my database user? Currently, it’s returning undefined. Is my adapter not working correctly, is this behavior dependent on the underlying OAuth provider, or am I just misunderstanding/mis-implementing the feature?

2

Answers


  1. Chosen as BEST ANSWER

    I continued to tinker around with NextAuth and learned that there is a bit of a convention with all adapters. When you work with "Credentials" and/or an OAuth provider, your database user is in fact looked up and returned back. Within the the jwt callback user will return as the result of the lookup for both provider types. Just make sure you wrap it with a conditional check as shown below.

    It's worth noting that this requires you to have a database set up, and just as NextAuth is able to create/update (automatically) users/accounts, it can --and does, look up the matching collections/rows during signin and registration. Which means you do not have to perform another look-up within the jwt callback to get your user's information, since your database information is automatically handed back to you during signin through the callback flow.

    Also, while I have a database, as per the docs, you can override the default "database" strategy with session: {strategy: 'jwt' } which is required if you still want to sign in and register users in through email/password credentials.

    // here is the relevant code within AuthOptions
    
      session: {
        strategy: "jwt",
      },
      callbacks: {
        async jwt({ token, user }) {
          if (user) {
            token.id = user.id;
            token.role = user.role || null;
          }
          return token;
        },
        async session({ session, token }) {
          session.user.userId = token.id as string;
          session.user.role = token.role as string;
          return session;
        },
      },
    

  2. .profile is not your database user. its type:

    /** The OAuth profile returned from your provider */
    export interface Profile {
        sub?: string;
        name?: string;
        email?: string;
        image?: string;
    }
    

    this comes from the oauth provider. you have to fetch the database user inside the jwt callback

    callbacks: {
      async jwt({ token, user }) {
        // make a query based on your logic
        const dbUser = await User.findOne({tokenId:token.id})
        
        ...
      },
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search