oa-ai/src/app/api/auth/login/oidc/route.ts

82 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'
import { getOidcClient, generatePKCE, generateState, generateNonce } from '@/lib/oidc'
import { verifySharedJwt } from '@/lib/jwt'
export async function GET(request: Request) {
const cookieStore = await cookies()
const existingSession = cookieStore.get('tlyq_session')?.value
const url = new URL(request.url)
const switchUser = url.searchParams.get('switch') === '1'
// 检查是否已有登录用户
if (existingSession && !switchUser) {
const existing = verifySharedJwt(existingSession)
if (existing) {
return NextResponse.json({
conflict: true,
currentUser: existing.username,
displayName: existing.displayName,
})
}
}
// 预检 Authelia 健康状态
const autheliaUrl = process.env.AUTHELIA_URL || 'https://sso.tlyq.ai'
try {
const healthRes = await fetch(`${autheliaUrl}/api/health`, {
signal: AbortSignal.timeout(5000),
})
if (!healthRes.ok) {
return NextResponse.json({ fallback: 'ldap', error: 'Authelia 不可用' }, { status: 503 })
}
} catch {
return NextResponse.json({ fallback: 'ldap', error: 'Authelia 不可达' }, { status: 503 })
}
// 生成 PKCE 参数
const { codeVerifier, codeChallenge } = generatePKCE()
const state = generateState()
const nonce = generateNonce()
// 构建授权 URL
const client = await getOidcClient()
const authorizationUrl = client.authorizationUrl({
scope: 'openid profile email',
state,
nonce,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
...(switchUser && { prompt: 'login' }),
})
// 存储到 httpOnly cookie5 分钟过期)
const response = NextResponse.redirect(authorizationUrl)
response.cookies.set('oidc_code_verifier', codeVerifier, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 300,
path: '/',
})
response.cookies.set('oidc_state', state, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 300,
path: '/',
})
response.cookies.set('oidc_nonce', nonce, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 300,
path: '/',
})
return response
}