diff --git a/backend/core/settings.py b/backend/core/settings.py
index 7fa2954..f06c5ac 100644
--- a/backend/core/settings.py
+++ b/backend/core/settings.py
@@ -46,19 +46,19 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
-
- 'tasks',
+ 'django.contrib.sites',
+ 'tasks',
'users',
- 'rest_framework',
+
'corsheaders',
- 'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
+ 'rest_framework',
'dj_rest_auth',
'dj_rest_auth.registration',
'rest_framework.authtoken',
@@ -70,10 +70,10 @@ REST_FRAMEWORK = {
],
'DEFAULT_AUTHENTICATION_CLASSES': [
+ 'rest_framework.authentication.BasicAuthentication',
+ 'rest_framework.authentication.TokenAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
- 'rest_framework.authentication.BasicAuthentication',
- 'rest_framework.authentication.SessionAuthentication',
]
}
@@ -105,7 +105,7 @@ CORS_ALLOWED_ORIGINS = [
"http://localhost:5173",
]
-CSRF_TRUSTED_ORIGINS = ["http://*"]
+CSRF_TRUSTED_ORIGINS = ["http://localhost:5173"]
CORS_ORIGIN_WHITELIST = ["*"]
diff --git a/backend/users/serializers.py b/backend/users/serializers.py
index d6deaa5..14d96be 100644
--- a/backend/users/serializers.py
+++ b/backend/users/serializers.py
@@ -24,7 +24,7 @@ class CustomUserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
- fields = ('email', 'username', 'password')
+ fields = ('email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
diff --git a/backend/users/urls.py b/backend/users/urls.py
index 309d11c..fd03be1 100644
--- a/backend/users/urls.py
+++ b/backend/users/urls.py
@@ -1,6 +1,6 @@
from django.urls import path
from rest_framework_simplejwt import views as jwt_views
-from .views import ObtainTokenPairWithCustomView, CustomUserCreate, GreetingView, GoogleLogin
+from .views import ObtainTokenPairWithCustomView, CustomUserCreate, GreetingView, GoogleLogin, GoogleRetrieveUserInfo
urlpatterns = [
path('user/create/', CustomUserCreate.as_view(), name="create_user"),
@@ -9,4 +9,5 @@ urlpatterns = [
path('token/custom_obtain/', ObtainTokenPairWithCustomView.as_view(), name='token_create_custom'),
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())
]
\ No newline at end of file
diff --git a/backend/users/views.py b/backend/users/views.py
index fd20a19..93e0ab0 100644
--- a/backend/users/views.py
+++ b/backend/users/views.py
@@ -1,16 +1,25 @@
"""This module defines API views for authentication, user creation, and a simple hello message."""
-from django.shortcuts import render
+import json
+import requests
+
+from django.contrib.auth.hashers import make_password
+
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
-from dj_rest_auth.registration.views import SocialLoginView
-from allauth.socialaccount.providers.oauth2.client import OAuth2Client
-from .adapter import CustomGoogleOAuth2Adapter
-from .serializers import MyTokenObtainPairSerializer, CustomUserSerializer
+from rest_framework_simplejwt.tokens import RefreshToken
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
+from allauth.socialaccount.providers.oauth2.client import OAuth2Client
+
+from dj_rest_auth.registration.views import SocialLoginView
+
+from .serializers import MyTokenObtainPairSerializer, CustomUserSerializer
+from .managers import CustomAccountManager
+from .models import CustomUser
+
class ObtainTokenPairWithCustomView(APIView):
"""
@@ -78,5 +87,47 @@ class GoogleLogin(SocialLoginView):
"""
# permission_classes = (AllowAny,)
adapter_class = GoogleOAuth2Adapter
- client_class = OAuth2Client
- # callback_url = 'http://localhost:8000/accounts/google/login/callback/'
\ No newline at end of file
+ # client_class = OAuth2Client
+ # callback_url = 'http://localhost:8000/accounts/google/login/callback/'
+
+
+class GoogleRetrieveUserInfo(APIView):
+ """
+ Retrieve user information from Google and create a user if not exists.
+ """
+ def post(self, request):
+ access_token = request.data.get("token")
+
+ user_info = self.get_google_user_info(access_token)
+
+ if 'error' in user_info:
+ error_message = 'Wrong Google token or the token has expired.'
+ return Response({'message': error_message, 'error': user_info['error']})
+
+ user = self.get_or_create_user(user_info)
+ token = RefreshToken.for_user(user)
+
+ response = {
+ 'username': user.username,
+ 'access_token': str(token.access_token),
+ 'refresh_token': str(token),
+ }
+
+ return Response(response)
+
+ def get_google_user_info(self, access_token):
+ url = 'https://www.googleapis.com/oauth2/v2/userinfo'
+ payload = {'access_token': access_token}
+ response = requests.get(url, params=payload)
+ return json.loads(response.text)
+
+ def get_or_create_user(self, user_info):
+ try:
+ user = CustomUser.objects.get(email=user_info['email'])
+ except CustomUser.DoesNotExist:
+ user = CustomUser()
+ user.username = user_info['email']
+ user.password = make_password(CustomAccountManager().make_random_password())
+ user.email = user_info['email']
+ user.save()
+ return user
\ No newline at end of file
diff --git a/frontend/package.json b/frontend/package.json
index 98acb6d..7ff9906 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -16,6 +16,7 @@
"@material-ui/icons": "^4.11.3",
"@mui/icons-material": "^5.14.15",
"@mui/material": "^5.14.15",
+ "@mui/system": "^5.14.15",
"@react-oauth/google": "^0.11.1",
"axios": "^1.5.1",
"bootstrap": "^5.3.2",
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index d085dbb..18613e9 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -23,6 +23,9 @@ dependencies:
'@mui/material':
specifier: ^5.14.15
version: 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
+ '@mui/system':
+ specifier: ^5.14.15
+ version: 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.33)(react@18.2.0)
'@react-oauth/google':
specifier: ^0.11.1
version: 0.11.1(react-dom@18.2.0)(react@18.2.0)
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 658d5e3..0ac97eb 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,38 +1,18 @@
-// import './App.css';
-// import { Routes, Route, Link } from "react-router-dom";
-// import Login from "./components/login";
-// import TestAuth from './components/testAuth';
-
-// function App() {
-// return (
-//
-//
-// Home
-// Login
-// testAuth
-//
-//
-//
-//
-// This is Home page! } />
-//
-//
-// );
-// }
-
-// export default App;
-
import './App.css';
-import {BrowserRouter, Route, Routes, Link} from "react-router-dom";
-import Login from "./components/login";
+import { BrowserRouter, Route, Routes, Link } from 'react-router-dom';
+
import TestAuth from './components/testAuth';
-import Signup from './components/signup';
-import IconSideNav from "./components/IconSideNav";
+import IconSideNav from './components/IconSideNav';
+import AuthenticantionPage from './components/authentication/AuthenticationPage';
+import SignUpPage from './components/authentication/SignUpPage';
+import NavBar from './components/Nav/Navbar';
+
const App = () => {
return (
+
Home
Login
@@ -41,14 +21,14 @@ const App = () => {
This is Home page! } />
- }/>
- }/>
+ }/>
+ }/>
}/>
- */}
);
}
diff --git a/frontend/src/api/axiosapi.jsx b/frontend/src/api/axiosapi.jsx
index a96be14..211cf14 100644
--- a/frontend/src/api/axiosapi.jsx
+++ b/frontend/src/api/axiosapi.jsx
@@ -62,13 +62,12 @@ const apiUserLogout = () => {
}
// Function for Google login
-const googleLogin = async (accesstoken) => {
+const googleLogin = async (token) => {
axios.defaults.withCredentials = true
let res = await axios.post(
- "http://localhost:8000/api/dj-rest-auth/google/",
+ "http://localhost:8000/api/auth/google/",
{
- access_token: accesstoken,
- id_token: accesstoken,
+ token: token,
}
);
// console.log('service google login res: ', res);
diff --git a/frontend/src/components/Nav/Navbar.jsx b/frontend/src/components/Nav/Navbar.jsx
new file mode 100644
index 0000000..93cbd08
--- /dev/null
+++ b/frontend/src/components/Nav/Navbar.jsx
@@ -0,0 +1,177 @@
+import * as React from 'react';
+import { Link } from 'react-router-dom';
+import AppBar from '@mui/material/AppBar';
+import Box from '@mui/material/Box';
+import Toolbar from '@mui/material/Toolbar';
+import IconButton from '@mui/material/IconButton';
+import Typography from '@mui/material/Typography';
+import Menu from '@mui/material/Menu';
+import MenuIcon from '@mui/icons-material/Menu';
+import Container from '@mui/material/Container';
+import Avatar from '@mui/material/Avatar';
+import Button from '@mui/material/Button';
+import Tooltip from '@mui/material/Tooltip';
+import MenuItem from '@mui/material/MenuItem';
+import AdbIcon from '@mui/icons-material/Adb';
+
+const pages = {
+ TestAuth: '/testAuth',
+
+};
+const settings = {
+ Profile: '/profile',
+ Account: '/account',
+ Dashboard: '/dashboard',
+ Logout: '/logout',
+ };
+
+function NavBar() {
+ const [anchorElNav, setAnchorElNav] = React.useState(null);
+ const [anchorElUser, setAnchorElUser] = React.useState(null);
+
+ const handleOpenNavMenu = (event) => {
+ setAnchorElNav(event.currentTarget);
+ };
+ const handleOpenUserMenu = (event) => {
+ setAnchorElUser(event.currentTarget);
+ };
+
+ const handleCloseNavMenu = () => {
+ setAnchorElNav(null);
+ };
+
+ const handleCloseUserMenu = () => {
+ setAnchorElUser(null);
+ };
+
+ return (
+
+
+
+
+
+ LOGO
+
+
+
+
+
+
+
+
+
+
+ LOGO
+
+
+ {Object.entries(pages).map(([page, path]) => (
+
+ {page}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+export default NavBar;
\ No newline at end of file
diff --git a/frontend/src/components/authentication/AuthenticationPage.jsx b/frontend/src/components/authentication/AuthenticationPage.jsx
new file mode 100644
index 0000000..791cdd9
--- /dev/null
+++ b/frontend/src/components/authentication/AuthenticationPage.jsx
@@ -0,0 +1,215 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useGoogleLogin } from '@react-oauth/google';
+
+import Avatar from '@mui/material/Avatar';
+import Button from '@mui/material/Button';
+import CssBaseline from '@mui/material/CssBaseline';
+import TextField from '@mui/material/TextField';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import Checkbox from '@mui/material/Checkbox';
+import Link from '@mui/material/Link';
+import Divider from '@mui/material/Divider';
+import Paper from '@mui/material/Paper';
+import Box from '@mui/material/Box';
+import Grid from '@mui/material/Grid';
+import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
+import Typography from '@mui/material/Typography';
+import { createTheme, ThemeProvider } from '@mui/material/styles';
+
+import axiosapi from '../../api/axiosapi';
+
+
+function Copyright(props) {
+ return (
+
+ {'Copyright © '}
+
+ Your Website
+ {' '}
+ {new Date().getFullYear()}
+ {'.'}
+
+ );
+}
+
+
+const defaultTheme = createTheme();
+
+export default function SignInSide() {
+
+ const Navigate = useNavigate();
+
+ const [email, setEmail] = useState("");
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+
+ const handleUsernameChange = (event) => {
+ setUsername(event.target.value);
+ }
+
+ const handleEmailChange = (event) => {
+ setEmail(event.target.value);
+ }
+
+ const handlePasswordChange = (event) => {
+ setPassword(event.target.value);
+ }
+
+ const handleSubmit = (event) => {
+ event.preventDefault();
+
+ // Send a POST request to the authentication API
+ axiosapi.apiUserLogin({
+ email: email,
+ username: username,
+ password: password
+ }).then(res => {
+ // On successful login, store tokens and set the authorization header
+ localStorage.setItem('access_token', res.data.access);
+ localStorage.setItem('refresh_token', res.data.refresh);
+ axiosapi.axiosInstance.defaults.headers['Authorization'] = "Bearer " + res.data.access;
+ Navigate('/');
+ }).catch(err => {
+ console.log('Login failed'); // Handle login failure
+ console.log(err)
+ });
+ }
+
+ const responseGoogle = async (response) => {
+ // Handle Google login response
+ let googleResponse = await axiosapi.googleLogin(response.access_token);
+ console.log('Google Response:\n', googleResponse);
+
+ if (googleResponse.status === 200) {
+ // Store Google login tokens and set the authorization header on success
+ localStorage.setItem('access_token', googleResponse.data.access_token);
+ localStorage.setItem('refresh_token', googleResponse.data.refresh_token);
+ axiosapi.axiosInstance.defaults.headers['Authorization'] = "Bearer " + googleResponse.data.access_token;
+ Navigate('/');
+ }
+ }
+
+ const googleLoginImplicit = useGoogleLogin({
+ // flow: 'auth-code',
+ onSuccess: async (response) => {
+ console.log(response);
+
+ try {
+ const loginResponse = await axiosapi.googleLogin(response.access_token);
+ if (loginResponse && loginResponse.data) {
+ const { access_token, refresh_token } = loginResponse.data;
+
+ // Save the tokens in localStorage
+ localStorage.setItem('access_token', access_token);
+ localStorage.setItem('refresh_token', refresh_token);
+ Navigate('/');
+ }
+ } catch (error) {
+ console.error('Error with the POST request:', error);
+ }
+ },
+ onError: errorResponse => console.log(errorResponse),
+ });
+
+
+ return (
+
+
+
+
+ t.palette.mode === 'light' ? t.palette.grey[50] : t.palette.grey[900],
+ backgroundSize: 'cover',
+ backgroundPosition: 'center',
+ }}
+ />
+
+
+
+
+
+
+ Sign in
+
+
+
+
+ }
+ label="Remember me"
+ />
+
+ Sign In
+
+ OR
+
+ googleLoginImplicit()}
+ >
+ Sign in with Google 🚀
+
+
+
+
+
+ Forgot password?
+
+
+
+
+ {"Don't have an account? Sign Up"}
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/authentication/IsAuthenticated.jsx b/frontend/src/components/authentication/IsAuthenticated.jsx
new file mode 100644
index 0000000..48edd73
--- /dev/null
+++ b/frontend/src/components/authentication/IsAuthenticated.jsx
@@ -0,0 +1,48 @@
+import { useState, useEffect } from 'react';
+import axiosapi from './axiosapi';
+
+function IsAuthenticated() {
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
+
+ useEffect(() => {
+ const checkAuthentication = async () => {
+ const access_token = localStorage.getItem('access_token');
+ const refresh_token = localStorage.getItem('refresh_token');
+
+ if (access_token && refresh_token) {
+ const isAccessTokenExpired = checkIfAccessTokenExpired(access_token);
+
+ if (!isAccessTokenExpired) {
+ setIsAuthenticated(true);
+ } else {
+ try {
+ // Attempt to refresh the access token using the refresh token
+ const response = await axiosapi.refreshAccessToken(refresh_token);
+ if (response.status === 200) {
+ const newAccessToken = response.data.access_token;
+ localStorage.setItem('access_token', newAccessToken);
+ setIsAuthenticated(true);
+ } else {
+ setIsAuthenticated(false);
+ }
+ } catch (error) {
+ setIsAuthenticated(false);
+ }
+ }
+ } else {
+ setIsAuthenticated(false);
+ }
+ };
+
+ checkAuthentication();
+ }, []);
+
+ const checkIfAccessTokenExpired = (accessToken) => {
+ // Need to change logic again!
+ return !accessToken;
+ };
+
+ return isAuthenticated;
+}
+
+export default IsAuthenticated;
diff --git a/frontend/src/components/authentication/SignUpPage.jsx b/frontend/src/components/authentication/SignUpPage.jsx
new file mode 100644
index 0000000..3e33a86
--- /dev/null
+++ b/frontend/src/components/authentication/SignUpPage.jsx
@@ -0,0 +1,151 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import axiosapi from '../../api/axiosapi';
+
+import Avatar from '@mui/material/Avatar';
+import Button from '@mui/material/Button';
+import CssBaseline from '@mui/material/CssBaseline';
+import TextField from '@mui/material/TextField';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import Checkbox from '@mui/material/Checkbox';
+import Link from '@mui/material/Link';
+import Grid from '@mui/material/Grid';
+import Box from '@mui/material/Box';
+import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
+import Typography from '@mui/material/Typography';
+import Container from '@mui/material/Container';
+import { createTheme, ThemeProvider } from '@mui/material/styles';
+
+
+function Copyright(props) {
+ return (
+
+ {'Copyright © '}
+
+ Your Website
+ {' '}
+ {new Date().getFullYear()}
+ {'.'}
+
+ );
+}
+
+const defaultTheme = createTheme();
+
+export default function SignUp() {
+
+ const Navigate = useNavigate();
+
+ const [formData, setFormData] = useState({
+ email: '',
+ username: '',
+ password: '',
+ });
+ const [error, setError] = useState(null);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setIsSubmitting(true);
+ setError(null);
+
+ try {
+ axiosapi.createUser(formData);
+ } catch (error) {
+ console.error('Error creating user:', error);
+ setError('Registration failed. Please try again.');
+ } finally {
+ setIsSubmitting(false);
+ }
+ Navigate('/login');
+ };
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({ ...formData, [name]: value });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ Sign up
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ label="I want to receive inspiration, marketing promotions and updates via email."
+ />
+
+
+
+ Sign Up
+
+
+
+
+ Already have an account? Sign in
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/login.jsx b/frontend/src/components/login.jsx
deleted file mode 100644
index b088523..0000000
--- a/frontend/src/components/login.jsx
+++ /dev/null
@@ -1,161 +0,0 @@
-import React, { useState } from 'react';
-import Avatar from '@material-ui/core/Avatar';
-import Button from '@material-ui/core/Button';
-import CssBaseline from '@material-ui/core/CssBaseline';
-import TextField from '@material-ui/core/TextField';
-import Typography from '@material-ui/core/Typography';
-import { makeStyles } from '@material-ui/core/styles';
-import Container from '@material-ui/core/Container';
-import axiosapi from '../api/axiosapi';
-import { useNavigate } from 'react-router-dom';
-import { useGoogleLogin } from '@react-oauth/google';
-
-const useStyles = makeStyles((theme) => ({
- // Styles for various elements
- paper: {
- marginTop: theme.spacing(8),
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- },
- avatar: {
- margin: theme.spacing(1),
- backgroundColor: theme.palette.secondary.main,
- },
- form: {
- width: '100%',
- marginTop: theme.spacing(1),
- },
- submit: {
- margin: theme.spacing(3, 0, 2),
- },
-}));
-
-export default function Login() {
- const history = useNavigate();
- const classes = useStyles();
-
- const [email, setEmail] = useState("");
- const [username, setUsername] = useState("");
- const [password, setPassword] = useState("");
-
- const handleUsernameChange = (event) => {
- // Update the 'username' state when the input field changes
- setUsername(event.target.value);
- }
-
- const handleEmailChange = (event) => {
- // Update the 'email' state when the email input field changes
- setEmail(event.target.value);
- }
-
- const handlePasswordChange = (event) => {
- // Update the 'password' state when the password input field changes
- setPassword(event.target.value);
- }
-
- const handleSubmit = (event) => {
- event.preventDefault();
-
- // Send a POST request to the authentication API
- axiosapi.apiUserLogin({
- email: email,
- username: username,
- password: password
- }).then(res => {
- // On successful login, store tokens and set the authorization header
- localStorage.setItem('access_token', res.data.access);
- localStorage.setItem('refresh_token', res.data.refresh);
- axiosapi.axiosInstance.defaults.headers['Authorization'] = "Bearer " + res.data.access;
- history.push('/testAuth');
- }).catch(err => {
- console.log('Login failed'); // Handle login failure
- console.log(err)
- });
- }
-
- const responseGoogle = async (response) => {
- // Handle Google login response
- let googleResponse = await axiosapi.googleLogin(response.access_token);
- console.log('Google Response:\n', googleResponse);
-
- if (googleResponse.status === 200) {
- // Store Google login tokens and set the authorization header on success
- localStorage.setItem('access_token', googleResponse.data.access_token);
- localStorage.setItem('refresh_token', googleResponse.data.refresh_token);
- axiosapi.axiosInstance.defaults.headers['Authorization'] = "Bearer " + googleResponse.data.access_token;
- history.push('/testAuth');
- }
- }
-
- const googleLoginflow = useGoogleLogin({
- onSuccess: async tokenResponse => {
- console.log(tokenResponse);
- responseGoogle(tokenResponse);
- },
- })
-
- return (
-
-
-
-
-
- Sign in
-
-
-
-
-
googleLoginflow()}>
- Sign in with Google 🚀{' '}
-
-
-
- );
-}
diff --git a/frontend/src/components/testAuth.jsx b/frontend/src/components/testAuth.jsx
index 937a046..9677133 100644
--- a/frontend/src/components/testAuth.jsx
+++ b/frontend/src/components/testAuth.jsx
@@ -4,7 +4,7 @@ import Button from '@material-ui/core/Button';
import { useNavigate } from 'react-router-dom';
function TestAuth() {
- let history = useNavigate();
+ let Navigate = useNavigate();
const [message, setMessage] = useState("");
@@ -22,19 +22,19 @@ function TestAuth() {
const logout = () => {
// Log out the user, clear tokens, and navigate to the "/testAuth" route
axiosapi.apiUserLogout();
- history('/testAuth');
+ Navigate('/testAuth');
}
return (
{message !== "" && (
-
Hello!
+ Login! Hello!
{message}
Logout
)}
- {message === "" &&
Need to sign in }
+ {message === "" &&
Need to sign in, No authentication found }
);
}
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index af048e7..7aa4bf4 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -1,13 +1,12 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
-import { GoogleLogin, GoogleOAuthProvider} from '@react-oauth/google';
+import { GoogleOAuthProvider} from '@react-oauth/google';
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID
-
ReactDOM.createRoot(document.getElementById("root")).render(
-
+
);
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 81c4d2f..e5171c0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,4 +8,7 @@ djangorestframework>=3.14
markdown>=3.5
django-filter>=23.3
djangorestframework-simplejwt>=5.3
-django-cors-headers>=4.3
\ No newline at end of file
+django-cors-headers>=4.3
+google_api_python_client>=2.1
+google_auth_oauthlib>=1.1
+google-auth-httplib2>=0.1
\ No newline at end of file