Deel geen antwoorden meer over een identiteitswissel heen

De verzoekdeler uit de vorige commit houdt een antwoord driehonderd
milliseconde vast om de opstartcascade op te vangen. Uitloggen herlaadt de
pagina echter niet, dus die tabel overleeft een sessiewissel: binnen dat venster
zou de volgende gebruiker het antwoord van de vorige kunnen krijgen.

Met de hand is dat nauwelijks te bereiken, maar een verzoekcache die een
identiteitswissel overleeft is hoe dan ook fout. De tabel wordt nu gewist bij
uitloggen, bij aanmelden en zodra de sessie verloopt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jens
2026-08-23 15:04:45 +02:00
co-authored by Claude Opus 5
parent 70157400d9
commit 7a5f64e5ca
3 changed files with 50 additions and 1 deletions
+4
View File
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import { formatAuthError } from '../lib/authError'
import { getAuthSession, logout, type AuthSession } from '../services/api/auth'
import { vergeetGedeeldeVerzoeken } from '../services/api/client'
const signedOutSession: AuthSession = {
authentication_required: true,
@@ -53,6 +54,8 @@ export function useOperatorSession() {
setLoggingOut(true)
try {
setSession(await logout())
// Geen gedeelde antwoorden meenemen naar de volgende gebruiker.
vergeetGedeeldeVerzoeken()
setSessionError(null)
} catch (error) {
setSessionError(formatAuthError(error, 'Uitloggen is niet gelukt. Vernieuw de pagina en probeer opnieuw.'))
@@ -62,6 +65,7 @@ export function useOperatorSession() {
}
const handleAuthenticated = (authenticatedSession: AuthSession) => {
vergeetGedeeldeVerzoeken()
setSession(authenticatedSession)
setSessionError(null)
}
+30 -1
View File
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { apiGet, apiPost } from './client'
import { apiGet, apiPost, vergeetGedeeldeVerzoeken } from './client'
function antwoord(data: unknown) {
return {
@@ -85,6 +85,35 @@ describe('apiGet deelt verzoeken', () => {
expect(fetchSpy).toHaveBeenCalledTimes(2)
})
it('deelt niets meer over een identiteitswissel heen', async () => {
// Uitloggen herlaadt de pagina niet, dus de tabel leeft door. Zonder deze
// reset zou de volgende gebruiker binnen het deelvenster het antwoord van
// de vorige kunnen krijgen.
const fetchSpy = vi.fn()
.mockResolvedValueOnce(antwoord({ items: ['werkruimte van A'] }))
.mockResolvedValueOnce(antwoord({ items: ['werkruimte van B'] }))
vi.stubGlobal('fetch', fetchSpy)
const vanA = await apiGet<{ items: string[] }>('/api/v1/projects')
vergeetGedeeldeVerzoeken()
const vanB = await apiGet<{ items: string[] }>('/api/v1/projects')
expect(fetchSpy).toHaveBeenCalledTimes(2)
expect(vanA.items).toEqual(['werkruimte van A'])
expect(vanB.items).toEqual(['werkruimte van B'])
})
it('wist de tabel ook wanneer de sessie verloopt', async () => {
const fetchSpy = vi.fn().mockResolvedValue(antwoord({ items: [] }))
vi.stubGlobal('fetch', fetchSpy)
await apiGet('/api/v1/projects')
window.dispatchEvent(new CustomEvent('geointel:session-expired'))
await apiGet('/api/v1/projects')
expect(fetchSpy).toHaveBeenCalledTimes(2)
})
it('laat een mislukt verzoek niet in de tabel achter', async () => {
const fetchSpy = vi.fn()
.mockRejectedValueOnce(new Error('netwerk weg'))
+16
View File
@@ -57,6 +57,22 @@ const lopendeGets = new Map<string, Promise<unknown>>();
*/
const DEEL_VENSTER_MS = 300;
/**
* Vergeet alle gedeelde antwoorden.
*
* Uitloggen herlaadt de pagina niet, dus zonder dit zou een antwoord van de ene
* gebruiker binnen het deelvenster nog aan de volgende geserveerd kunnen worden.
* Dat is binnen driehonderd milliseconde met de hand nauwelijks te bereiken,
* maar een verzoekcache die een identiteitswissel overleeft is hoe dan ook fout.
*/
export function vergeetGedeeldeVerzoeken(): void {
lopendeGets.clear();
}
if (typeof window !== "undefined") {
window.addEventListener("geointel:session-expired", vergeetGedeeldeVerzoeken);
}
export async function apiGet<T>(path: string): Promise<T> {
const lopend = lopendeGets.get(path);
if (lopend) {