82 lines
2.3 KiB
Plaintext
82 lines
2.3 KiB
Plaintext
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 cookie(5 分钟过期)
|
||
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
|
||
}
|