Next.js Discord

Discord Forum

NextAuth. How to put database id into session

Unanswered
Philippine Crocodile posted this in #help-forum
Open in Discord
Philippine CrocodileOP
Im creating an app with google sign in. I want to store my users in the database and for optimization I want to store user's database id in the session. how Can I do that? In which callback should I put my initial user creation in the database and how to pass id from database to the session. docs didn't help

14 Replies

Plott Hound
@Philippine Crocodile this is probably the easiest way to do what you're trying to achieve:
import { PrismaAdapter } from "@auth/prisma-adapter";
import type { NextAuthOptions } from "next-auth";
import prisma from '@/db/prisma';
import GoogleProvider from "next-auth/providers/google";

export const authOptions: NextAuthOptions = {
    adapter: PrismaAdapter(prisma),
    session: {
        strategy: "jwt",
    },
    callbacks: {
        jwt: async ({ user, token }) => {
    if (user) {
        token.uid = user.id;
    }
    return token;
},
session: async ({ session, token }) => {
    if (session?.user) {
        (session.user as any).id = token.uid;
    }
    return session;
}

    },
    providers: [
        GoogleProvider({
            clientId: process.env.GOOGLE_CLIENT_ID ?? "",
            clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
        })
    ],
};
@Plott Hound are you using any ORM?
Philippine CrocodileOP
nah, I use pocketbase as a db
Plott Hound
@Philippine Crocodile hmm, i havent used pocketbase but i assume something like this would work:
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { pocketbaseClient } from '@/db/pocketbase'; // Assuming this is the PocketBase client setup

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID ?? "",
      clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
    }),
  ],
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      try {
        // Check if user exists in PocketBase
        const existingUser = await pocketbaseClient.users.get(email);

        if (!existingUser) {
          // Create new user in PocketBase
          await pocketbaseClient.users.create({
            email: email,
            // other user details
          });
        }
        
        return true; // Sign-in successful
      } catch (error) {
        console.error('Sign-in error:', error);
        return false; // Sign-in failed
      }
    },
    // other callbacks
  },
  // other config options
};

basically you should focus on the signIn callback. This callback is triggered during the sign-in process and is the ideal place to check if a user exists in the PocketBase database and create a new user if necessary.
Philippine CrocodileOP
thanks, I tried it. then after creation of the user in my db how can I pass its id to the session?
and should I? or it's a bad practice
Plott Hound
im just trying to replicate your setup, gimme 5mins. could try something like this:

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { pocketbaseClient } from '@/db/pocketbase';

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID ?? "",
      clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
    }),
  ],
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      try {
        let userId;

        // Check if user exists in PocketBase
        const existingUser = await pocketbaseClient.users.get(email);
        if (existingUser) {
          userId = existingUser.id;
        } else {
          // Create new user in PocketBase
          const newUser = await pocketbaseClient.users.create({
            email: email,
            // other user details
          });
          userId = newUser.id;
        }

        // Add userId to the token
        user.id = userId;
        return true; // Sign-in successful
      } catch (error) {
        console.error('Sign-in error:', error);
        return false; // Sign-in failed
      }
    },
    async jwt({ token, user }) {
      // Pass the user ID to the JWT token
      if (user?.id) {
        token.uid = user.id;
      }
      return token;
    },
    async session({ session, token }) {
      // Retrieve the user ID from the JWT token and add it to the session
      session.userId = token.uid;
      return session;
    },
  },
  // other config options
};
Philippine CrocodileOP
thanks
@Philippine Crocodile and should I? or it's a bad practice
Plott Hound
Putting the user ID in the session is not inherently a bad practice, and it's actually quite common in web development. It reduces lookups, Instead of querying the database for the user ID on every request, you can directly use the ID stored in the session.

The primary concern is security. The user ID should be treated as sensitive information.

mainly you want to avoid exposing it to the client side
Philippine CrocodileOP
thank you
Plott Hound
your welcome. mark this as solved once you get it working 🙂
Philippine CrocodileOP
  callbacks: {
    async signIn({ user, account, profile }) {
      let userId;
      try {
        const DbUser = await pb.collection('users').create({ username: profile.name, email: profile.email });
        userId = DbUser.id
      }
      catch (e) {
        const DbUser = await pb.collection('users').getFirstListItem(`email="${profile.email}"`)
        userId = DbUser.id
      }
      user.id = userId;

      return true
    },
    async jwt({token, user, account, profile}) {
      token.id = user.id
      return token
    },
    async session({session, token}) {
      session.id = token.id
      return session
    },

@Plott Hound here is my current code. It doesnt work because jwt() can be called without user.id
user object at jwt function is undefined sometimes