mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-04 03:31:28 +00:00
feat: Securisation du Backend
This commit is contained in:
101
Front-End/src/utils/fetchWithAuth.js
Normal file
101
Front-End/src/utils/fetchWithAuth.js
Normal file
@ -0,0 +1,101 @@
|
||||
import { getSession } from 'next-auth/react';
|
||||
import {
|
||||
requestResponseHandler,
|
||||
errorHandler,
|
||||
triggerSignOut,
|
||||
} from '@/app/actions/actionsHandlers';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
// Déduplique les appels concurrents à getSession() :
|
||||
// si plusieurs fetchWithAuth() partent en même temps (chargement de page),
|
||||
// ils partagent la même promesse au lieu de déclencher N refreshs JWT en parallèle.
|
||||
let _pendingSessionPromise = null;
|
||||
|
||||
const getSessionOnce = () => {
|
||||
if (!_pendingSessionPromise) {
|
||||
_pendingSessionPromise = getSession().finally(() => {
|
||||
_pendingSessionPromise = null;
|
||||
});
|
||||
}
|
||||
return _pendingSessionPromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Récupère le token JWT Bearer depuis la session NextAuth.
|
||||
* @returns {Promise<string|null>}
|
||||
*/
|
||||
export const getAuthToken = async () => {
|
||||
const session = await getSessionOnce();
|
||||
if (!session) {
|
||||
logger.warn('getAuthToken: session nulle, aucun token envoyé');
|
||||
return null;
|
||||
}
|
||||
if (session?.error === 'RefreshTokenError') {
|
||||
logger.warn(
|
||||
'getAuthToken: RefreshTokenError détecté, déconnexion en cours'
|
||||
);
|
||||
await triggerSignOut();
|
||||
return null;
|
||||
}
|
||||
if (!session?.user?.token) {
|
||||
logger.warn('getAuthToken: session présente mais token absent', {
|
||||
session,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return session.user.token;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper de fetch qui injecte automatiquement le header Authorization Bearer
|
||||
* depuis la session NextAuth, puis passe la réponse dans requestResponseHandler.
|
||||
*
|
||||
* - Ajoute Content-Type: application/json par défaut (sauf si le body est FormData)
|
||||
* - Ajoute credentials: 'include' par défaut
|
||||
* - Les options.headers passées en paramètre surchargent les défauts (ex: X-CSRFToken)
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {RequestInit} options
|
||||
* @returns {Promise<any>} Corps de la réponse désérialisé
|
||||
*/
|
||||
export const fetchWithAuth = async (url, options = {}) => {
|
||||
const token = await getAuthToken();
|
||||
const isFormData = options.body instanceof FormData;
|
||||
|
||||
const headers = {
|
||||
...(isFormData ? {} : { 'Content-Type': 'application/json' }),
|
||||
...options.headers,
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
};
|
||||
|
||||
return fetch(url, {
|
||||
credentials: 'include',
|
||||
...options,
|
||||
headers,
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Variante de fetchWithAuth qui retourne la Response brute sans passer
|
||||
* par requestResponseHandler. Utile quand l'appelant gère lui-même response.ok.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {RequestInit} options
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
export const fetchWithAuthRaw = async (url, options = {}) => {
|
||||
const token = await getAuthToken();
|
||||
|
||||
const headers = {
|
||||
...options.headers,
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
};
|
||||
|
||||
return fetch(url, {
|
||||
credentials: 'include',
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user