From ad8a3e639b0ec4506a1d9fdaaef220d9e36ada67 Mon Sep 17 00:00:00 2001
From: a2-imeri <Alfret2.imeri@live.uwe.ac.uk>
Date: Tue, 2 Jul 2024 18:07:16 +0100
Subject: [PATCH] Update Authorization Token

---
 .../__pycache__/settings.cpython-310.pyc      | Bin 3680 -> 3897 bytes
 MisplaceAI/MisplaceAI/settings.py             |   7 +++
 MisplaceAI/admin_app/urls.py                  |   2 -
 MisplaceAI/admin_app/views.py                 |  16 -------
 .../__pycache__/urls.cpython-310.pyc          | Bin 335 -> 600 bytes
 .../__pycache__/views.cpython-310.pyc         | Bin 1555 -> 2185 bytes
 MisplaceAI/authentication/urls.py             |   7 ++-
 MisplaceAI/authentication/views.py            |  21 ++++++++-
 frontend/src/forms/Auth/AdminLoginForm.js     |   8 +---
 frontend/src/forms/Auth/LoginForm.js          |   8 +---
 frontend/src/forms/Auth/RegisterForm.js       |   7 +--
 frontend/src/services/api.js                  |  27 +++++++++---
 frontend/src/services/auth.js                 |  41 ++++++++++++++++++
 13 files changed, 100 insertions(+), 44 deletions(-)

