Harden WebBFF and deployment release behavior

Model fail-closed cross-replica session renewal and implement owner-fenced refresh, transient observer handling, logout fencing, and graceful shutdown. Remove chart-owned Ingress while retaining qualification-owned external routing.
This commit is contained in:
ms2k 2026-07-20 17:09:26 +09:00
commit 414b380516

View file

@ -2,8 +2,8 @@ import assert from 'node:assert/strict'
import { createSign, generateKeyPairSync } from 'node:crypto'
import { test } from 'node:test'
import { OAuthClient } from './oauth.mjs'
import { SESSION_COOKIE } from './sessions.mjs'
import { OAuthClient, SessionRenewalPendingError } from './oauth.mjs'
import { SESSION_COOKIE, SESSION_RENEWAL_PENDING } from './sessions.mjs'
const identifier = 'a'.repeat(43)
const replacement = 'b'.repeat(43)
@ -163,9 +163,88 @@ test('expired and malformed authenticated sessions are destroyed', async () => {
assert.deepEqual(store.destroyed, [identifier, identifier])
})
test('token renewal preserves the session identifier and CSRF value', async () => {
const store = new MemoryStore()
store.value = {
...validSession(),
refreshToken: 'refresh-token',
accessExpiresAt: now + 20,
}
const oauth = new OAuthClient(config, store)
oauth.refresh = async (session) => ({
...session,
accessToken: 'renewed-access-token',
accessExpiresAt: now + 300,
})
const loaded = await oauth.authenticated({
headers: { cookie: `${SESSION_COOKIE}=${identifier}` },
})
assert.equal(loaded.identifier, identifier)
assert.equal(loaded.session.accessToken, 'renewed-access-token')
assert.equal(loaded.session.csrfToken, 'csrf-value')
assert.equal(store.replaced, 0)
})
test('concurrent renewal uses only an unexpired access token', async () => {
const store = new MemoryStore()
store.pending = true
store.value = {
...validSession(),
refreshToken: 'refresh-token',
accessExpiresAt: now + 20,
}
const oauth = new OAuthClient(config, store)
const request = {
headers: { cookie: `${SESSION_COOKIE}=${identifier}` },
}
assert.equal(
(await oauth.authenticated(request)).session.accessToken,
'access-token',
)
store.value.accessExpiresAt = now - 1
await assert.rejects(
oauth.authenticated(request, false),
SessionRenewalPendingError,
)
assert.deepEqual(store.destroyed, [])
})
test('logout destroys an expired session during owned renewal', async () => {
const store = new MemoryStore()
store.pending = true
store.value = {
...validSession(),
accessExpiresAt: now - 1,
}
const oauth = new OAuthClient(config, store)
oauth.metadata = async () => ({})
const response = new TestResponse()
await oauth.logout(
{
headers: {
cookie: `${SESSION_COOKIE}=${identifier}`,
origin: config.publicOrigin,
'sec-fetch-site': 'same-origin',
'x-remora-csrf': 'csrf-value',
},
},
response,
)
assert.equal(response.status, 200)
assert.deepEqual(store.destroyed, [identifier])
assert.match(response.headers['Set-Cookie'], /Max-Age=0/)
})
class MemoryStore {
value = null
destroyed = []
replaced = 0
pending = false
async create(value) {
this.value = value
@ -177,10 +256,23 @@ class MemoryStore {
}
async replace(_identifier, value) {
this.replaced += 1
this.value = value
return replacement
}
async renew(identifier, shouldRenew, renewal) {
if (!shouldRenew(this.value)) return this.value
if (this.pending) return SESSION_RENEWAL_PENDING
const renewed = await renewal(this.value)
if (renewed === null) {
await this.destroy(identifier)
return null
}
this.value = renewed
return this.value
}
async destroy(identifier) {
this.destroyed.push(identifier)
this.value = null