mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-03 16:51:26 +00:00
159 lines
4.9 KiB
JavaScript
159 lines
4.9 KiB
JavaScript
import NextAuth from 'next-auth';
|
|
import CredentialsProvider from 'next-auth/providers/credentials';
|
|
import jwt_decode from 'jsonwebtoken';
|
|
import logger from '@/utils/logger';
|
|
|
|
const options = {
|
|
secret: process.env.AUTH_SECRET,
|
|
providers: [
|
|
CredentialsProvider({
|
|
name: 'Credentials',
|
|
credentials: {
|
|
email: { label: 'Email', type: 'email' },
|
|
password: { label: 'Password', type: 'password' },
|
|
},
|
|
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 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,
|
|
}),
|
|
});
|
|
|
|
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');
|
|
}
|
|
},
|
|
}),
|
|
],
|
|
session: {
|
|
strategy: 'jwt',
|
|
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: {
|
|
name: 'n3wtschool_session_token',
|
|
options: {
|
|
httpOnly: true,
|
|
sameSite: 'lax',
|
|
path: '/',
|
|
secure: process.env.NODE_ENV === 'production',
|
|
},
|
|
},
|
|
},
|
|
callbacks: {
|
|
async jwt({ token, user }) {
|
|
// Si c'est la première connexion
|
|
if (user && user?.token) {
|
|
return {
|
|
...token,
|
|
token: user.token,
|
|
refresh: user.refresh,
|
|
tokenExpires: jwt_decode.decode(user.token).exp * 1000,
|
|
};
|
|
}
|
|
|
|
// Vérifier si le token n'est pas expiré
|
|
if (Date.now() < token.tokenExpires) {
|
|
return token;
|
|
}
|
|
|
|
// 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 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('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);
|
|
session.user = {
|
|
...session.user,
|
|
token: token.token,
|
|
refresh: token.refresh,
|
|
user_id: user_id,
|
|
email: email,
|
|
roles: roles,
|
|
roleIndexLoginDefault: roleIndexLoginDefault,
|
|
};
|
|
}
|
|
return session;
|
|
},
|
|
},
|
|
pages: {
|
|
signIn: '/[locale]/users/login',
|
|
},
|
|
csrf: true,
|
|
};
|
|
|
|
export default (req, res) => NextAuth(req, res, options);
|