Files
Henry_Du 4ce4649aaf
Build VoiceHub FPK / build-fpk (push) Failing after 56m5s
Build and Push Docker Image / build-and-push (push) Canceled after 1h9m35s
初次提交
2026-09-06 08:19:02 +08:00

88 lines
2.2 KiB
TypeScript

import CryptoJS from 'crypto-js'
import { randomBytes } from 'node:crypto'
const SECRET_KEY = process.env.OAUTH_STATE_SECRET
if (!SECRET_KEY) {
throw new Error('OAUTH_STATE_SECRET environment variable is required')
}
export interface OAuthState {
target: string
csrf: string
timestamp: number
provider?: string
}
// 生成 OAuth 状态参数
export const generateState = (
targetOrigin: string,
provider?: string
): { state: string; csrf: string } => {
const csrf = randomBytes(32).toString('hex')
const payload: OAuthState = {
target: targetOrigin,
csrf,
timestamp: Date.now(),
provider
}
const json = JSON.stringify(payload)
const state = CryptoJS.AES.encrypt(json, SECRET_KEY).toString()
return { state, csrf }
}
// 解析 OAuth 状态参数
export const parseState = (
stateStr: string,
expectedOrigin?: string,
expectedCsrf?: string
): OAuthState | null => {
try {
const bytes = CryptoJS.AES.decrypt(stateStr, SECRET_KEY)
const json = bytes.toString(CryptoJS.enc.Utf8)
if (!json) return null
const payload = JSON.parse(json)
// 检查时间戳是否过期(例如:10分钟)
if (Date.now() - payload.timestamp > 10 * 60 * 1000) {
console.error('OAuth state expired')
return null
}
// 验证 target origin
if (expectedOrigin && payload.target !== expectedOrigin) {
console.error('OAuth state target mismatch')
return null
}
// 验证 CSRF
if (!expectedCsrf || payload.csrf !== expectedCsrf) {
console.error('OAuth state CSRF mismatch or missing cookie')
return null
}
return payload
} catch (e) {
console.error('Failed to parse OAuth state', e)
return null
}
}
export const getRedirectUri = (provider: string): string => {
let redirectUri = process.env.OAUTH_REDIRECT_URI
if (!redirectUri) {
throw createError({ statusCode: 500, message: 'OAUTH_REDIRECT_URI not configured' })
}
// 支持 [provider] 占位符
redirectUri = redirectUri.replace('[provider]', provider)
// 兼容用户可能错误地将 "provider" 作为字面量填写的情况
if (redirectUri.includes('/provider/callback')) {
redirectUri = redirectUri.replace('/provider/callback', `/${provider}/callback`)
}
return redirectUri
}