mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-03 16:51:26 +00:00
feat: Securisation du Backend
This commit is contained in:
@ -1,6 +1,5 @@
|
||||
import NextAuth from 'next-auth';
|
||||
import CredentialsProvider from 'next-auth/providers/credentials';
|
||||
import { getJWT, refreshJWT } from '@/app/actions/authAction';
|
||||
import jwt_decode from 'jsonwebtoken';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
@ -13,19 +12,32 @@ const options = {
|
||||
email: { label: 'Email', type: 'email' },
|
||||
password: { label: 'Password', type: 'password' },
|
||||
},
|
||||
authorize: async (credentials, req) => {
|
||||
authorize: async (credentials) => {
|
||||
// URL calculée ici (pas au niveau module) pour garantir que NEXT_PUBLIC_API_URL est chargé
|
||||
const loginUrl = `${process.env.NEXT_PUBLIC_API_URL}/Auth/login`;
|
||||
try {
|
||||
const data = {
|
||||
email: credentials.email,
|
||||
password: credentials.password,
|
||||
};
|
||||
const res = await fetch(loginUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Connection: close évite le SocketError undici lié au keep-alive vers Daphne
|
||||
Connection: 'close',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: credentials.email,
|
||||
password: credentials.password,
|
||||
}),
|
||||
});
|
||||
|
||||
const user = await getJWT(data);
|
||||
|
||||
if (user) {
|
||||
return user;
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
throw new Error(body?.errorMessage || 'Identifiants invalides');
|
||||
}
|
||||
|
||||
const user = await res.json();
|
||||
return user || null;
|
||||
} catch (error) {
|
||||
logger.error('Authorize error:', error.message);
|
||||
throw new Error(error.message || 'Invalid credentials');
|
||||
}
|
||||
},
|
||||
@ -33,8 +45,10 @@ const options = {
|
||||
],
|
||||
session: {
|
||||
strategy: 'jwt',
|
||||
maxAge: 30 * 24 * 60 * 60, // 30 jours
|
||||
updateAge: 24 * 60 * 60, // 24 heures
|
||||
maxAge: 60 * 60, // 1 Hour
|
||||
// 0 = réécrire le cookie à chaque fois que le token change (indispensable avec
|
||||
// un access token Django de 15 min, sinon le cookie expiré reste en place)
|
||||
updateAge: 0,
|
||||
},
|
||||
cookies: {
|
||||
sessionToken: {
|
||||
@ -64,25 +78,61 @@ const options = {
|
||||
return token;
|
||||
}
|
||||
|
||||
// Token expiré, essayer de le rafraîchir
|
||||
// Token Django expiré (lifetime = 15 min), essayer de le rafraîchir
|
||||
logger.info('JWT: access token expiré, tentative de refresh');
|
||||
|
||||
if (!token.refresh) {
|
||||
logger.error('JWT: refresh token absent dans la session');
|
||||
return { ...token, error: 'RefreshTokenError' };
|
||||
}
|
||||
|
||||
const refreshUrl = `${process.env.NEXT_PUBLIC_API_URL}/Auth/refreshJWT`;
|
||||
if (!process.env.NEXT_PUBLIC_API_URL) {
|
||||
logger.error('JWT: NEXT_PUBLIC_API_URL non défini, refresh impossible');
|
||||
return { ...token, error: 'RefreshTokenError' };
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await refreshJWT({ refresh: token.refresh });
|
||||
if (response && response?.token) {
|
||||
return {
|
||||
...token,
|
||||
token: response.token,
|
||||
refresh: response.refresh,
|
||||
tokenExpires: jwt_decode.decode(response.token).exp * 1000,
|
||||
};
|
||||
} else {
|
||||
throw new Error('Failed to refresh token');
|
||||
const res = await fetch(refreshUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Connection: close évite le SocketError undici lié au keep-alive vers Daphne
|
||||
Connection: 'close',
|
||||
},
|
||||
body: JSON.stringify({ refresh: token.refresh }),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
logger.error('JWT: refresh échoué', { status: res.status, body });
|
||||
throw new Error(`Refresh HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
const response = await res.json();
|
||||
if (!response?.token) {
|
||||
logger.error('JWT: réponse refresh sans token', { response });
|
||||
throw new Error('Réponse refresh invalide');
|
||||
}
|
||||
|
||||
logger.info('JWT: refresh réussi');
|
||||
return {
|
||||
...token,
|
||||
token: response.token,
|
||||
refresh: response.refresh,
|
||||
tokenExpires: jwt_decode.decode(response.token).exp * 1000,
|
||||
error: undefined,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Refresh token failed:', error);
|
||||
return token;
|
||||
logger.error('JWT: refresh token failed', { message: error.message });
|
||||
return { ...token, error: 'RefreshTokenError' };
|
||||
}
|
||||
},
|
||||
async session({ session, token }) {
|
||||
if (token?.error === 'RefreshTokenError') {
|
||||
session.error = 'RefreshTokenError';
|
||||
return session;
|
||||
}
|
||||
if (token && token?.token) {
|
||||
const { user_id, email, roles, roleIndexLoginDefault } =
|
||||
jwt_decode.decode(token.token);
|
||||
|
||||
Reference in New Issue
Block a user