Compare commits

..

No commits in common. "3ef7b09f84094b387d8574dee2dfda993d2727c4" and "f8dd45a6f0e32201ecccb3fc4c1a2ac95cad9c20" have entirely different histories.

3 changed files with 1 additions and 74 deletions

View file

@ -9,7 +9,6 @@ import { ensureUploadDir, removeFile } from './lib/storage.js';
import { createUploadSessionStore } from './lib/upload-sessions.js';
import { createOidcClient, type OidcClient } from './oidc.js';
import { authRoutes } from './routes/auth.js';
import { createSessionRefresher } from './session.js';
import { chunkedUploadRoutes } from './routes/upload-chunked.js';
import { healthRoutes } from './routes/health.js';
import { pageRoutes } from './routes/pages.js';
@ -62,12 +61,6 @@ export async function buildApp(
await notifier.close();
});
const refreshSession = createSessionRefresher(config.session.maxAgeSeconds);
app.addHook('preHandler', (request, _reply, hookDone) => {
refreshSession(request);
hookDone();
});
// Corps brut (flux) pour les morceaux d'upload fragmenté ; écrit en streaming.
app.addContentTypeParser('application/octet-stream', (_request, payload, taskDone) => {
taskDone(null, payload);

View file

@ -26,25 +26,6 @@ export function getSessionUser(request: FastifyRequest): SessionUser | null {
return user;
}
/** Intervalle minimal entre deux prolongations (évite un `Set-Cookie` à chaque requête). */
const REFRESH_MIN_INTERVAL_MS = 60_000;
/**
* Session glissante : chaque requête authentifiée repousse l'expiration de
* `maxAgeSeconds` (au plus une fois par minute), côté jeton et côté cookie.
* Une session n'expire donc qu'après `maxAgeSeconds` d'inactivité.
*/
export function createSessionRefresher(maxAgeSeconds: number): (request: FastifyRequest) => void {
const maxAgeMs = maxAgeSeconds * 1000;
return (request: FastifyRequest): void => {
const user = getSessionUser(request);
if (user === null || user.expiresAt - Date.now() >= maxAgeMs - REFRESH_MIN_INTERVAL_MS) {
return;
}
request.session.set('user', { ...user, expiresAt: Date.now() + maxAgeMs });
};
}
/** Hook `preHandler` : rejette (401) toute requête sans session valide. */
export function requireSession(
request: FastifyRequest,

View file

@ -2,12 +2,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify';
import { describe, expect, it, vi } from 'vitest';
import { UnauthorizedError } from '../src/errors.js';
import {
createSessionRefresher,
getSessionUser,
requireSession,
type SessionUser,
} from '../src/session.js';
import { getSessionUser, requireSession, type SessionUser } from '../src/session.js';
function requestWithSessionUser(user: SessionUser | undefined): FastifyRequest {
return {
@ -33,48 +28,6 @@ describe('getSessionUser', () => {
});
});
describe('createSessionRefresher (session glissante)', () => {
const MAX_AGE_S = 3600;
function requestRecordingSet(user: SessionUser | undefined): {
request: FastifyRequest;
set: ReturnType<typeof vi.fn>;
} {
const set = vi.fn();
const request = {
session: { get: (key: string) => (key === 'user' ? user : undefined), set },
} as unknown as FastifyRequest;
return { request, set };
}
it('prolonge une session utilisée depuis plus d’une minute', () => {
const user: SessionUser = { sub: 'u1', name: 'Alice', expiresAt: Date.now() + 1_800_000 };
const { request, set } = requestRecordingSet(user);
createSessionRefresher(MAX_AGE_S)(request);
expect(set).toHaveBeenCalledTimes(1);
const [key, value] = set.mock.calls[0] as [string, SessionUser];
expect(key).toBe('user');
expect(value.sub).toBe('u1');
expect(value.expiresAt).toBeGreaterThan(Date.now() + 3_500_000);
});
it('ne réécrit pas la session à chaque requête (moins d’une minute)', () => {
const user: SessionUser = { sub: 'u1', name: 'Alice', expiresAt: Date.now() + 3_590_000 };
const { request, set } = requestRecordingSet(user);
createSessionRefresher(MAX_AGE_S)(request);
expect(set).not.toHaveBeenCalled();
});
it('ne ressuscite pas une session expirée ni une requête anonyme', () => {
const expired: SessionUser = { sub: 'u1', name: 'Alice', expiresAt: Date.now() - 1000 };
for (const user of [expired, undefined]) {
const { request, set } = requestRecordingSet(user);
createSessionRefresher(MAX_AGE_S)(request);
expect(set).not.toHaveBeenCalled();
}
});
});
describe('requireSession', () => {
it('appelle done(UnauthorizedError) sans session', () => {
const done = vi.fn();