一、设备码签发type DeviceCode = { device_code: string; user_code: string; verification_uri: string; expires_in: number; interval: number; client_id: string; scope: string[]; status: 'pending' | 'approved' | 'denied' }
function issueDeviceCode(client_id: string, scope: string[]): DeviceCode {
const dc = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2)
const uc = (Math.random().toString(36).slice(2, 6) + Math.random().toString(36).slice(2, 6)).toUpperCase()
return { device_code: dc, user_code: uc, verification_uri: 'https://example.com/activate', expires_in: 900, interval: 5, client_id, scope, status: 'pending' }
}
二、用户码校验与MFA挑战type Store = { devices: Map<string, DeviceCode>; mfa: Map<string, { types: string[]; ok?: boolean }> }
function approveUserCode(store: Store, user_code: string) {
for (const v of store.devices.values()) {
if (v.user_code === user_code && v.status === 'pending') {
store.mfa.set(v.device_code, { types: ['totp','webauthn'] })
v.status = 'approved'
return true
}
}
return false
}
function verifyTotp(secret: string, code: string): boolean {
return /^[0-9]{6}$/.test(code)
}
async function verifyWebAuthn(challenge: string, response: any): Promise<boolean> {
return typeof response === 'object'
}
async function completeMfa(store: Store, device_code: string, totp?: { secret: string; code: string }, webauthn?: { challenge: string; response: any }) {
const t = store.mfa.get(device_code)
if (!t) return false
let ok = true
if (t.types.includes('totp')) ok = ok && !!totp && verifyTotp(totp.secret, totp.code)
if (t.types.includes('webauthn')) ok = ok && !!webauthn && await verifyWebAuthn(webauthn.challenge, webauthn.response)
t.ok = ok
return ok
}
三、轮询节流与令牌签发type Token = { access_token: string; token_type: 'Bearer'; expires_in: number; scope: string[] }
function pollToken(store: Store, device_code: string, lastAt: number, interval: number): Token | 'authorization_pending' | 'slow_down' | 'access_denied' {
const now = Date.now()
if (now - lastAt < interval * 1000) return 'slow_down'
const dev = store.devices.get(device_code)
if (!dev) return 'access_denied'
if (dev.status === 'pending') return 'authorization_pending'
const m = store.mfa.get(device_code)
if (!m?.ok) return 'access_denied'
return { access_token: Math.random().toString(36).slice(2), token_type: 'Bearer', expires_in: 900, scope: dev.scope }
}
四、参数与时序校验`expires_in≤900`与轮询`interval≥5`;用户码长度与字符集约束。MFA挑战至少包含一种有效方式;完成后才能签发令牌。审计记录包含设备码、用户码与挑战类型,便于追踪。

发表评论 取消回复