Firebase部署后Next.js+Clerk认证重定向异常求助
问题现象
Next.js项目部署到Firebase App Hosting生产环境后,其他功能正常,但认证模块出现异常:本地开发环境无问题,生产环境创建账号并登录后,被重定向至https://mydomain/sign-in?redirect_url=https%3A%2F%2F0.0.0.0%3A8080%2Fdashboard,陷入登录页面循环。
报错信息:
clerk.browser.js:2 Clerk: Redirect URL https://0.0.0.0:8080/dashboard is not on one of the allowedRedirectOrigins, falling back to the default redirect URL.
已配置内容
环境变量
- variable: NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL value: "/dashboard" - variable: NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL value: "/membercard/onboarding" - variable: NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL value: "/dashboard" - variable: NEXT_PUBLIC_CLERK_SIGN_IN_URL value: "/sign-in" - variable: NEXT_PUBLIC_CLERK_SIGN_UP_URL value: "/sign-up" - variable: NEXT_PUBLIC_CLERK_SIGN_IN_URL value: "/sign-in"
Middleware代码
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' import { NextRequest, NextResponse } from 'next/server'; const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)','/','/api/webhooks(.*)','/api/wallet(.*)','/membercard/profiles(.*)']); const isOnboardingRoute = createRouteMatcher(['/membercard/onboarding']); const isAdminRoute = createRouteMatcher(['/admin(.*)']); // New route matcher for admin routes export default clerkMiddleware((auth, req: NextRequest) => { const { userId, sessionClaims, redirectToSignIn } = auth(); console.log("Middleware triggered for URL:", req.url); // Check for admin routes if (isAdminRoute(req)) { if (!userId) { return redirectToSignIn({ returnBackUrl: req.url }); } if (sessionClaims?.metadata?.role !== 'admin') { return NextResponse.redirect(new URL('/dashboard', req.url)); } } if (userId && isOnboardingRoute(req)) { console.log("User is authenticated and on onboarding route."); return NextResponse.next(); } if (!userId && !isPublicRoute(req)) { console.log("User is not authenticated and route is not public. Redirecting to sign-in."); const redirectUrl = new URL(req.url, process.env.NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL); console.log("Redirect URL:", redirectUrl); return redirectToSignIn({ returnBackUrl: redirectUrl.toString() }); } if (userId && !sessionClaims?.metadata?.onboardingComplete) { const onboardingUrl = new URL("/membercard/onboarding", req.url); return NextResponse.redirect(onboardingUrl); } if (userId && !isPublicRoute(req)) return NextResponse.next(); console.log("User is authenticated and route is protected. Allowing access."); return NextResponse.next(); }); export const config = { matcher: ['/((?!.*\..*|_next).*)', '/', '/(api|trpc)(.*)'], };
Clerk支持团队回复
该报错表明存在网络问题,可能是K8s、Docker或Firebase Hosting的网络控制导致,建议在请求头中转发主机域名。同时请确认已在Firebase变量中配置生产环境密钥。
云控制台日志
2024-08-13 07:31:04.796 CEST User is not authenticated and route is not public. Redirecting to sign-in. 2024-08-13 07:31:04.796 CEST Redirect URL: URL { } 2024-08-13 07:31:04.949 CEST GET307378 B9 msGoogle https://t-4011972723---enigmamembers-backend-fxedffxlpa-uc.a.run.app/dashboard?_rsc=2qkwf 2024-08-13 07:31:04.957 CEST Middleware triggered for URL: https://0.0.0.0:8080/dashboard 2024-08-13 07:31:04.959 CEST User is not authenticated and route is not public. Redirecting to sign-in. 2024-08-13 07:31:04.959 CEST Redirect URL: URL { } 2024-08-13 07:31:05.542 CEST GET200640 B41 msGoogle https://t-4011972723---enigmamembers-backend-fxedffxlpa-uc.a.run.app/sign-in?redirect_url=https%3A%2F%2F0.0.0.0%3A8080%2Fmembercard 2024-08-13 07:31:05.544 CEST GET2001.1 KB42 msGoogle https://t-4011972723---enigmamembers-backend-fxedffxlpa-uc.a.run.app/sign-in?redirect_url=https%3A%2F%2F0.0.0.0%3A8080%2Fdashboard
解决方案
1. 修复Middleware中的RedirectUrl构建错误
从日志可见redirectUrl输出为空,说明process.env.NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL未正确配置,且new URL(req.url, base)的用法逻辑颠倒(正确应为new URL(path, base))。修改对应代码:
if (!userId && !isPublicRoute(req)) { console.log("User is not authenticated and route is not public. Redirecting to sign-in."); // 直接使用当前请求URL作为返回地址,避免环境变量依赖错误 return redirectToSignIn({ returnBackUrl: req.url }); }
若需使用环境变量,确保NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL为生产域名(如https://mydomain.com),并正确构建URL:
const baseUrl = process.env.NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL || req.nextUrl.origin; const redirectUrl = new URL(req.nextUrl.pathname, baseUrl); return redirectToSignIn({ returnBackUrl: redirectUrl.toString() });
2. 配置Firebase Hosting转发主机头
Firebase Hosting代理请求时可能修改主机头,导致Clerk无法获取正确域名。在firebase.json中添加以下配置:
{ "hosting": { "headers": [ { "source": "**", "headers": [ { "key": "X-Forwarded-Host", "value": "$host" }, { "key": "Host", "value": "$host" } ] } ] } }
3. 确认Clerk控制台的Allowed Redirect Origins
在Clerk控制台Authentication > Settings > Redirects中,添加生产环境完整域名(如https://mydomain.com)及Firebase云运行实例域名(如日志中的https://t-4011972723---enigmamembers-backend-fxedffxlpa-uc.a.run.app)。
4. 清理冗余环境变量
删除重复定义的NEXT_PUBLIC_CLERK_SIGN_IN_URL,确保所有变量值无多余引号。
5. 检查Clerk Provider配置
在app/layout.tsx或pages/_app.tsx中,确保<ClerkProvider>的domain属性设置为生产域名:
<ClerkProvider domain="https://mydomain.com"> {/* 应用内容 */} </ClerkProvider>
内容的提问来源于stack exchange,提问作者Jan

