Files
pda-uniapp/api/logout-session.js
T

34 lines
2.3 KiB
JavaScript

import { logout, refresh, getPendingRefresh } from './auth.js'
import { session, signOut, getSessionRevision, decodeAccessToken } from '../stores/session.js'
function replacement(captured, tokens) {
if (!tokens?.accessToken || typeof tokens.refreshToken !== 'string' || !tokens.refreshToken.trim()) throw new Error('退出刷新响应缺少会话凭据')
const previous = decodeAccessToken(captured.accessToken), next = decodeAccessToken(tokens.accessToken)
if (!Number.isFinite(Number(next.exp)) || Number(next.exp) * 1000 <= Date.now()) throw new Error('退出刷新凭据无效或已过期')
for (const field of ['user_id', 'membership_id', 'tenant_id', 'app_code']) {
if (previous[field] == null || String(previous[field]) !== String(next[field])) throw new Error('退出刷新返回了不同作业主体,无法确认原会话撤销')
}
if (String(previous.domestic_warehouse_id ?? '') !== String(next.domestic_warehouse_id ?? '')) throw new Error('退出刷新返回了不同作业仓库,无法确认原会话撤销')
return { accessToken: tokens.accessToken, refreshToken: tokens.refreshToken }
}
export function logoutCurrentSession() {
const captured = { accessToken: session.token, refreshToken: session.refreshToken, pendingRefresh: getPendingRefresh(session.refreshToken) }
// Clear sensitive data and invalidate business requests while revocation is pending.
signOut()
const clearedRevision = getSessionRevision()
const revoke = async () => {
if (!captured.accessToken && !captured.refreshToken) return { revoked: false, reason: 'NO_CAPTURED_SESSION' }
if (!captured.accessToken || !captured.refreshToken) throw new Error('原会话凭据不完整,服务端退出未确认')
let credentials = captured, refreshed = false
if (captured.pendingRefresh) { refreshed = true; credentials = replacement(captured, await captured.pendingRefresh) }
try { await logout(credentials.refreshToken, credentials.accessToken) }
catch (error) {
if (error.network || error.statusCode !== 401 || refreshed) throw error
credentials = replacement(captured, await refresh(captured.refreshToken))
await logout(credentials.refreshToken, credentials.accessToken)
}
return { revoked: true }
}
return revoke().then(result => ({ ...result, clearedRevision }), error => { error.logoutRevision = clearedRevision; throw error })
}