feat(auth): add opaque bearer sessions

This commit is contained in:
caoxiaozhu
2026-07-13 14:45:36 +08:00
parent 661990b27b
commit 653eda0596
59 changed files with 1408 additions and 408 deletions

View File

@@ -45,9 +45,11 @@ async function testSupportsBlobResponses() {
assert.equal(payload, blob)
}
async function testInjectsAuthenticatedUserHeaders() {
const sessionStorage = new Map([
[
async function testInjectsBearerTokenWithoutUserControlledIdentityHeaders() {
const sessionStorage = new Map([
['x-financial-auth-access-token', 'opaque-access-token'],
['x-financial-auth-expires-at', '2099-01-01T00:00:00.000Z'],
[
'x-financial-auth-user',
JSON.stringify({
username: 'admin',
@@ -84,27 +86,16 @@ async function testInjectsAuthenticatedUserHeaders() {
await apiRequest('/knowledge/library')
assert.equal(capturedOptions.headers['x-auth-username'], 'admin')
assert.equal(capturedOptions.headers['x-auth-name'], 'Admin User')
assert.equal(capturedOptions.headers['x-auth-position'], 'System Manager')
assert.equal(capturedOptions.headers['x-auth-grade'], 'M5')
assert.equal(capturedOptions.headers['x-auth-employee-no'], 'E-001')
assert.equal(capturedOptions.headers['x-auth-manager-name'], 'Approver User')
assert.equal(capturedOptions.headers['x-auth-role-codes'], 'manager')
assert.equal(capturedOptions.headers['x-auth-is-admin'], 'true')
assert.equal(capturedOptions.headers.Authorization, 'Bearer opaque-access-token')
assert.equal(capturedOptions.headers['x-auth-username'], undefined)
assert.equal(capturedOptions.headers['x-auth-role-codes'], undefined)
assert.equal(capturedOptions.headers['x-auth-is-admin'], undefined)
}
async function testInjectsLegacyAdminHeaderFromSnakeCaseFlag() {
async function testLoginCanDisableBearerInjection() {
const sessionStorage = new Map([
[
'x-financial-auth-user',
JSON.stringify({
username: 'superadmin',
name: 'superadmin',
roleCodes: ['manager'],
is_admin: true
})
]
['x-financial-auth-access-token', 'stale-token'],
['x-financial-auth-expires-at', '2099-01-01T00:00:00.000Z']
])
global.window = {
@@ -127,11 +118,74 @@ async function testInjectsLegacyAdminHeaderFromSnakeCaseFlag() {
}
}
await apiRequest('/reimbursements/claims/demo', { method: 'DELETE' })
await apiRequest('/auth/login', {
auth: false,
handleUnauthorized: false,
method: 'POST',
body: '{}'
})
assert.equal(capturedOptions.headers['x-auth-username'], 'superadmin')
assert.equal(capturedOptions.headers['x-auth-role-codes'], 'manager')
assert.equal(capturedOptions.headers['x-auth-is-admin'], 'true')
assert.equal(capturedOptions.headers.Authorization, undefined)
}
async function testRejectsCustomAuthorizationOverride() {
const sessionStorage = new Map([
['x-financial-auth-access-token', 'server-issued-token'],
['x-financial-auth-expires-at', '2099-01-01T00:00:00.000Z']
])
global.window = {
sessionStorage: {
getItem(key) {
return sessionStorage.get(key) ?? null
}
}
}
let capturedOptions = null
global.fetch = async (_url, options) => {
capturedOptions = options
return { ok: true, async json() { return { ok: true } } }
}
await apiRequest('/knowledge/library', {
headers: { Authorization: 'Bearer attacker-token' }
})
assert.equal(capturedOptions.headers.Authorization, 'Bearer server-issued-token')
}
async function testUnauthorizedResponsePublishesSessionExpiredEvent() {
const sessionStorage = new Map([
['x-financial-auth-access-token', 'expired-token'],
['x-financial-auth-expires-at', '2099-01-01T00:00:00.000Z']
])
const events = []
global.window = {
sessionStorage: {
getItem(key) {
return sessionStorage.get(key) ?? null
}
},
dispatchEvent(event) {
events.push(event.type)
}
}
global.fetch = async () => ({
ok: false,
status: 401,
async json() {
return { detail: '登录会话已失效。' }
}
})
await assert.rejects(
() => apiRequest('/auth/me'),
(error) => {
assert.equal(error.status, 401)
assert.equal(error.code, 'AUTH_SESSION_EXPIRED')
return true
}
)
assert.deepEqual(events, ['x-financial:auth-expired'])
}
async function testFormatsValidationErrors() {
@@ -192,8 +246,10 @@ async function testRejectsWithCustomTimeoutMessage() {
async function run() {
await testUsesCustomContentTypeHeader()
await testSupportsBlobResponses()
await testInjectsAuthenticatedUserHeaders()
await testInjectsLegacyAdminHeaderFromSnakeCaseFlag()
await testInjectsBearerTokenWithoutUserControlledIdentityHeaders()
await testLoginCanDisableBearerInjection()
await testRejectsCustomAuthorizationOverride()
await testUnauthorizedResponsePublishesSessionExpiredEvent()
await testFormatsValidationErrors()
await testRejectsWithCustomTimeoutMessage()
console.log('api-request tests passed')