diff --git a/backend/user/urls.py b/backend/user/urls.py index 03c8ed2d..437f66b7 100644 --- a/backend/user/urls.py +++ b/backend/user/urls.py @@ -5,5 +5,6 @@ from user import views urlpatterns = [ path("login/", views.LoginApiView.as_view(), name="api-user-login"), + path("logout/", views.LogoutApiView.as_view(), name="api-user-logout"), path("me/", views.UserConfigView.as_view(), name="api-user-me"), ] diff --git a/backend/user/views.py b/backend/user/views.py index b1ef04f5..4cbb4d71 100644 --- a/backend/user/views.py +++ b/backend/user/views.py @@ -1,7 +1,7 @@ """all user api views""" from common.views import ApiBaseView -from django.contrib.auth import authenticate, login +from django.contrib.auth import authenticate, login, logout from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from rest_framework.permissions import AllowAny @@ -82,3 +82,14 @@ class LoginApiView(APIView): return Response({"message": "Login successful"}, status=200) return Response({"message": "Invalid credentials"}, status=400) + + +class LogoutApiView(ApiBaseView): + """resolves to /api/user/logout/ + POST: handle logout + """ + + def post(self, request, *args, **kwargs): + """logout on post request""" + logout(request) + return Response({"message": "Successfully logged out."}, status=200) diff --git a/frontend/src/api/actions/logOut.ts b/frontend/src/api/actions/logOut.ts new file mode 100644 index 00000000..63e10ee9 --- /dev/null +++ b/frontend/src/api/actions/logOut.ts @@ -0,0 +1,22 @@ +import defaultHeaders from '../../configuration/defaultHeaders'; +import getApiUrl from "../../configuration/getApiUrl" +import getFetchCredentials from "../../configuration/getFetchCredentials"; +import getCookie from "../../functions/getCookie"; + +const logOut = async () => { + const apiUrl = getApiUrl(); + const csrfCookie = getCookie('csrftoken'); + + const response = await fetch(`${apiUrl}/api/user/logout/`, { + method: 'POST', + headers: { + ...defaultHeaders, + 'X-CSRFToken': csrfCookie || '', + }, + credentials: getFetchCredentials(), + }) + + return response; +} + +export default logOut; diff --git a/frontend/src/components/Navigation.tsx b/frontend/src/components/Navigation.tsx index 91a0a4b0..1bccc441 100644 --- a/frontend/src/components/Navigation.tsx +++ b/frontend/src/components/Navigation.tsx @@ -1,15 +1,24 @@ -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import iconSearch from '/img/icon-search.svg'; import iconGear from '/img/icon-gear.svg'; import iconExit from '/img/icon-exit.svg'; import Routes from '../configuration/routes/RouteList'; import NavigationItem from './NavigationItem'; +import logOut from '../api/actions/logOut'; interface NavigationProps { isAdmin: boolean; } const Navigation = ({ isAdmin }: NavigationProps) => { + + const navigate = useNavigate(); + const handleLogout = async (event: { preventDefault: () => void }) => { + event.preventDefault(); + await logOut(); + navigate(Routes.Login) + } + return (