NextAuthでOAUTH_CALLBACK_ERRORが発生し、OAuthのコールバックに失敗する
課題
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で解決策が議論されています。
結論
新しいドキュメントを参考にスキーマを作成し直せば解決する
詳細
私は、新しい 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