NextAuth and Prisma: extend the user object in the session

NextAuth and Prisma: extend the user object in the session

Update 22.09.22: I found an easier way to fix some of the problems with adding to the session object: This official guide shows how to alter the session schema in prisma to add additional fields. It doesnt solve everything, so if I wanted the full userDetails from the db I still have to use the method below. But if I only wanted the userDetails.id I could use this official way.

When you use NextAuth with the Prisma adapter you get the user object automatically attached to the session.

But I have additional relations on my User model in the Prisma schema:

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
  accountType    Role[] // custom relation containing roles
  userDetails    UserDetails // custom table containing additional info about the user
}

So how to get this added to the user object in the session?

I tried changing the callbacks in [...nextauth].ts:

callbacks: {
    async session({ session, token, user }) {
      // Send properties to the client, like an access_token from a provider.
      session.user = user;

      let userDetails = await prisma.userDetails.findUnique({
        where: {
          userId: user.id,
        },
      });
      session.userdetails = userDetails;

      return session;
    },
  },

This works, but the session callback now have an additional object, like this:

session: 
{
    user: {user},
    session: {session},
    userdetails: {userDetails}
}

While I want it like this:

session: 
{
    user: {user}, // userDetails and accountType added to this object
    session: {session},
}

The PrismaAdapter is opensource and I found it on github: https://github.com/nextauthjs/adapters/blob/main/packages/prisma/src/index.ts

I then added this function to my [...nextauth].ts file, but changed the getSessionAndUser function.

So instead of importing the PrismaAdapter, you add the function yourself. You could of course put it in its own file and import that.

Now the file looks kinda like this, the change is the "include" portion.

// [...nextauth].ts
// import { PrismaAdapter } from "@next-auth/prisma-adapter"; // not importing this anymore

// added this to the file:
function PrismaAdapter(p: PrismaClient): Adapter {
  return {
   ... // not showing all the functions, only the changed part:

    async getSessionAndUser(sessionToken) {
      const userAndSession = await p.session.findUnique({
        where: { sessionToken },
        include: {
          user: {
            include: {
              accountType: true,
              userDetails: true,
            },
          },
        },
      });
      if (!userAndSession) return null;
      const { user, ...session } = userAndSession;
      return { user, session };
    },

    ...
}

Now I get accountType and userDetails included in my {user} in the session!