nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.37k stars 3.41k forks source link

Unknown argument `provider_providerAccountId`. Available options are marked with ? #11205

Open mashwishi opened 3 months ago

mashwishi commented 3 months ago

Adapter type

@auth/prisma-adapter

Environment

  System:
    OS: Windows 11 10.0.22631
    CPU: (12) x64 Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
    Memory: 17.58 GB / 31.94 GB
  Binaries:
    Node: 18.18.2 - c:\program files\nodejs\node.EXE
    Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 9.8.1 - c:\program files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (122.0.2365.66)
    Internet Explorer: 11.0.22621.3527
  npmPackages:
    @auth/prisma-adapter: ^1.3.3 => 1.6.0 
    next: 14.1.0 => 14.1.0 
    next-auth: 5.0.0-beta.5 => 5.0.0-beta.5 
    react: ^18 => 18.3.1 

Reproduction URL

https://github.com/mashwishi/mash-nextauth

Describe the issue

I'm encountering an issue with NextAuth's Prisma adapter. When attempting to use prisma.account.findUnique() with a specific where clause, I'm getting an error indicating an invalid argument provider_providerAccountId. The error message suggests using providerId_providerAccountId instead.

Here's the error message I'm receiving:

[auth][error] AdapterError: Read more at https://errors.authjs.dev#adaptererror
[auth][cause]: PrismaClientValidationError:
Invalid `prisma.account.findUnique()` invocation:

{
  where: {
    provider_providerAccountId: {
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
      providerAccountId: "XXXXXXXXXXXXXXXX",
      provider: "google"
    },
?   id?: Int,
?   providerId?: String,
?   providerAccountId_provider?: accountProviderAccountIdProviderCompoundUniqueInput,
?   AND?: accountWhereInput | accountWhereInput[],
?   OR?: accountWhereInput[],
?   NOT?: accountWhereInput | accountWhereInput[],
?   userId?: IntFilter | Int,
?   provider?: StringFilter | String,
?   providerAccountId?: StringFilter | String,
?   refreshToken?: StringNullableFilter | String | Null,
?   accessToken?: StringNullableFilter | String | Null,
?   accessTokenExpires?: DateTimeNullableFilter | DateTime | Null,
?   createdAt?: DateTimeFilter | DateTime,
?   updatedAt?: DateTimeFilter | DateTime,
?   user?: UserRelationFilter | userWhereInput
  },
  select: {
    user: true
  }
}

Unknown argument `provider_providerAccountId`. Available options are marked with ?.

And here's my Prisma schema:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model account {
  id                       Int       @id @default(autoincrement())
  userId                   Int?      @unique(map: "userId")
  type                     String?   @db.VarChar(255)
  provider                 String?   @db.VarChar(255)
  providerAccountId        String?   @db.VarChar(255)
  refresh_token            String?   @db.Text
  access_token             String?   @db.Text
  expires_at               Int?
  token_type               String?   @db.VarChar(255)
  scope                    String?   @db.VarChar(255)
  id_token                 String?   @db.Text
  session_state            String?   @db.VarChar(255)
  refresh_token_expires_in Int?
  createdAt                DateTime? @default(now()) @db.DateTime(0)
  updatedAt                DateTime? @db.DateTime(0)
  user                     user?     @relation(fields: [userId], references: [id], onDelete: Restrict, onUpdate: Restrict, map: "account_ibfk_1")

  @@unique([providerAccountId, provider], map: "provider_providerAccountId")
}

model session {
  id           Int       @id @default(autoincrement())
  sessionToken String?   @unique(map: "sessionToken") @db.VarChar(255)
  userId       Int?
  expires      DateTime? @db.DateTime(0)
  createdAt    DateTime? @default(now()) @db.DateTime(0)
  updatedAt    DateTime? @db.DateTime(0)
  user         user?     @relation(fields: [userId], references: [id], onDelete: Restrict, onUpdate: Restrict, map: "session_ibfk_1")

  @@index([userId], map: "userId")
}

