Search code examples
next-auth

Empty User Object in nextAuth JWT Callback


I am trying to adjust my JWT and Session callbacks to attach properties to my browser session, but I can't seem to figure out what is being returned incorrectly which is leading to my JWT callback having an undefined user. I am using a JWT session strategy and custom login page.

Here is a log of JWT and Session callbacks:

JWT Callback {
  token: {
    name: 'John Doe',
    email: '[email protected]',
    iat: 1678918901,
    exp: 1681511921,
    jti: '4e1b6c12-68f0-4009-8497-b2fa6a3bbb7e'
  },
  user: undefined
}
Session Callback {
  session: {
    user: { name: 'John Doe', email: '[email protected]', image: undefined },
    expires: '2023-04-14T22:26:51.318Z'
  },
  token: {
    name: 'John Doe',
    email: '[email protected]',
    iat: 167891221,
    exp: 1682510921,
    jti: '4e1b6c12-68f0-4009-8497-b2fa6a3bbb7e'
  }
}

Here is the authorize:

async authorize(credentials) {
        console.log(credentials);
        const email = credentials.email;
        const password = credentials.password;
        const formPassword = credentials.formPassword;
        const authFormType = credentials.newUser ? formPassword : password;

        try {
          if (!email || !password) {
            console.log('No Email or Password Provided');
            return null;
          }

          const user = await prisma.appUsers.findUnique({
            where: {
              email: email,
            },
          });

          if (!user) {
            console.log('User does not exist');
            return null;
          }

          const isPasswordValid = await confirmPasswordHash(
            authFormType,
            user.password
          );

          if (!isPasswordValid) {
            console.log('Password is not correct');
            return null;
          }

          return {
            email: user.email,
            name: user.firstName + ' ' + user.lastName,
            // id: user.id,
            // newUser: credentials.newUser,
          };
        } catch (error) {
          console.log(`Error: ${error}`);
        }
      },

Callbacks:

callbacks: {
    session: ({ session, token }) => {
      console.log('Session Callback', { session, token });
      return session;
    },
    jwt: ({ token, user }) => {
      console.log('JWT Callback', { token, user });
      if (user) {
        // console.log('user', user);
      }
      return token;
    },
  },

Solution

  • If the user is undefined in the jwt callback, how do you explain that the token and the session are correctly updated from the response?

    The session callback is called whenever a session is checked. By default, only a subset of the token is returned for increased security. If you want to make something available you added to the token (like access_token and user.id from above) via the jwt() callback, you have to explicitly forward it here to make it available to the client.

    so at some point the user was defined and available in the jwt callback to update the token and this latter was forwarded to the session callback, owtherwise the token and the session will never be updated

    the user may be undefined sometimes inside the jwt callback when its value is not available yet

    Callbacks are asynchronous functions you can use to control what happens when an action is performed.

    so if you look deeper in your logs you can see that the jwt callback was called twice and on the first call the user was defined.

    However please tell that that the callbacks mentioned here in your code example are just for making the code shorther because the output you got with those empty callbacks is really confusing