prismanext-authnuxt-auth

Additional User data on prisma adapter for authjs


Without modifying the Prisma adapter itself, I can't figure out how I could add a required String "handler" that would be saved when the user creates an account.

When they sign in, I need to generate a random unique handler for the user. Is it possible?

I tried to add info in createUser Event without success.

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  handler       String @unique <-------
  accounts      Account[]
  sessions      Session[]
}
import GithubProvider from 'next-auth/providers/github'
import EmailProvider from 'next-auth/providers/email'
import { PrismaAdapter } from '@next-auth/prisma-adapter'
import { PrismaClient } from '@prisma/client'
import { NuxtAuthHandler } from '#auth'
import { generateFromEmail } from "unique-username-generator";

const prisma = new PrismaClient()

export default NuxtAuthHandler({
  adapter: PrismaAdapter(prisma),
  session: {
    strategy: 'database',
    maxAge: 30 * 24 * 60 * 60,
    updateAge: 24 * 60 * 60
  },
  pages: {
    // Change the default behavior to use custom login page /auth/login as the path for the sign-in page
    signIn: '/auth/login'
  },
  secret: process.env.AUTH_SECRET,
  providers: [
    // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point
    GithubProvider.default({
      clientId: process.env.AUTH_GITHUB_CLIENT_ID,
      clientSecret: process.env.AUTH_GITHUB_CLIENT_SECRET,
      allowDangerousEmailAccountLinking: true,
    }),
    // @ts-expect-error
    EmailProvider.default({
      server: {
        host: process.env.EMAIL_SERVER_HOST,
        port: process.env.EMAIL_SERVER_PORT,
        auth: {
          user: process.env.EMAIL_SERVER_USER,
          pass: process.env.EMAIL_SERVER_PASSWORD
        }
      },
      from: process.env.EMAIL_FROM,
      allowDangerousEmailAccountLinking: true,
    })
  ],
  events: {
    createUser: async (user) => {
      console.log(user)
      try {
        if (!user.handler && user.email) {
          console.log('creating handler')
          const handler = generateFromEmail(user.email, 4)
          
          console.log({handler})
          const existingUser = await prisma.user.findUnique({
            where: {
              email: user.email
            }
          })
          
          if (existingUser) {
            console.log({existingUser})
            await prisma.user.update({
              where: {
                id: user.id
              },
              data: {
                handler
              }
            })
          }
        }
      } catch (err) {
        console.log(err)
      }
    }

  }
})

Thank you!


Solution

  • Struggled with this for a while too. As far as I know there is no way to implement that other than modifying the Adapter.

    Here is my implementation, the relevant parts:

    function CustomPrismaAdapter(p: typeof prisma): Adapter {
      return {
        ...PrismaAdapter(p),
        async createUser(user: Omit<AdapterUser, "id">) {
          const created = await p.user.create({
            data: {
              ...user,
              folders: {
                create: { name: "all" },
              },
            },
          });
    
          return created as AdapterUser;
        },
      };
    }
    
    export const authOptions: NextAuthOptions = {
      adapter: CustomPrismaAdapter(prisma),
      session: { strategy: "jwt" },
      secret: process.env.NEXTAUTH_SECRET,
      pages: {
        signIn: "/login",
      },
      providers: [
        GitHub({
          clientId: process.env.GITHUB_ID ?? "",
          clientSecret: process.env.GITHUB_SECRET ?? "",
        }),
        Google({
          clientId: process.env.GOOGLE_ID ?? "",
          clientSecret: process.env.GOOGLE_SECRET ?? "",
        }),
      ],
      ...
    }
    

    You have to fight TypeScript a bit, but besides that it works as expected.