组合next-intl与next-auth中间件出现ERR_TOO_MANY_REDIRECTS问题求助
next-intl 结合 next-auth 引发无限循环问题
背景说明
我的实现基于next-intl官方的next-auth示例项目,针对业务需求做了以下调整:
- 无任何未受保护的页面
- 用户访问首页时自动重定向到授权提供商页面
- 登出操作需通过授权提供商完成
项目文件夹结构

相关代码
middleware.ts
import { NextRequestWithAuth, withAuth } from 'next-auth/middleware'; import createIntlMiddleware from 'next-intl/middleware'; import { NextRequest, NextResponse } from 'next/server'; const locales = ['bg', 'en']; const intlMiddleware = createIntlMiddleware({ locales, defaultLocale: 'bg', }); const authMiddleware = withAuth( (req: NextRequestWithAuth) => { const now: any = new Date(); const timestamp = Math.round(now / 1000); const { expiresAt }: any = req.nextauth.token; if (timestamp > expiresAt) { if (!req.nextUrl.pathname.startsWith('/auth')) { const url = '/auth/signout'; return NextResponse.redirect(new URL(url, req.nextUrl)); } } // 此处触发多次重定向 return intlMiddleware(req); }, { pages: { signIn: '/auth/signin', error: '/auth/error', signOut: '/auth/signout', }, }, ); export default function middleware(req: NextRequest) { return (authMiddleware as any)(req); } export const config = { matcher: ['/((?!api|_next|.*\\..*).*)'], };
app/layout.tsx
'use client'; import React from 'react'; import { SessionProvider } from 'next-auth/react'; import { BodyStyled } from '@/global/styles'; type RootLayoutTypes = { children: React.ReactNode, params: any } export default function RootLayout({ children, params: { locale } }: RootLayoutTypes) { return ( <html lang={locale}> <BodyStyled> <SessionProvider> {children} </SessionProvider> </BodyStyled> </html> ); }
app/[locale]/layout.tsx
import React from 'react'; import { NextIntlClientProvider } from 'next-intl'; import bg from '@/public/locales/bg/common.json'; import en from '@/public/locales/en/common.json'; type RootLayoutTypes = { children: React.ReactNode, params: any } export function generateStaticParams() { return [{ locale: 'bg' }, { locale: 'en' }]; } export default function RootLayout({ children, params: { locale } }: RootLayoutTypes) { return ( <NextIntlClientProvider locale={locale} messages={locale === 'bg' ? bg : en}> {children} </NextIntlClientProvider> ); }
app/api/auth/[...nextauth]/route.ts
/* eslint-disable no-param-reassign */ import NextAuth, { NextAuthOptions } from 'next-auth'; import jwtDecode from 'jwt-decode'; const authOptions: NextAuthOptions = { providers: [ { wellKnown: `${process.env.NEXT_PUBLIC_AUTH_AUTHORITY}/.well-known/openid-configuration`, authorization: { params: { scope: 'openid profile' }, }, type: 'oauth', id: 'customOauth', clientId: process.env.NEXT_PUBLIC_AUTH_CLIENT_ID, clientSecret: process.env.NEXT_PUBLIC_AUTH_CLIENT_SECRET, name: '', checks: ['pkce', 'state'], profile(profile) { return { id: profile.sub, permissions: profile.permissions, }; }, }, ], secret: process.env.NEXTAUTH_SECRET, session: { strategy: 'jwt', }, callbacks: { async redirect() { return '/dashboard'; }, async jwt({ token, account }) { // 登录后将OAuth访问令牌和用户ID持久化到token中 if (account) { const { exp }: any = jwtDecode(account.id_token || ''); token.accessToken = account.access_token; token.idToken = account.id_token; token.expiresAt = exp; } return token; }, }, pages: { signIn: '/auth/signin', }, debug: true, }; const handler = NextAuth(authOptions); export { handler as GET, handler as POST }
app/auth/signin/page.tsx
'use client'; import React, { useEffect } from 'react'; import { signIn, useSession } from 'next-auth/react'; import { useRouter } from 'next/navigation'; interface LoginProps { } const Login: React.FC<LoginProps> = () => { const router = useRouter(); const { status } = useSession(); useEffect(() => { if (status === 'unauthenticated') { const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); if (urlParams.get('error')) { void router.push('/auth/error'); } else { void signIn('customOauth', undefined, { prompt: 'select_account' }); } } else if (status === 'authenticated') { void router.push('/dashboard'); } }, [status, router]); return null; }; export default Login;
我遗漏了什么?已经卡了三周,希望能解决这个问题...
内容的提问来源于stack exchange,提问作者Chris Panayotova
相关产品推荐
相关产品推荐

