mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
chore: ajustement JWT
This commit is contained in:
@ -2,12 +2,13 @@ from django.urls import path, re_path
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
import Auth.views
|
import Auth.views
|
||||||
from Auth.views import ProfileSimpleView, ProfileView, SessionView, LoginView, SubscribeView, NewPasswordView, ResetPasswordView
|
from Auth.views import ProfileSimpleView, ProfileView, SessionView, LoginView, RefreshJWTView, SubscribeView, NewPasswordView, ResetPasswordView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
re_path(r'^csrf$', Auth.views.csrf, name='csrf'),
|
re_path(r'^csrf$', Auth.views.csrf, name='csrf'),
|
||||||
|
|
||||||
re_path(r'^login$', LoginView.as_view(), name="login"),
|
re_path(r'^login$', LoginView.as_view(), name="login"),
|
||||||
|
re_path(r'^refreshJWT$', RefreshJWTView.as_view(), name="refresh_jwt"),
|
||||||
re_path(r'^subscribe$', SubscribeView.as_view(), name='subscribe'),
|
re_path(r'^subscribe$', SubscribeView.as_view(), name='subscribe'),
|
||||||
re_path(r'^newPassword$', NewPasswordView.as_view(), name='newPassword'),
|
re_path(r'^newPassword$', NewPasswordView.as_view(), name='newPassword'),
|
||||||
re_path(r'^resetPassword/(?P<code>[a-zA-Z]+)$', ResetPasswordView.as_view(), name='resetPassword'),
|
re_path(r'^resetPassword/(?P<code>[a-zA-Z]+)$', ResetPasswordView.as_view(), name='resetPassword'),
|
||||||
|
|||||||
@ -13,8 +13,9 @@ from rest_framework import status
|
|||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
import jwt
|
import jwt
|
||||||
|
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from . import validator
|
from . import validator
|
||||||
@ -26,11 +27,13 @@ from Subscriptions.models import RegistrationForm
|
|||||||
from Subscriptions.signals import clear_cache
|
from Subscriptions.signals import clear_cache
|
||||||
import Subscriptions.mailManager as mailer
|
import Subscriptions.mailManager as mailer
|
||||||
import Subscriptions.util as util
|
import Subscriptions.util as util
|
||||||
|
import logging
|
||||||
from N3wtSchool import bdd, error
|
from N3wtSchool import bdd, error
|
||||||
|
|
||||||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||||
|
|
||||||
|
logger = logging.getLogger("AuthViews")
|
||||||
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
method='get',
|
method='get',
|
||||||
@ -162,13 +165,17 @@ class LoginView(APIView):
|
|||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
200: openapi.Response('Connexion réussie', schema=openapi.Schema(
|
200: openapi.Response('Connexion réussie', schema=openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
properties={
|
||||||
|
'token': openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
'refresh': openapi.Schema(type=openapi.TYPE_STRING)
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
400: openapi.Response('Connexion échouée', schema=openapi.Schema(
|
||||||
type=openapi.TYPE_OBJECT,
|
type=openapi.TYPE_OBJECT,
|
||||||
properties={
|
properties={
|
||||||
'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT),
|
'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT),
|
||||||
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING),
|
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING)
|
||||||
'profil': openapi.Schema(type=openapi.TYPE_INTEGER),
|
|
||||||
'droit': openapi.Schema(type=openapi.TYPE_INTEGER),
|
|
||||||
'id': openapi.Schema(type=openapi.TYPE_INTEGER),
|
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -179,6 +186,7 @@ class LoginView(APIView):
|
|||||||
retour = error.returnMessage[error.WRONG_ID]
|
retour = error.returnMessage[error.WRONG_ID]
|
||||||
validationOk, errorFields = validatorAuthentication.validate()
|
validationOk, errorFields = validatorAuthentication.validate()
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
if validationOk:
|
if validationOk:
|
||||||
user = authenticate(
|
user = authenticate(
|
||||||
email=data.get('email'),
|
email=data.get('email'),
|
||||||
@ -191,6 +199,31 @@ class LoginView(APIView):
|
|||||||
user.save()
|
user.save()
|
||||||
clear_cache()
|
clear_cache()
|
||||||
retour = ''
|
retour = ''
|
||||||
|
# Générer le JWT avec la bonne syntaxe datetime
|
||||||
|
access_payload = {
|
||||||
|
'user_id': user.id,
|
||||||
|
'email': user.email,
|
||||||
|
'droit': user.droit,
|
||||||
|
'type': 'access',
|
||||||
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
|
||||||
|
'iat': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||||
|
# Générer le Refresh Token (exp: 7 jours)
|
||||||
|
refresh_payload = {
|
||||||
|
'user_id': user.id,
|
||||||
|
'type': 'refresh',
|
||||||
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
|
||||||
|
'iat': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'token': access_token,
|
||||||
|
'refresh': refresh_token
|
||||||
|
}, safe=False)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
retour = error.returnMessage[error.PROFIL_INACTIVE]
|
retour = error.returnMessage[error.PROFIL_INACTIVE]
|
||||||
else:
|
else:
|
||||||
@ -199,10 +232,101 @@ class LoginView(APIView):
|
|||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'errorFields': errorFields,
|
'errorFields': errorFields,
|
||||||
'errorMessage': retour,
|
'errorMessage': retour,
|
||||||
'profil': user.id if user else -1,
|
}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
'droit': user.droit if user else -1,
|
|
||||||
'id': user.id if user else -1,
|
|
||||||
}, safe=False)
|
class RefreshJWTView(APIView):
|
||||||
|
@swagger_auto_schema(
|
||||||
|
operation_description="Rafraîchir le token d'accès",
|
||||||
|
request_body=openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
required=['refresh'],
|
||||||
|
properties={
|
||||||
|
'refresh': openapi.Schema(type=openapi.TYPE_STRING)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses={
|
||||||
|
200: openapi.Response('Token rafraîchi avec succès', schema=openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
properties={
|
||||||
|
'token': openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
'refresh': openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
400: openapi.Response('Échec du rafraîchissement', schema=openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
properties={
|
||||||
|
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
def post(self, request):
|
||||||
|
data = JSONParser().parse(request)
|
||||||
|
refresh_token = data.get("refresh")
|
||||||
|
logger.info(f"Token reçu: {refresh_token[:20]}...") # Ne pas logger le token complet pour la sécurité
|
||||||
|
|
||||||
|
if not refresh_token:
|
||||||
|
return JsonResponse({'errorMessage': 'Refresh token manquant'}, status=400)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Décoder le Refresh Token
|
||||||
|
logger.info("Tentative de décodage du token")
|
||||||
|
logger.info(f"Algorithme utilisé: {settings.SIMPLE_JWT['ALGORITHM']}")
|
||||||
|
|
||||||
|
# Vérifier le format du token avant décodage
|
||||||
|
token_parts = refresh_token.split('.')
|
||||||
|
if len(token_parts) != 3:
|
||||||
|
logger.error("Format de token invalide - pas 3 parties")
|
||||||
|
return JsonResponse({'errorMessage': 'Format de token invalide'}, status=400)
|
||||||
|
|
||||||
|
payload = jwt.decode(
|
||||||
|
refresh_token,
|
||||||
|
settings.SIMPLE_JWT['SIGNING_KEY'],
|
||||||
|
algorithms=[settings.SIMPLE_JWT['ALGORITHM']] # Noter le passage en liste
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Token décodé avec succès. Type: {payload.get('type')}")
|
||||||
|
# Vérifier s'il s'agit bien d'un Refresh Token
|
||||||
|
if payload.get('type') != 'refresh':
|
||||||
|
return JsonResponse({'errorMessage': 'Token invalide'}, status=400)
|
||||||
|
|
||||||
|
# Récupérer les informations utilisateur
|
||||||
|
user = Profile.objects.get(id=payload['user_id'])
|
||||||
|
|
||||||
|
# Générer un nouveau Access Token avec les informations complètes
|
||||||
|
new_access_payload = {
|
||||||
|
'user_id': user.id,
|
||||||
|
'email': user.email,
|
||||||
|
'droit': user.droit,
|
||||||
|
'type': 'access',
|
||||||
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
|
||||||
|
'iat': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
new_access_token = jwt.encode(new_access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||||
|
|
||||||
|
new_refresh_payload = {
|
||||||
|
'user_id': user.id,
|
||||||
|
'type': 'refresh',
|
||||||
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
|
||||||
|
'iat': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
new_refresh_token = jwt.encode(new_refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||||
|
|
||||||
|
return JsonResponse({'token': new_access_token, 'refresh': new_refresh_token}, status=200)
|
||||||
|
|
||||||
|
except ExpiredSignatureError as e:
|
||||||
|
logger.error(f"Token expiré: {str(e)}")
|
||||||
|
return JsonResponse({'errorMessage': 'Refresh token expiré'}, status=400)
|
||||||
|
except InvalidTokenError as e:
|
||||||
|
logger.error(f"Token invalide: {str(e)}")
|
||||||
|
return JsonResponse({'errorMessage': f'Token invalide: {str(e)}'}, status=400)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur inattendue: {str(e)}")
|
||||||
|
return JsonResponse({'errorMessage': f'Erreur inattendue: {str(e)}'}, status=400)
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_protect, name='dispatch')
|
@method_decorator(csrf_protect, name='dispatch')
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
|
|||||||
@ -314,7 +314,7 @@ REDIS_PASSWORD = None
|
|||||||
|
|
||||||
SECRET_KEY = 'QWQ8bYlCz1NpQ9G0vR5kxMnvWszfH2y3'
|
SECRET_KEY = 'QWQ8bYlCz1NpQ9G0vR5kxMnvWszfH2y3'
|
||||||
SIMPLE_JWT = {
|
SIMPLE_JWT = {
|
||||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
|
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
|
||||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
|
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
|
||||||
'ROTATE_REFRESH_TOKENS': False,
|
'ROTATE_REFRESH_TOKENS': False,
|
||||||
'BLACKLIST_AFTER_ROTATION': True,
|
'BLACKLIST_AFTER_ROTATION': True,
|
||||||
|
|||||||
@ -8,6 +8,9 @@ const nextConfig = {
|
|||||||
output: "standalone",
|
output: "standalone",
|
||||||
experimental: {
|
experimental: {
|
||||||
instrumentationHook: true,
|
instrumentationHook: true,
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
domains:['i.pravatar.cc'],
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
NEXT_PUBLIC_APP_VERSION: pkg.version,
|
NEXT_PUBLIC_APP_VERSION: pkg.version,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import Sidebar from '@/components/Sidebar';
|
import Sidebar from '@/components/Sidebar';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import {useTranslations} from 'next-intl';
|
import {useTranslations} from 'next-intl';
|
||||||
|
import Image from 'next/image';
|
||||||
import {
|
import {
|
||||||
Users,
|
Users,
|
||||||
Building,
|
Building,
|
||||||
@ -94,7 +95,7 @@ export default function Layout({
|
|||||||
<header className="h-16 bg-white border-b border-gray-200 px-8 py-4 flex items-center justify-between z-9">
|
<header className="h-16 bg-white border-b border-gray-200 px-8 py-4 flex items-center justify-between z-9">
|
||||||
<div className="text-xl font-semibold">{headerTitle}</div>
|
<div className="text-xl font-semibold">{headerTitle}</div>
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
buttonContent={<img src="https://i.pravatar.cc/32" alt="Profile" className="w-8 h-8 rounded-full cursor-pointer" />}
|
buttonContent={<Image src="https://i.pravatar.cc/32" alt="Profile" className="w-8 h-8 rounded-full cursor-pointer" width={150} height={150} />}
|
||||||
items={dropdownItems}
|
items={dropdownItems}
|
||||||
buttonClassName=""
|
buttonClassName=""
|
||||||
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded shadow-lg"
|
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded shadow-lg"
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { SendHorizontal } from 'lucide-react';
|
import { SendHorizontal } from 'lucide-react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
const contacts = [
|
const contacts = [
|
||||||
{ id: 1, name: 'Facturation', profilePic: 'https://i.pravatar.cc/32' },
|
{ id: 1, name: 'Facturation', profilePic: 'https://i.pravatar.cc/32' },
|
||||||
@ -61,7 +62,7 @@ export default function MessageriePage() {
|
|||||||
className={`p-2 cursor-pointer ${selectedContact?.id === contact.id ? 'bg-gray-200' : ''}`}
|
className={`p-2 cursor-pointer ${selectedContact?.id === contact.id ? 'bg-gray-200' : ''}`}
|
||||||
onClick={() => setSelectedContact(contact)}
|
onClick={() => setSelectedContact(contact)}
|
||||||
>
|
>
|
||||||
<img src={contact.profilePic} alt={`${contact.name}'s profile`} className="w-8 h-8 rounded-full inline-block mr-2" />
|
<Image src={contact.profilePic} alt={`${contact.name}'s profile`} className="w-8 h-8 rounded-full inline-block mr-2" width={150} height={150}/>
|
||||||
{contact.name}
|
{contact.name}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -75,7 +76,7 @@ export default function MessageriePage() {
|
|||||||
style={{ borderRadius: message.isResponse ? '20px 20px 0 20px' : '20px 20px 20px 0', minWidth: '25%' }}
|
style={{ borderRadius: message.isResponse ? '20px 20px 0 20px' : '20px 20px 20px 0', minWidth: '25%' }}
|
||||||
>
|
>
|
||||||
<div className="flex items-center mb-1">
|
<div className="flex items-center mb-1">
|
||||||
<img src={selectedContact.profilePic} alt={`${selectedContact.name}'s profile`} className="w-8 h-8 rounded-full inline-block mr-2" />
|
<img src={selectedContact.profilePic} alt={`${selectedContact.name}'s profile`} className="w-8 h-8 rounded-full inline-block mr-2" width={150} height={150} />
|
||||||
<span className="text-xs text-gray-600">{selectedContact.name}</span>
|
<span className="text-xs text-gray-600">{selectedContact.name}</span>
|
||||||
<span className="text-xs text-gray-400 ml-2">{new Date(message.date).toLocaleTimeString()}</span>
|
<span className="text-xs text-gray-400 ml-2">{new Date(message.date).toLocaleTimeString()}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import logoImage from '@/img/logo_min.svg'; // Assurez-vous que le chemin vers l
|
|||||||
|
|
||||||
const Logo = ({ className }) => {
|
const Logo = ({ className }) => {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={`max-w-[150px] ${className}`}>
|
||||||
<Image src={logoImage} alt="Logo" width={150} height={150} />
|
<Image src={logoImage} alt="Logo" style={{ width: 'auto', height: 'auto'}} priority />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,23 +5,21 @@ import Loader from '@/components/Loader'; // Importez le composant Loader
|
|||||||
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
|
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
|
||||||
|
|
||||||
const ProtectedRoute = ({ children }) => {
|
const ProtectedRoute = ({ children }) => {
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession({
|
||||||
const router = useRouter();
|
required: true,
|
||||||
|
onUnauthenticated() {
|
||||||
useEffect(() => {
|
|
||||||
if (status === 'loading') return; // Ne rien faire tant que le statut est "loading"
|
|
||||||
if (!session) {
|
|
||||||
// Rediriger vers la page de login si l'utilisateur n'est pas connecté
|
|
||||||
router.push(`${FE_USERS_LOGIN_URL}`);
|
router.push(`${FE_USERS_LOGIN_URL}`);
|
||||||
}
|
}
|
||||||
}, [session, status, router]);
|
});
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
if (status === 'loading' || !session) {
|
// Ne vérifier que si le statut est définitif
|
||||||
return <Loader />; // Affichez un loader pendant le chargement ou si l'utilisateur n'est pas connecté
|
if (status === 'loading') {
|
||||||
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Afficher les enfants seulement si l'utilisateur est connecté
|
// Autoriser l'affichage si authentifié
|
||||||
return children;
|
return session ? children : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProtectedRoute;
|
export default ProtectedRoute;
|
||||||
@ -1,31 +1,48 @@
|
|||||||
import { useState } from "react"
|
'use client';
|
||||||
|
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
const useLocalStorage = (key, initialValue) => {
|
const useLocalStorage = (key, initialValue) => {
|
||||||
const [state, setState] = useState(() => {
|
const [storedValue, setStoredValue] = useState(() => {
|
||||||
// Initialize the state
|
|
||||||
try {
|
try {
|
||||||
const value = window.localStorage.getItem(key)
|
if (typeof window !== 'undefined') {
|
||||||
// Check if the local storage already has any values,
|
const item = window.localStorage.getItem(key);
|
||||||
// otherwise initialize it with the passed initialValue
|
// Vérifier si l'item existe et n'est pas undefined
|
||||||
return value ? JSON.parse(value) : initialValue
|
return item !== null && item !== 'undefined'
|
||||||
|
? JSON.parse(item)
|
||||||
|
: initialValue;
|
||||||
|
}
|
||||||
|
return initialValue;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.error('Error reading from localStorage:', error);
|
||||||
|
return initialValue;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
const setValue = value => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
// If the passed value is a callback function,
|
// Vérifier si la valeur n'est pas undefined avant de la stocker
|
||||||
// then call it with the existing state.
|
if (typeof storedValue !== 'undefined') {
|
||||||
const valueToStore = value instanceof Function ? value(state) : value
|
window.localStorage.setItem(key, JSON.stringify(storedValue));
|
||||||
window.localStorage.setItem(key, JSON.stringify(valueToStore))
|
} else {
|
||||||
setState(value)
|
window.localStorage.removeItem(key);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.error('Error writing to localStorage:', error);
|
||||||
}
|
}
|
||||||
}
|
}, [key, storedValue]);
|
||||||
|
|
||||||
return [state, setValue]
|
const setValue = (value) => {
|
||||||
}
|
try {
|
||||||
|
// Permettre à la valeur d'être une fonction
|
||||||
|
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||||
|
setStoredValue(valueToStore);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating localStorage value:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default useLocalStorage
|
return [storedValue, setValue];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLocalStorage;
|
||||||
@ -34,45 +34,69 @@ const options = {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
session: {
|
session: {
|
||||||
strategy:"jwt",
|
strategy: "jwt",
|
||||||
|
maxAge: 30 * 24 * 60 * 60, // 30 jours
|
||||||
|
updateAge: 24 * 60 * 60, // 24 heures
|
||||||
|
},
|
||||||
|
cookies: {
|
||||||
|
sessionToken: {
|
||||||
|
name: 'n3wtschool_session_token',
|
||||||
|
options: {
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
path: '/',
|
||||||
|
secure: process.env.NODE_ENV === 'production'
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async jwt({ token, user }) {
|
async jwt({ token, user, trigger }) {
|
||||||
if (user) {
|
// Si c'est la première connexion
|
||||||
token.token = user.token;
|
if (user) {
|
||||||
token.refresh = user.refresh;
|
return {
|
||||||
token.tokenExpires = jwt_decode.decode(user.token).exp * 1000;
|
...token,
|
||||||
|
token: user.token,
|
||||||
|
refresh: user.refresh,
|
||||||
|
tokenExpires: jwt_decode.decode(user.token).exp * 1000
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// Vérifie si l'access token a expiré
|
|
||||||
|
// Vérifier si le token n'est pas expiré
|
||||||
if (Date.now() < token.tokenExpires) {
|
if (Date.now() < token.tokenExpires) {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
// Renouvelle le token expiré
|
|
||||||
|
// Token expiré, essayer de le rafraîchir
|
||||||
try {
|
try {
|
||||||
const data = {refresh: token.refresh}
|
const response = await refreshJWT({ refresh: token.refresh });
|
||||||
const res = await refreshJWT(data);
|
if (!response) {
|
||||||
console.log(res);
|
throw new Error('Failed to refresh token');
|
||||||
token.token = res.token;
|
}
|
||||||
token.refresh = res.refresh;
|
|
||||||
token.tokenExpires = jwt_decode.decode(res.token).exp * 1000;
|
return {
|
||||||
console.log("Token refreshed", token);
|
...token,
|
||||||
return token;
|
token: response.token,
|
||||||
|
refresh: response.refresh,
|
||||||
|
tokenExpires: jwt_decode.decode(response.token).exp * 1000
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors du rafraîchissement du token", error);
|
console.error("Refresh token failed:", error);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async session({ session, token }) {
|
async session({ session, token }) {
|
||||||
console.log("Session callback called", token);
|
if (token) {
|
||||||
if (!token) {
|
const {user_id, droit, email} = jwt_decode.decode(token.token);
|
||||||
throw new Error('Token not found');
|
session.user = {
|
||||||
}
|
...session.user,
|
||||||
else{
|
token: token.token,
|
||||||
const decodedToken = jwt_decode.decode(token.token);
|
refresh: token.refresh
|
||||||
const {user_id,email,droit} = decodedToken;
|
};
|
||||||
session.user = {id:user_id,email,droit};
|
session.user.user_id = user_id;
|
||||||
return session;
|
session.user.droit = droit;
|
||||||
|
session.user.email = email;
|
||||||
}
|
}
|
||||||
|
return session;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
|
|||||||
Reference in New Issue
Block a user