diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index a100081..4e205c7 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -1,16 +1,18 @@ // GET + POST /api/auth/logout — 退出登录(清除 cookie + 302 跳转 /login) import { NextResponse, type NextRequest } from 'next/server' -const autheliaUrl = process.env.AUTHELIA_URL || 'https://sso.tlyq.ai' const cookieDomain = process.env.COOKIE_DOMAIN || '.tlyq.ai' +/** 从 OIDC_REDIRECT_URI 提取 site URL(不可信请求头,见 LESSONS-LEARNED #51) */ +function getSiteUrl(): string { + const redirectUri = process.env.OIDC_REDIRECT_URI || '' + try { const u = new URL(redirectUri); return `${u.protocol}//${u.host}` } catch { /* fallthrough */ } + return process.env.NEXT_PUBLIC_SITE_URL || 'https://oa.tlyq.ai' +} + /** 清除 tlyq_session + session cookie → 302 跳转 /login */ -function logoutResponse(request: NextRequest): NextResponse { - // 使用请求头获取真实域名(nginx 反向代理后 nextUrl.origin 是容器内部地址) - const host = request.headers.get('x-forwarded-host') || request.headers.get('host') || 'localhost' - const proto = request.headers.get('x-forwarded-proto') || 'https' - const baseUrl = `${proto}://${host}` - const response = NextResponse.redirect(new URL('/login', baseUrl)) +function logoutResponse(): NextResponse { + const response = NextResponse.redirect(new URL('/login', getSiteUrl())) response.cookies.set('tlyq_session', '', { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', domain: cookieDomain, path: '/', maxAge: 0, @@ -19,5 +21,5 @@ function logoutResponse(request: NextRequest): NextResponse { return response } -export async function GET(request: NextRequest) { return logoutResponse(request) } -export async function POST(request: NextRequest) { return logoutResponse(request) } +export async function GET() { return logoutResponse() } +export async function POST() { return logoutResponse() }