shared/lib/auth/jwt-v2.ts

61 lines
2.2 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.

// shared/lib/auth/jwt-v2.ts — V2 JWT 签发/验证(步骤 1a 新增,旧 jwt.ts 不动)
// 与 V1 的关键差异signJwtV2 强制注入 issverifyJwtV2 强制校验 iss 匹配
import crypto from 'crypto'
import type { SessionPayloadV2 } from './types-v2'
interface SignJwtV2Options {
secret: string
payload: { username: string; displayName?: string }
/** 签发者标识OA LDAP 用 "oa.tlyq.ai"OIDC 用 autheliaUrl */
iss: string
expiresInSeconds?: number
}
/** 签发 JWTV2强制注入 iss */
export function signJwtV2({ secret, payload, iss, expiresInSeconds = 604800 }: SignJwtV2Options): string {
const header = { alg: 'HS256', typ: 'JWT' }
const now = Math.floor(Date.now() / 1000)
const body = { ...payload, iss, iat: now, exp: now + expiresInSeconds }
const encoded = (obj: object) => Buffer.from(JSON.stringify(obj)).toString('base64url')
const signature = crypto
.createHmac('sha256', secret)
.update(`${encoded(header)}.${encoded(body)}`)
.digest('base64url')
return `${encoded(header)}.${encoded(body)}.${signature}`
}
/** 验证 JWTV23 参数强制校验 iss 匹配 */
export function verifyJwtV2(token: string, secret: string, expectedIss: string): SessionPayloadV2 | null {
try {
const parts = token.split('.')
if (parts.length !== 3) return null
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString()) as SessionPayloadV2
if (payload.exp && payload.exp * 1000 < Date.now()) return null
if (payload.iss !== expectedIss) return null
const signature = crypto
.createHmac('sha256', secret)
.update(`${parts[0]}.${parts[1]}`)
.digest('base64url')
return signature === parts[2] ? payload : null
} catch {
return null
}
}
/** 从 token 解码提取 iss不验签用于 Layer 2/3 自引用校验:先提取 iss → 再 verifyJwtV2 */
export function extractIss(token: string): string | null {
try {
const parts = token.split('.')
if (parts.length !== 3) return null
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString()) as SessionPayloadV2
return payload.iss || null
} catch {
return null
}
}