😇

NextAuthでOAUTH_CALLBACK_ERRORが発生し、OAuthのコールバックに失敗する

2024/11/18に公開

課題

Next.jsでNextAuthを利用した認証処理を実装した際に、コールバックエラーが発生した。
OAuth認証でGithub認証を実装していたが、Github側での認証は成功している。
しかし、コールバック時にエラーが発生してている。
エラー内容は下記になる。

https://next-auth.js.org/errors#oauth_callback_error The "payload" argument must be of type object. Received null

エラーログを読み進めていくと、下記が記載されていた。

Unknown argument `provider_providerAccountId`. Did you mean `providerId_providerAccountId`? Available options are marked with ?.

oauth_callback_errorについては、下記のように説明されています。

code_verifierこれは、Cookie が見つからなかった場合、または OAuth プロバイダーから無効な状態が返された場合に、コールバックの処理中に発生する可能性があります。

要点

  • githubとのOAuth認証自体は成功している
  • コールバック時にoauth_callback_errorで失敗している

この課題を解決して、コールバックを成功させたい。

環境

next 15.0.3
next-auth 4.24.10
prisma 5.22.0

対処方法

NextAuthのDiscussionsで解決策が議論されています。
https://github.com/nextauthjs/next-auth/discussions/4653

結論

新しいドキュメントを参考にスキーマを作成し直せば解決する
https://authjs.dev/reference/adapter/prisma#prismaadapter

詳細

私は、新しい authjs/prisma-adapter で next-auth ドキュメントのテンプレート スキーマを使用していたときに、この問題に遭遇しました。スキーマには若干の変更があり、新しいドキュメントのテンプレートを使用したら、すべて期待どおりに動作しました。
新しいドキュメント: https://authjs.dev/reference/adapter/prisma#prismaadapter
古いドキュメント: https://next-auth.js.org/v3/adapters/prisma

ここで記載されている通り、v3とv4で微妙にセットアップ手順が異なります。

現在のversionがv4なのに、v3のドキュメントを参考にしてしまうとこのような状況にいたります。
なぜドキュメントを間違えるかといえば、Google検索で「NextAuth Prisma」で検索するとv3の記事がヒットしてしまうからです。

v3のスキーマ構成ではproviderIdがunique指定されています


model Account {
  id                 String    @id @default(cuid())
  ...
  user               User      @relation(fields: [userId], references: [id])

  @@unique([providerId, providerAccountId])
}

しかし、v4ではproviderがunique指定されています。

model Account {
  id                 String  @id @default(cuid())
  ...
  provider           String
  providerAccountId  String  @map("provider_account_id")
  ...
 
  @@unique([provider, providerAccountId])
  @@map("accounts")
}

この差異により、エラーが生じていたということです

Unknown argument `provider_providerAccountId`. Did you mean `providerId_providerAccountId`? Available options are marked with ?.

V4のドキュメントを参考に、下記のように修正すれば改善されるはずです。

model Account {
  id                 String  @id @default(cuid())
  userId             String  @map("user_id")
  type               String
  provider           String
  providerAccountId  String  @map("provider_account_id")
  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?
 
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
 
  @@unique([provider, providerAccountId])
  @@map("accounts")
}
 
model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique @map("session_token")
  userId       String   @map("user_id")
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
 
  @@map("sessions")
}
 
model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime? @map("email_verified")
  image         String?
  accounts      Account[]
  sessions      Session[]
 
  @@map("users")
}
 
model VerificationToken {
  identifier String
  token      String
  expires    DateTime
 
  @@unique([identifier, token])
  @@map("verification_tokens")
}

Discussion