mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 04:30:20 +08:00
fix(frontend): fail fast on deterministic refresh errors
This commit is contained in:
@@ -198,6 +198,26 @@ describe('CrossTabRefreshCoordinator', () => {
|
||||
coordinator.destroy()
|
||||
})
|
||||
|
||||
it('does not wait on other deterministic client-side refresh rejections', async () => {
|
||||
const refreshError = createHttpError(400, 'invalid refresh request')
|
||||
const executor = vi.fn(() => Promise.reject(refreshError))
|
||||
const coordinator = new CrossTabRefreshCoordinator({
|
||||
storage: localStorage,
|
||||
channelFactory: createChannel,
|
||||
waitTimeoutMs: 500,
|
||||
})
|
||||
|
||||
const outcome = await Promise.race([
|
||||
coordinator.run(executor).catch((error: unknown) => error),
|
||||
new Promise<symbol>((resolve) => setTimeout(() => resolve(Symbol('timeout')), 0)),
|
||||
])
|
||||
|
||||
expect(outcome).toBe(refreshError)
|
||||
expect(executor).toHaveBeenCalledTimes(1)
|
||||
|
||||
coordinator.destroy()
|
||||
})
|
||||
|
||||
it('keeps the coordination window for a refresh-token rotation conflict', async () => {
|
||||
const refreshError = createHttpError(409, 'refresh token was rotated concurrently')
|
||||
const executor = vi.fn(() => Promise.reject(refreshError))
|
||||
|
||||
@@ -90,7 +90,10 @@ function isDefinitiveRefreshRejection(error: unknown): boolean {
|
||||
return false
|
||||
}
|
||||
const status = (response as { status?: unknown }).status
|
||||
return status === 401 || status === 403
|
||||
// Refresh uses 409 exclusively for a previous-token rotation race. Every
|
||||
// other client-side rejection is deterministic and cannot be repaired by
|
||||
// waiting for another tab.
|
||||
return status >= 400 && status < 500 && status !== 409
|
||||
}
|
||||
|
||||
export class CrossTabRefreshCoordinator {
|
||||
@@ -197,8 +200,8 @@ export class CrossTabRefreshCoordinator {
|
||||
}
|
||||
|
||||
// The refresh endpoint reserves 409 for a concurrent token rotation.
|
||||
// A direct 401/403 is authoritative, so waiting for the HTTP timeout
|
||||
// cannot recover the session and would block initial navigation.
|
||||
// Other 4xx responses are authoritative, so waiting for the HTTP
|
||||
// timeout cannot recover the session and would block initial navigation.
|
||||
if (isDefinitiveRefreshRejection(error)) {
|
||||
throw error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user