model user {
  id            Int             @id @default(autoincrement())
  name          String?         @db.VarChar(255)
  username      String?         @unique(map: "username") @db.VarChar(255)
  email         String?         @unique(map: "email") @db.VarChar(255)
  emailVerified DateTime?       @db.DateTime(0)
  role          String?         @default("User") @db.VarChar(255)
  password      String          @db.VarChar(255)
  image         String?         @db.VarChar(255)
  createdAt     DateTime?       @default(now()) @db.DateTime(0)
  updatedAt     DateTime?       @db.DateTime(0)
  account       account?
  authenticator authenticator[]
  session       session[]
}

model authenticator {
  credentialID         String   @id @db.VarChar(255)
  userId               Int?
  providerAccountId    String?  @db.VarChar(255)
  credentialPublicKey  String?  @db.VarChar(255)
  counter              Int?
  credentialDeviceType String?  @db.VarChar(255)
  credentialBackedUp   Boolean?
  transports           String?  @db.VarChar(255)
  user                 user?    @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "authenticator_ibfk_1")

  @@index([userId], map: "userId")
}

model verificationtoken {
  identifier Int       @id @default(autoincrement())
  token      String?   @db.VarChar(255)
  expires    DateTime? @db.DateTime(0)

  @@unique([identifier, token], map: "identifier")
}

I haven't modified the Prisma schema generated by npx prisma db pull, so I believe this might be an issue related to the adapter or how NextAuth interfaces with Prisma.

Thank you for your help in resolving this issue! Let me know if you need any more information.

How to reproduce

Attempt to use google signin, Here's my auth implementation:

export const { handlers, auth, signIn, signOut } = NextAuth({
  session: { strategy: 'jwt' },
  adapter: PrismaAdapter(prisma) as Adapter,
  pages: {
    signIn: '/login',
  },
  providers: [
    google,
  ],
  callbacks: {
    authorized({ auth, request: { nextUrl } }) {
      //....
    },
    jwt: ({ token, user }) => {
      //....
    },
    session(params) {
      //....
    },
  },
});

Expected behavior

The expected behavior would be for the Prisma adapter to recognize and accept the provider_providerAccountId argument in the where clause of the prisma.account.findUnique() invocation without throwing an error. This means that the Prisma adapter should handle queries with this argument properly and not suggest a different argument (providerId_providerAccountId) in the error message.

mashwishi commented 3 months ago

is there updated for this?

https://next-auth.js.org/v3/adapters/typeorm/mysql

mashwishi commented 3 months ago

I have finally solved the issue, So here's the steps I did.

  1. Update packages @auth/prisma-adapter: 1.3.3 => 1.6.0 next-auth: 5.0.0-beta.5 => 5.0.0-beta.19

    1. Changed my schema manually
      
      generator client {
      provider = "prisma-client-js"
      }

datasource db { provider = "mysql" url = env("DATABASE_URL") }

model Account { id Int @id @default(autoincrement()) userId Int type String provider String providerAccountId String refresh_token String? @db.Text access_token String? @db.Text expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? createdAt DateTime @default(now()) @db.DateTime(0) updatedAt DateTime @default(now()) @db.DateTime(0) user User @relation(fields: [userId], references: [id], onDelete: Cascade)

@@unique([provider, providerAccountId]) }

enum UserRole { Admin Moderator User }

model User { id Int @id @default(autoincrement()) name String? email String? @unique emailVerified DateTime? image String? password String? role UserRole @default(User) accounts Account[] isTwoFactorEnabled Boolean @default(false) twoFactorConfirmation TwoFactorConfirmation? createdAt DateTime @default(now()) @db.DateTime(0) updatedAt DateTime @default(now()) @db.DateTime(0) }

model VerificationToken { id Int @id @default(autoincrement()) email String token String @unique expires DateTime

@@unique([email, token]) }

model ResetPasswordToken { id Int @id @default(autoincrement()) email String token String @unique expires DateTime

@@unique([email, token]) }

model TwoFactorToken { id Int @id @default(autoincrement()) email String token String @unique expires DateTime

@@unique([email, token]) }

model TwoFactorConfirmation { id Int @id @default(autoincrement()) userId Int @unique expires DateTime user User @relation(fields: [userId], references: [id], onDelete: Cascade) }


 3. Run `npx prisma db push` to push my schema to database, but make sure your database is empty or new, no tables and other existing data because we are going to push this schema.

 4. Run `npx prisma generate`, and after that run the project.