diff --git a/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc
index 3e6038a00a1f6a1ac6472eb94b2cba19f6f3c152..7fa541b2a5a2e158311f32a20c37fca92143ce0d 100644
GIT binary patch
delta 687
zcmaDLvr~>QpO=@5fq{XcU%DkNM|2{e3}e?sZRz?H#uSzumR!~-Rz`+Yrc}0O#s%yP
z8L~LCIHEXHSfjX7*rK>onX`Bn@TPL6urFka;!EL(;!ojBXN(d^XGq~%#26)*&XB^r
zh%rhig(sacN*GLvr0_<Grtn3HrSPXSq;RBw)Cw+Q%w$Xvn!}RL7$u$}93_#?kWw$Q
zh%rSpoiRmh4pWq5Dr<^(ibOACDr<^lic~LS3P%b@iu4?oD5(^g6xkHH6!{c|6vY&!
z6y-T=nT%1=DJm(dnT#oFDe5U2nT#o#bC^<8QnWG|Q&dv4Ga1uaqGVEZqGVHaqvTRJ
zqU2LJq7+i}q7+j(Q}m@7QaMu$q#5ednWB_Z45O4&`HK#xFs3l)s6?q$DW<9{P)#vf
z$hbglAw!C>Btr_LBtwdcBtw*Xs`>(r6w`%_DQ1!k3p7&97czq6qBK(l7HFkvE@Wh6
z2xmxP4rX9sNL9(w&eEwo#K-`GkzmnsFsW&g7{tiHz@^}uxp^*Q3?r{5<1NmT%-qzJ
z)SQyU%|Drv+3H`iGB7Y`GTvg(&CDw;NiBZK2;sA&Bvuw{vP6kGIy<`t2gir_d%OC@
z`*^y!hIsnAMu`Qvx&^rgd!UPp2Kk3LhPcL~NC!trIQcj_d;53>hr~O&g}4UAgOz#u
z`%RAFEMpU7U|^{7n0$asV-i;hH;7ln4id5l2~A$k^@&k;@+@vy-ju|WRFLzFm>C!t
zHgDx-VdRP83ik93@Ntdz3J;mQljj+mEdv8Xk?rKAy!zZz7#J8>7<hO&7<ne&<u&Eu
U;Nf6oVq{`u`@_UFS%l9A02#-udH?_b

delta 452
zcmdlf_dtd(pO=@5fq{WRwW=zuLu4YK3}ebfZRz?{rc{<@#s#bk8M4^2*rM1|SfV&m
zSfe;onX|YSaHq1Tuq|YY;z?nT;!WX5XN=-YXGq~(#2Ce&&XB^jh%rhag*%-wN)Sv6
zrSL=vr|?FJq<~bhr-0N5EMm-LOc9*JlFk?<nj#b>md=nOyofPHB%LuubPiKgy?BaP
zig=1die!pZigb!hitHS=OvWgQ6uA`nOvV(26vY&!OvV)DIZP>XDJq$aDRL>QnT+Wy
zQIaWYQBo=DQPL^wQ8Fp)QL-r-QF5v5DVov@sq86Q(hTWLQSvF;Q3|QNMH^BWQ<!rU
zqZF$YQxz8|rRXeVT%f#=Aw^e`As~fOk|9M;k|9bZRb_!{ivB{z6az_y1*$2A3mHLj
zQEI7t3)EB97BVt2gfpZt2Q#oRq$*}<WNB8eXJi1uNU&%*nA9}dyo)iFakCC{GTUT6
zt}-?r1_p*IkI9DI8k0A1m4LZLY@2ns<rx{ZCj0QnZm!^AVw~K@`;5(;fq|jOd~y+=
bK4%950|N^K4==~$HGHN_O#GAI^7#M&Zw_fB

diff --git a/MisplaceAI/MisplaceAI/settings.py b/MisplaceAI/MisplaceAI/settings.py
index a1a77a2..f163600 100644
--- a/MisplaceAI/MisplaceAI/settings.py
+++ b/MisplaceAI/MisplaceAI/settings.py
@@ -13,6 +13,7 @@ https://docs.djangoproject.com/en/4.0/ref/settings/
 
 from pathlib import Path
 import os
+from datetime import timedelta
 
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
 BASE_DIR = Path(__file__).resolve().parent.parent
@@ -110,6 +111,12 @@ REST_FRAMEWORK = {
     ],
 }
 
+SIMPLE_JWT = {
+    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
+    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
+    'ROTATE_REFRESH_TOKENS': True,
+    'BLACKLIST_AFTER_ROTATION': True,
+}
 # Database
 # https://docs.djangoproject.com/en/4.0/ref/settings/#databases
 
diff --git a/MisplaceAI/admin_app/urls.py b/MisplaceAI/admin_app/urls.py
index ea58610..3648c70 100644
--- a/MisplaceAI/admin_app/urls.py
+++ b/MisplaceAI/admin_app/urls.py
@@ -1,7 +1,6 @@
 # MisplaceAI/admin_app/urls.py
 from django.urls import path
 from .views import (
-    AdminLoginView,
     admin_dashboard_view,
     admin_users_view,
     admin_deactivate_user_view,
@@ -10,7 +9,6 @@ from .views import (
 )
 
 urlpatterns = [
-    path('login/', AdminLoginView.as_view(), name='admin_login'),
     path('dashboard/', admin_dashboard_view, name='admin_dashboard'),
     path('users/', admin_users_view, name='admin_users'),
     path('users/deactivate/<int:user_id>/', admin_deactivate_user_view, name='admin_deactivate_user'),
diff --git a/MisplaceAI/admin_app/views.py b/MisplaceAI/admin_app/views.py
index 5e137a3..46dd4a4 100644
--- a/MisplaceAI/admin_app/views.py
+++ b/MisplaceAI/admin_app/views.py
@@ -12,22 +12,6 @@ from django.contrib.auth.decorators import login_required
 from .serializers import UserSerializer
 
 
-class AdminLoginView(APIView):
-    def post(self, request, *args, **kwargs):
-        username = request.data.get('username')
-        password = request.data.get('password')
-        user = authenticate(request, username=username, password=password)
-        if user is not None and user.is_superuser:
-            login(request, user)
-            refresh = RefreshToken.for_user(user)
-            return Response({
-                'refresh': str(refresh),
-                'access': str(refresh.access_token),
-                'is_admin': True,
-                'is_authenticated': True
-            }, status=status.HTTP_200_OK)
-        return Response({'error': 'Invalid username or password or you do not have the necessary permissions to access this page.'}, status=status.HTTP_400_BAD_REQUEST)
-
 @api_view(['GET'])
 @permission_classes([IsAuthenticated])
 def admin_dashboard_view(request):
diff --git a/MisplaceAI/authentication/__pycache__/urls.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/urls.cpython-310.pyc
index 8bb1713bf934e8764d4b1e4c353a79cd98bf0961..1008e47ecfc3b133b5260926e8c9ad6097420ed4 100644
GIT binary patch
literal 600
zcmd1j<>g{vU|=ZHZ%K1wVqka-;vi#A1_lNP1_p-W4h9B>6owSW9EM!RC`LvQn<<AW
zmpO_#mnDjYks*aSg(ZhImo17dmpzI-mm`XUks*~ag*BCJ0p~)76t)!hUglKB6pmDO
zFrO33=St-O^SPmXo>XQqpBKvKOJxD``9b`2)+nwNfnWwr!IvO6YBJtpDM&2I&}6zL
z9Fm`%n&+QXl9-tnkeFE%mYG_9O8_hwl$utQTATqAdCADYz@W)|izg^GJ+rtZ6{3vO
zCqF$i4<vt!&oL!8GY`(M;w*wH)W5|6V}fkWOUzBJV#@*P)4#<ErmJ`oL3;JU9Q`P6
zFfSg=t70nwxlTVy5KPDCgPj>)keFFi#RnGBFM>EyKZ+O3kB9R8G`Vkar(`AOrRVFF
z7UdKdF)=VOM9CGU7MH}Q6(#1Tmgg5`#}{Yj7UZO6m6zz1Wu}%F7cqk(l!bwT;T9`I
zfDI(T4k9=}qU?#q@gRv?+@(c11&JjksYQ9kD;bIeKq^4QFKPY6f&%@-(vpnSypqi1
v#FEVXJbjP@^a?6(aoFVMr<CTT+JQo|Scrjvfrn9mQG`)|S%47&Ier5Gsm-nw

delta 228
zcmcb?a-OL^pO=@5fq{X+qs2dsk&%JnF^GeV*%%lY92giFid`5O7*ZHg7;_kM8KW2(
z8B&;1m~)tNnWLC<S)y1N8B!TjSW=l6ur6dsVNGG{Wlm*GVNYcN^Ep8Lbfze_6wY7<
zO|F+9^EDZ7u@odqmSntSWME*>WV*!@l$xGdT#{N8mYG_9i*vFlqp7l=Ci^Y!l&r+O
z^nAV2qMYI)CI$wETdZZ7spZ8*EDQ_`MXZxQGRm;=f|$IMg_-pEK)&H&6krr!7GUJ~
F2LON8FOvWO

diff --git a/MisplaceAI/authentication/__pycache__/views.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/views.cpython-310.pyc
index 5fe73a8a01b42fc98c48199de2c648f55a70125c..b5f125413f28b700e37fb77778c585ec39c6ddc8 100644
GIT binary patch
delta 924
zcmbQt(<#W8&&$ijz`($err(m*!ZDFghH>6R?Rb|Ih7`^mhFpOt0Y(OQh7_(8?iPj=
z?o{S1!Di+tA$Nupo)q2|h7{gZj%Ma4VRwcUz7+lzh7^7<UnE5!m_bu;;-(f(O~zZS
zIr-_Cd6Q+Bq$US2_OOXEFfbH}PJYCwB+ARcz`(=6z);M_z`(%4z{SYHSj0P7l1YZ~
z^JFuoGh8bfZ?U8#mL!I(oXo?l#<`NINEW17Zn8DAD&xw@iOgn<Qj=#eCo$fd{F~W;
zaphzk7DFi|kP?U`AeI;d1H&zrg8bqV!O7(;)^;mdiugd{{2)RAM3{gGga$ARq^n4R
zfq?;};xh}_c|43o5+KJJ6^S!2Fle&e;&V*N&CK%w`7SIowLF;#6pm2L&cMI`@|8cx
zdli$*n9S;pYgn^bi;PRy7O>Z_E@Z4_E8!^NtYK?r1d&->&5S8bDNHHsb685avv^WC
zdYOV5N;pzDQ@Ao2OSrRmAtIXGenpZD3=APf(jaF;!UM#T2fL{V?0aPf28Jk(%;NY&
zkOOZCfLNvVB^jxCC7H>IC8;SziXbft3=9laah`c)i8+}m3Z=!VMR|$2sS5c;3I&P9
z#pU@$DIj)beyKuAzCvDpi9$wVS*k)wMyf(yYI16Eabi)WLP2U#Zf0?DW`16=LP@?t
zVlqfVp(G=-SfL;>JylPWuLzX9ili9o85oLGK-PkSsYn&XQUej7kSfvuu~<QbCWz1i
z5ui{n(gv|~K!h%c07nj(0LKt0>Oh_?26>x@k%y6qk%N(gk?kKB3l}33BO4<JBNHPN
zBg-EaRyL2{LTpvSs8OZ|N*8`brl>|E%r|FXU;r6iWCl_J;`wQcPX5MnI-v-ZZg25A
z7o{eaq&fz8f)YRxC_RHyO%X_G5!f6A0dnyz4x8Nkl+v73J4R5#015Fh2{3}3&cg_y
Kg_s0bM1%k`&%lBJ

delta 362
zcmeAaoXo?Q&&$ijz`(%J+U%bu&N`7#hOud)cD#l=LkedKR|`W5S1NOsKr?fcpgThf
zcM4AnLkdqSM>BJjPzrA_gC^g^b1jn{nItEdF!r*EFfcF_iA-i^QWE81U|;|lTg=A5
zz`()4#mK=}#538BNrtg_ax~K!E=|T;EGda4i6NSk4Vl$AHJOSeL7Jr|CororYEEur
zHe(c@yq7tNF>0~`ivgqN<UkffDH)Iwh$SGFC<6n-EtZ1(;u8MJb6BihG+ByxLE?NM
zf*(ZafCz*JFbkxsNQ{Aj0i>*$1?)T?MzDyVrtsuA_R~R8yv{|bi6yCy0iI!*spUl=
u&wv#aflWXVAhT|9*yQG?l;)(`F)}bP6bmshFz_%6F!F#Qh!$cJVF3VM^+6o~

diff --git a/MisplaceAI/authentication/urls.py b/MisplaceAI/authentication/urls.py
index aeb6386..fdf2fd9 100644
--- a/MisplaceAI/authentication/urls.py
+++ b/MisplaceAI/authentication/urls.py
@@ -1,9 +1,12 @@
 # MisplaceAI/authentication/urls.py
 from django.urls import path
-from .views import RegisterView, LoginView
+from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
+from .views import RegisterView, LoginView, AdminLoginView
 
 urlpatterns = [
     path('register/', RegisterView.as_view(), name='register'),
     path('login/', LoginView.as_view(), name='login'),
- 
+    path('admin/login/', AdminLoginView.as_view(), name='admin_login'),
+    path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
+    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
 ]
diff --git a/MisplaceAI/authentication/views.py b/MisplaceAI/authentication/views.py
index f0eb4cd..e1e96bd 100644
--- a/MisplaceAI/authentication/views.py
+++ b/MisplaceAI/authentication/views.py
@@ -1,4 +1,5 @@
 # MisplaceAI/authentication/views.py
+
 from django.contrib.auth import authenticate
 from rest_framework import generics, status
 from rest_framework.response import Response
@@ -6,6 +7,7 @@ from rest_framework.views import APIView
 from rest_framework_simplejwt.tokens import RefreshToken
 from .serializers import RegisterSerializer, LoginSerializer
 from django.contrib.auth.models import User
+from django.contrib.auth import login
 
 class RegisterView(generics.CreateAPIView):
     queryset = User.objects.all()
@@ -29,5 +31,20 @@ class LoginView(APIView):
             }, status=status.HTTP_200_OK)
         return Response({'error': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST)
 
-
- 
\ No newline at end of file
+class AdminLoginView(APIView):
+    def post(self, request, *args, **kwargs):
+        serializer = LoginSerializer(data=request.data)
+        serializer.is_valid(raise_exception=True)
+        user = authenticate(
+            username=serializer.validated_data['username'],
+            password=serializer.validated_data['password']
+        )
+        if user and user.is_superuser:
+            refresh = RefreshToken.for_user(user)
+            return Response({
+                'refresh': str(refresh),
+                'access': str(refresh.access_token),
+                'is_admin': True,
+                'is_authenticated': True
+            }, status=status.HTTP_200_OK)
+        return Response({'error': 'Invalid username or password or you do not have the necessary permissions to access this page.'}, status=status.HTTP_400_BAD_REQUEST)
diff --git a/frontend/src/forms/Auth/AdminLoginForm.js b/frontend/src/forms/Auth/AdminLoginForm.js
index 6899fa3..3e295dc 100644
--- a/frontend/src/forms/Auth/AdminLoginForm.js
+++ b/frontend/src/forms/Auth/AdminLoginForm.js
@@ -1,7 +1,7 @@
 // src/forms/Auth/AdminLoginForm.js
 import React, { useState } from 'react';
 import { useNavigate } from 'react-router-dom';
-import api from '../../services/api';
+import { adminLogin } from '../../services/auth';  // Import the adminLogin function
 import FormContainer from '../../components/Common/Form/FormContainer';
 import FormField from '../../components/Common/Form/FormField';
 import PasswordInputField from '../../components/Common/Password/PasswordInputField';
@@ -18,11 +18,7 @@ const AdminLoginForm = () => {
     const handleSubmit = async (e) => {
         e.preventDefault();
         try {
-            const response = await api.post('/api/admin-app/login/', { username, password });
-            localStorage.setItem('adminToken', response.data.access);
-            localStorage.setItem('isAdmin', true);
-            localStorage.setItem('username', username);
-            localStorage.setItem('isAuthenticated', true);
+            await adminLogin({ username, password });
             navigate('/admin/dashboard');
             window.location.reload(); // Refresh to update Navbar
         } catch (error) {
diff --git a/frontend/src/forms/Auth/LoginForm.js b/frontend/src/forms/Auth/LoginForm.js
index 930e31c..3c010c7 100644
--- a/frontend/src/forms/Auth/LoginForm.js
+++ b/frontend/src/forms/Auth/LoginForm.js
@@ -1,8 +1,7 @@
 // src/forms/Auth/LoginForm.js
-
 import React, { useState } from 'react';
 import { useNavigate } from 'react-router-dom';
-import api from '../../services/api';
+import { login } from '../../services/auth';
 import FormContainer from '../../components/Common/Form/FormContainer';
 import FormField from '../../components/Common/Form/FormField';
 import PasswordInputField from '../../components/Common/Password/PasswordInputField';
@@ -19,10 +18,7 @@ const LoginForm = () => {
     const handleSubmit = async (e) => {
         e.preventDefault();
         try {
-            const response = await api.post('/api/auth/login/', { username, password });
-            localStorage.setItem('token', response.data.access);
-            localStorage.setItem('username', username);
-            localStorage.setItem('isAuthenticated', true);
+            await login({ username, password });
             navigate('/');
             window.location.reload(); // Refresh to update Navbar
         } catch (error) {
diff --git a/frontend/src/forms/Auth/RegisterForm.js b/frontend/src/forms/Auth/RegisterForm.js
index 41e5e8a..df18d2e 100644
--- a/frontend/src/forms/Auth/RegisterForm.js
+++ b/frontend/src/forms/Auth/RegisterForm.js
@@ -1,7 +1,7 @@
 // src/forms/Auth/RegisterForm.js
 import React, { useState, useEffect, useCallback } from 'react';
 import { useNavigate } from 'react-router-dom';
-import api from '../../services/api';
+import { register } from '../../services/auth';
 import FormContainer from '../../components/Common/Form/FormContainer';
 import FormField from '../../components/Common/Form/FormField';
 import PasswordInputField from '../../components/Common/Password/PasswordInputField';
@@ -79,10 +79,7 @@ const RegisterForm = () => {
             return;
         }
         try {
-            const response = await api.post('/api/auth/register/', { username, email, password, password2 });
-            localStorage.setItem('token', response.data.access);
-            localStorage.setItem('username', username);
-            localStorage.setItem('isAuthenticated', true);
+            await register({ username, email, password });
             navigate('/');
             window.location.reload(); // Refresh to update Navbar
         } catch (error) {
diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js
index 5e6e9d1..d74f49b 100644
--- a/frontend/src/services/api.js
+++ b/frontend/src/services/api.js
@@ -1,5 +1,6 @@
 // src/services/api.js
 import axios from 'axios';
+import { refreshToken } from './auth';  // Import the refresh token function
 
 export const getCsrfToken = () => {
     const cookies = document.cookie.split(';');
@@ -13,25 +14,41 @@ export const getCsrfToken = () => {
 };
 
 const api = axios.create({
-    baseURL: 'http://localhost:8080', // Update this to match your Django backend address
+    baseURL: 'http://localhost:8080',
     headers: {
         'Content-Type': 'application/json',
     },
 });
 
 api.interceptors.request.use(
-    config => {
-        const token = localStorage.getItem('token') || localStorage.getItem('adminToken');
-        const csrfToken = getCsrfToken();
+    async (config) => {
+        let token = localStorage.getItem('token') || localStorage.getItem('adminToken');
         if (token) {
+            const tokenExpiry = localStorage.getItem('tokenExpiry');
+            const now = Math.floor(Date.now() / 1000);
+            if (tokenExpiry && now >= tokenExpiry) {
+                // Token expired, refresh it
+                const newTokens = await refreshToken();
+                if (newTokens) {
+                    token = newTokens.access;
+                } else {
+                    // Refresh token also expired or failed, logout user
+                    localStorage.removeItem('token');
+                    localStorage.removeItem('refresh');
+                    localStorage.removeItem('tokenExpiry');
+                    window.location.href = '/login';
+                    return Promise.reject('Session expired. Please log in again.');
+                }
+            }
             config.headers.Authorization = `Bearer ${token}`;
         }
+        const csrfToken = getCsrfToken();
         if (csrfToken) {
             config.headers['X-CSRFToken'] = csrfToken;
         }
         return config;
     },
-    error => Promise.reject(error)
+    (error) => Promise.reject(error)
 );
 
 export default api;
diff --git a/frontend/src/services/auth.js b/frontend/src/services/auth.js
index 73b599b..42da518 100644
--- a/frontend/src/services/auth.js
+++ b/frontend/src/services/auth.js
@@ -5,6 +5,9 @@ export const login = async (credentials) => {
     const response = await api.post('/api/auth/login/', credentials);
     if (response.data.access) {
         localStorage.setItem('token', response.data.access);
+        localStorage.setItem('refresh', response.data.refresh);
+        const tokenPayload = JSON.parse(atob(response.data.access.split('.')[1]));
+        localStorage.setItem('tokenExpiry', tokenPayload.exp);
         localStorage.setItem('isAuthenticated', true);
     }
     return response.data;
@@ -12,15 +15,53 @@ export const login = async (credentials) => {
 
 export const register = async (userData) => {
     const response = await api.post('/api/auth/register/', userData);
+    if (response.data.access) {
+        localStorage.setItem('token', response.data.access);
+        localStorage.setItem('refresh', response.data.refresh);
+        const tokenPayload = JSON.parse(atob(response.data.access.split('.')[1]));
+        localStorage.setItem('tokenExpiry', tokenPayload.exp);
+        localStorage.setItem('isAuthenticated', true);
+    }
     return response.data;
 };
 
 export const logout = () => {
     localStorage.removeItem('token');
+    localStorage.removeItem('refresh');
+    localStorage.removeItem('tokenExpiry');
     localStorage.removeItem('isAuthenticated');
 };
 
+export const refreshToken = async () => {
+    const refresh = localStorage.getItem('refresh');
+    if (refresh) {
+        try {
+            const response = await api.post('/api/auth/token/refresh/', { refresh });
+            localStorage.setItem('token', response.data.access);
+            const tokenPayload = JSON.parse(atob(response.data.access.split('.')[1]));
+            localStorage.setItem('tokenExpiry', tokenPayload.exp);
+            return response.data;
+        } catch (error) {
+            logout();
+        }
+    }
+    return null;
+};
+
 export const getCurrentUser = async () => {
     const response = await api.get('/api/auth/user/');
     return response.data;
 };
+
+
+export const adminLogin = async (credentials) => {
+    const response = await api.post('/api/auth/admin/login/', credentials);
+    if (response.data.access) {
+        localStorage.setItem('adminToken', response.data.access);
+        localStorage.setItem('isAdmin', true);
+        localStorage.setItem('username', credentials.username);
+        localStorage.setItem('isAuthenticated', true);
+    }
+    return response.data;
+}
+
-- 
GitLab