Next Auth自定义OIDC Provider的nonce校验失败问题求助
问题描述
我正在使用要求提供nonce的IDP(FranceConnect),Next Auth配置如下(已在authorization步骤传入自行生成的nonce):
import NextAuth, { NextAuthOptions } from 'next-auth' const randomString = (length: number) => { let text = '' let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' for (let i = 0; i < length; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)) } return text } const nonce = `nonce${randomString(32)}` const authOptions: NextAuthOptions = { providers: [ { issuer: 'https://fcp.integ01.dev-franceconnect.fr', id: 'franceconnect', clientSecret: process.env.FRANCE_CONNECT_SECRET || 'undefined', clientId: process.env.FRANCE_CONNECT_ID || 'undefined', name: 'FranceConnect', type: 'oauth', idToken: true, client: { authorization_signed_response_alg: 'HS256', id_token_signed_response_alg: 'HS256' }, authorization: { url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize', params: { scope: 'openid given_name gender', nonce, redirect_uri: `http://localhost:3000/api/auth/callback/franceconnect`, }, }, token:`https://fcp.integ01.dev-franceconnect.fr/api/v1/token`, userinfo: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo', profile(profile) { console.log(profile) return profile }, }, ], debug: true, secret: 'hdh-secret', callbacks: { async jwt({ token, account }) { return token }, async session({ session, token, user }) { return session }, }, } export default NextAuth(authOptions)
出现的错误
[next-auth][error][CALLBACK_OAUTH_ERROR] https://next-auth.js.org/errors#callback_oauth_error nonce mismatch, expected undefined, got: nonceZDBoVu2bD1rRESxh7y4kgZ76A6NiP22e RPError: nonce mismatch, expected undefined, got: nonceZDBoVu2bD1rRESxh7y4kgZ76A6NiP22e at Client.validateIdToken (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\openid-client\\lib\\client.js:784:13) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Client.callback (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\openid-client\\lib\\client.js:487:7) at async oAuthCallback (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\next-auth\\core\\lib\\oauth\\callback.js:114:16) at async Object.callback (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\next-auth\\core\\routes\\callback.js:50:11) at async NextAuthHandler (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\next-auth\\core\\index.js:186:28) at async NextAuthNextHandler (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\next-auth\\next\\index.js:23:19) at async C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\next-auth\\next\\index.js:59:32 at async Object.apiResolver (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\next\\dist\\server\\api-utils\\node.js:179:9) at async DevServer.runApi (C:\\Users\\Shadow\\Documents\\Projets\\HDH\\front\\node_modules\\next\\dist\\server\\next-server.js:381:9) { name: 'OAuthCallbackError', code: undefined }
若移除nonce参数,IDP会返回错误:
{"status":"fail","message":"The following fields are missing or empty : nonce"}
解决方案
方案1:改用OIDC类型Provider(推荐)
FranceConnect是标准OpenID Connect服务,将Provider的type设为oidc后,NextAuth会自动处理nonce的生成、传递和验证,无需手动配置:
import NextAuth, { NextAuthOptions } from 'next-auth' const authOptions: NextAuthOptions = { providers: [ { id: 'franceconnect', name: 'FranceConnect', type: 'oidc', issuer: 'https://fcp.integ01.dev-franceconnect.fr', clientId: process.env.FRANCE_CONNECT_ID || 'undefined', clientSecret: process.env.FRANCE_CONNECT_SECRET || 'undefined', client: { authorization_signed_response_alg: 'HS256', id_token_signed_response_alg: 'HS256' }, scope: 'openid given_name gender', redirectUri: 'http://localhost:3000/api/auth/callback/franceconnect', profile(profile) { console.log(profile) return profile }, }, ], debug: true, secret: 'hdh-secret', callbacks: { async jwt({ token, account }) { return token }, async session({ session, token, user }) { return session }, }, } export default NextAuth(authOptions)
方案2:手动动态生成nonce(不推荐)
如果必须手动控制nonce,需要确保每个请求的nonce唯一,且让NextAuth能跟踪验证:
import NextAuth, { NextAuthOptions } from 'next-auth' import { randomBytes } from 'crypto' const authOptions: NextAuthOptions = { providers: [ { issuer: 'https://fcp.integ01.dev-franceconnect.fr', id: 'franceconnect', clientSecret: process.env.FRANCE_CONNECT_SECRET || 'undefined', clientId: process.env.FRANCE_CONNECT_ID || 'undefined', name: 'FranceConnect', type: 'oauth', idToken: true, client: { authorization_signed_response_alg: 'HS256', id_token_signed_response_alg: 'HS256' }, // 用函数动态生成授权参数,每个请求生成唯一nonce authorizationParams: () => { const nonce = `nonce${randomBytes(16).toString('hex')}` return { scope: 'openid given_name gender', nonce, redirect_uri: `http://localhost:3000/api/auth/callback/franceconnect`, } }, token:`https://fcp.integ01.dev-franceconnect.fr/api/v1/token`, userinfo: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo', profile(profile) { console.log(profile) return profile }, }, ], debug: true, secret: 'hdh-secret', callbacks: { async jwt({ token, account, profile }) { return token }, async session({ session, token, user }) { return session }, }, } export default NextAuth(authOptions)
内容的提问来源于stack exchange,提问作者Valentine
相关产品推荐
相关产品推荐

