diff --git a/backend/authentications/urls.py b/backend/authentications/urls.py index 32965a4..75c5914 100644 --- a/backend/authentications/urls.py +++ b/backend/authentications/urls.py @@ -1,6 +1,6 @@ from django.urls import path from rest_framework_simplejwt import views as jwt_views -from authentications.views import ObtainTokenPairWithCustomView, GreetingView, GoogleLogin, GoogleRetrieveUserInfo +from authentications.views import ObtainTokenPairWithCustomView, GreetingView, GoogleLogin, GoogleRetrieveUserInfo, CheckAccessTokenAndRefreshToken urlpatterns = [ path('token/obtain/', jwt_views.TokenObtainPairView.as_view(), name='token_create'), @@ -9,4 +9,5 @@ urlpatterns = [ path('hello/', GreetingView.as_view(), name='hello_world'), path('dj-rest-auth/google/', GoogleLogin.as_view(), name="google_login"), path('auth/google/', GoogleRetrieveUserInfo.as_view()), + path('auth/status/', CheckAccessTokenAndRefreshToken.as_view(), name='check_token_status') ] \ No newline at end of file diff --git a/backend/authentications/views.py b/backend/authentications/views.py index 9b5f249..167581b 100644 --- a/backend/authentications/views.py +++ b/backend/authentications/views.py @@ -1,6 +1,3 @@ -from django.shortcuts import render - -# Create your views here. """This module defines API views for authentication, user creation, and a simple hello message.""" import json @@ -14,6 +11,8 @@ from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_simplejwt.tokens import RefreshToken +from rest_framework_simplejwt.authentication import JWTAuthentication + from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter @@ -27,6 +26,31 @@ from users.managers import CustomAccountManager from users.models import CustomUser +class CheckAccessTokenAndRefreshToken(APIView): + permission_classes = (AllowAny,) + JWT_authenticator = JWTAuthentication() + + def post(self, request, *args, **kwargs): + access_token = request.data.get('access_token') + refresh_token = request.data.get('refresh_token') + # Check if the access token is valid + if access_token: + response = self.JWT_authenticator.authenticate(request) + if response is not None: + return Response({'status': 'true'}, status=status.HTTP_200_OK) + + # Check if the refresh token is valid + if refresh_token: + try: + refresh = RefreshToken(refresh_token) + access_token = str(refresh.access_token) + return Response({'access_token': access_token}, status=status.HTTP_200_OK) + except Exception as e: + return Response({'status': 'false'}, status=status.HTTP_401_UNAUTHORIZED) + + return Response({'status': 'false'}, status=status.HTTP_400_BAD_REQUEST) + + class ObtainTokenPairWithCustomView(APIView): """ Custom Token Obtain Pair View. @@ -165,4 +189,4 @@ class GoogleRetrieveUserInfo(APIView): response = requests.get(api_url, headers=headers) if response.status_code == 200: return response.json() - raise Exception('Google API Error', response) + raise Exception('Google API Error', response) \ No newline at end of file diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json new file mode 100644 index 0000000..af4aef6 --- /dev/null +++ b/frontend/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "src/*": ["./src/*"] + } + } +} diff --git a/frontend/package.json b/frontend/package.json index 714e1c7..70698dc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,6 +37,7 @@ "framer-motion": "^10.16.4", "gapi-script": "^1.2.0", "jwt-decode": "^4.0.0", + "prop-types": "^15.8.1", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", "react-bootstrap": "^2.9.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 26c8422..143df2b 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -86,6 +86,9 @@ dependencies: jwt-decode: specifier: ^4.0.0 version: 4.0.0 + prop-types: + specifier: ^15.8.1 + version: 15.8.1 react: specifier: ^18.2.0 version: 18.2.0 @@ -3380,14 +3383,6 @@ packages: warning: 4.0.3 dev: false - /react-day-picker@8.9.1(date-fns@2.30.0)(react@18.2.0): - resolution: {integrity: sha512-W0SPApKIsYq+XCtfGeMYDoU0KbsG3wfkYtlw8l+vZp6KoBXGOlhzBUp4tNx1XiwiOZwhfdGOlj7NGSCKGSlg5Q==} - peerDependencies: - date-fns: ^2.28.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - date-fns: 2.30.0 - react: 18.2.0 /react-calendar@4.6.1(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-MvCPdvxEvq7wICBhFxlYwxS2+IsVvSjTcmlr0Kl3yDRVhoX7btNg0ySJx5hy9rb1eaM4nDpzQcW5c87nfQ8n8w==} peerDependencies: @@ -3479,6 +3474,16 @@ packages: - '@types/react-dom' dev: false + /react-day-picker@8.9.1(date-fns@2.30.0)(react@18.2.0): + resolution: {integrity: sha512-W0SPApKIsYq+XCtfGeMYDoU0KbsG3wfkYtlw8l+vZp6KoBXGOlhzBUp4tNx1XiwiOZwhfdGOlj7NGSCKGSlg5Q==} + peerDependencies: + date-fns: ^2.28.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + date-fns: 2.30.0 + react: 18.2.0 + dev: false + /react-dom@18.2.0(react@18.2.0): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -3511,8 +3516,6 @@ packages: tiny-warning: 1.0.3 dev: false - /react-icons@4.11.0(react@18.2.0): - resolution: {integrity: sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==} /react-icons@4.12.0(react@18.2.0): resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==} peerDependencies: @@ -3611,18 +3614,6 @@ packages: react-transition-group: 2.9.0(react-dom@18.2.0)(react@18.2.0) dev: false - /react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} - peerDependencies: - react: '>=15.0.0' - react-dom: '>=15.0.0' - dependencies: - dom-helpers: 3.4.0 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-lifecycles-compat: 3.0.4 /react-time-picker@6.5.2(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-xRamxjndpq3HfnEL+6T3VyirLMEn4D974OJgs9sTP8iJX/RB02rmwy09C9oBThTGuN3ycbozn06iYLn148vcdw==} peerDependencies: @@ -3648,6 +3639,20 @@ packages: - '@types/react-dom' dev: false + /react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} + peerDependencies: + react: '>=15.0.0' + react-dom: '>=15.0.0' + dependencies: + dom-helpers: 3.4.0 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-lifecycles-compat: 3.0.4 + dev: false + /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index b6d42b2..83ebef6 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,6 +1,7 @@ +import { useEffect } from "react"; import "./App.css"; -import { Route, Routes, useLocation } from "react-router-dom"; - +import { Route, Routes } from "react-router-dom"; +import axios from "axios"; import TestAuth from "./components/testAuth"; import LoginPage from "./components/authentication/LoginPage"; import SignUpPage from "./components/authentication/SignUpPage"; @@ -13,18 +14,66 @@ import PrivateRoute from "./PrivateRoute"; import ProfileUpdatePage from "./components/profilePage"; import Dashboard from "./components/dashboard/dashboard"; +import { useAuth } from "./hooks/AuthHooks"; const App = () => { - const location = useLocation(); - const prevention = ["/login", "/signup"]; - const isLoginPageOrSignUpPage = prevention.some(_ => location.pathname.includes(_)); + const { isAuthenticated, setIsAuthenticated } = useAuth(); + useEffect(() => { + const checkLoginStatus = async () => { + const data = { + access_token: localStorage.getItem("access_token"), + refresh_token: localStorage.getItem("refresh_token"), + }; + + await axios + .post("http://127.0.0.1:8000/api/auth/status/", data, { + headers: { + Authorization: "Bearer " + localStorage.getItem("access_token"), + }, + }) + .then((response) => { + if (response.status === 200) { + if (response.data.access_token) { + localStorage.setItem("access_token", response.data.access_token); + setIsAuthenticated(true); + } else { + setIsAuthenticated(true); + } + } else { + setIsAuthenticated(false); + } + }) + .catch((error) => { + console.error("Error checking login status:", error.message); + }); + }; + + checkLoginStatus(); + }, [setIsAuthenticated]); + + return