next-auth: Prisma Adapter Create Session Error From Using Wrong ID

https://github.com/nextauthjs/next-auth/blob/1a1a1f97214bedc7a2a0e092b168b331ffa6a64b/src/adapters/prisma/index.js#L163

I would submit a PR for this but want some context from maintainers on what might be going wrong because my solution might not be the right one.

I found an error using the Prisma adapter and google provider where creating a session when logging in after the first time creates a session with a userId of a different id than the user that successfully logs in. Logging in the first time with google is successful and creates a user, session and account in the db. All of the userId fields line up but when you log out and log back in again it creates a new session row in the db where the userId is not the same as the user object id in the db that just successfully logged in. Error I get is cannot read name of undefined where user is undefined because it is trying to look up a user of a different id. If there is a user of that different id it shows me logged in as that user which is not good. It appears right now that in the createSession function it’s creating a session where userId: user.id but user.id seems to actually be using the .id from the account object. In my current data the Account ID for User with ID: 9 is 8. And if I created a new user #10 then their Account ID would be 9 etc. So for example when I log in with user #10 it creates a session linked to user #9.

I changed the linked line of code above to be userId: user.userId, and that seems to work but I don’t know why the account object would be getting passed in here instead of the user. It is weird that it seems to work the first time it creates a session but not other times. I’ve also tried clearing my session cache. Could this be a weird google thing?

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 18 (6 by maintainers)

Most upvoted comments

Someone from the Prisma team is working on a new implementation of the Prisma adapter, might be interesting to you, Prisma folks: https://github.com/nextauthjs/adapters/pull/1

@itwasmattgregg no worries. I think I snagged it: https://github.com/royletron/next-auth/commit/c6ee715973ef7659a570bb075d75e7566569e0d7

So it looks like when creating the session it was using the id of the account. Which isn’t a problem as long as your incremental IDs are in sync - and it should be that you get 1 account per 1 user - so they should in theory match. Unless you like to trash your DB every now and then… which it then gets borked. The fix was to use the account.userId and the problem goes away. I will get it into a PR-able state after I have my production environment working again 🥱

@itwasmattgregg Wow that is really odd, agreed! Thank you for sharing findings.

If I can’t get to the bottom of this, I might suggest a change in the docs to use cuid() in the next release.

I haven’t seen this issue, so would like to try and replicate it first. Am aware we need to swap out a deprecated function for compatibility with new releases of Prisma, so might do that too as a trivial change to remove the call to a deprecated function. Look forward to seeing what the Prisma folks have in mind!

It’s really weird that cuid doesn’t have this problem but incremental does. I’m gonna create a fresh project and see if I can recreate the issue.

@itwasmattgregg I hadn’t realised you could use cuid - I had always struggled previous using uuid. So I think for now I can re-jig our schema to use cuid and then at least people aren’t inadvertantly logging in as one another. I cannot comprehensively recreate this issue so I am unsure what to do

It’s weird that it works totally fine with cuid. I feel like it shouldn’t based on the issues with auto incrementing IDs

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime? @map(name: "verified_email")
  image         String?
  createdAt     DateTime  @default(now()) @map(name: "created_at")
  updatedAt     DateTime  @updatedAt @map(name: "updated_at")

  @@map(name: "users")
}

model Account {
  id                 String    @id @default(cuid())
  compoundId         String    @unique @map(name: "compound_id")
  userId             String    @map(name: "user_id")
  providerType       String    @map(name: "provider_type")
  providerId         String    @map(name: "provider_id")
  providerAccountId  String    @map(name: "provider_account_id")
  refreshToken       String?   @map(name: "refresh_token")
  accessToken        String?   @map(name: "access_token")
  accessTokenExpires DateTime? @map(name: "access_token_expires")
  createdAt          DateTime  @default(now()) @map(name: "created_at")
  updatedAt          DateTime  @default(now()) @map(name: "updated_at")


  @@index([providerAccountId], name: "providerAccountId")
  @@index([providerId], name: "providerId")
  @@index([userId], name: "userId")
  @@map(name: "accounts")
}

model Session {
  id           String   @id @default(cuid())
  userId       String   @map(name: "user_id")
  expires      DateTime
  sessionToken String   @unique @map(name: "session_token")
  accessToken  String   @unique @map(name: "access_token")
  createdAt    DateTime @default(now()) @map(name: "created_at")
  updatedAt    DateTime @default(now()) @map(name: "updated_at")

  @@map(name: "sessions")
}

This means it would be broken for non-auto-incremented ids too like uuid and cuid for those models. Might be an easy way to test this.

updateSession should be using the User object not the Account object. I just couldn’t find where this was being called

Sorry @iaincollins it’s actually the updateSession that has the problem:

Screenshot 2021-01-22 at 18 00 58

Apologies for the crudity of this diagram…

I can do some more logging tomorrow. userId did exist though and id was definitely the id of the account object in my database. I’ll have to recreate a setup where the account ids don’t line up with user ids. Probably just create fake user rows in the db.