Fix 400 google OAuth api call

This commit is contained in:
sosokker 2023-10-28 22:41:33 +07:00
parent 1b3fedea36
commit 4203cf517a
9 changed files with 108 additions and 60 deletions

View File

@ -83,10 +83,18 @@ SOCIALACCOUNT_PROVIDERS = {
'client_id': config('GOOGLE_CLIENT_ID'), 'client_id': config('GOOGLE_CLIENT_ID'),
'secret': config('GOOGLE_CLIENT_SECRET'), 'secret': config('GOOGLE_CLIENT_SECRET'),
'key': '' 'key': ''
},
"SCOPE": [
"profile",
"email",
],
"AUTH_PARAMS": {
"access_type": "online",
} }
} }
} }
CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_ALL_ORIGINS = False CORS_ALLOW_ALL_ORIGINS = False
CORS_ALLOWED_ORIGINS = [ CORS_ALLOWED_ORIGINS = [
@ -95,6 +103,10 @@ CORS_ALLOWED_ORIGINS = [
"http://localhost:5173", "http://localhost:5173",
] ]
CSRF_TRUSTED_ORIGINS = ["http://*"]
CORS_ORIGIN_WHITELIST = ["*"]
MIDDLEWARE = [ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', 'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
@ -104,7 +116,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
"allauth.account.middleware.AccountMiddleware", # "allauth.account.middleware.AccountMiddleware",
] ]
ROOT_URLCONF = 'core.urls' ROOT_URLCONF = 'core.urls'
@ -195,4 +207,6 @@ SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/'
AUTH_USER_MODEL = "users.CustomUser" AUTH_USER_MODEL = "users.CustomUser"
ACCOUNT_EMAIL_REQUIRED = True

23
backend/users/adapter.py Normal file
View File

@ -0,0 +1,23 @@
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Error
import jwt
class CustomGoogleOAuth2Adapter(GoogleOAuth2Adapter):
def complete_login(self, request, app, token, response, **kwargs):
try:
identity_data = jwt.decode(
response["id_token"], #another nested id_token was returned
options={
"verify_signature": False,
"verify_iss": True,
"verify_aud": True,
"verify_exp": True,
},
issuer=self.id_token_issuer,
audience=app.client_id,
)
except jwt.PyJWTError as e:
raise OAuth2Error("Invalid id_token") from e
login = self.get_provider().sociallogin_from_response(request, identity_data)
return login

View File

@ -5,10 +5,12 @@ from rest_framework import status
from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from dj_rest_auth.registration.views import SocialLoginView 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 .serializers import MyTokenObtainPairSerializer, CustomUserSerializer
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
class ObtainTokenPairWithCustomView(APIView): class ObtainTokenPairWithCustomView(APIView):
""" """
@ -74,5 +76,7 @@ class GoogleLogin(SocialLoginView):
Google Login View. Google Login View.
Handles Google OAuth2 authentication. Handles Google OAuth2 authentication.
""" """
permission_classes = (AllowAny,) # permission_classes = (AllowAny,)
adapter_class = GoogleOAuth2Adapter adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client
# callback_url = 'http://localhost:8000/accounts/google/login/callback/'

View File

@ -16,11 +16,13 @@
"@material-ui/icons": "^4.11.3", "@material-ui/icons": "^4.11.3",
"@mui/icons-material": "^5.14.15", "@mui/icons-material": "^5.14.15",
"@mui/material": "^5.14.15", "@mui/material": "^5.14.15",
"@react-oauth/google": "^0.11.1",
"axios": "^1.5.1", "axios": "^1.5.1",
"bootstrap": "^5.3.2", "bootstrap": "^5.3.2",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"framer-motion": "^10.16.4", "framer-motion": "^10.16.4",
"gapi-script": "^1.2.0", "gapi-script": "^1.2.0",
"jwt-decode": "^4.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.9.1", "react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -23,6 +23,9 @@ dependencies:
'@mui/material': '@mui/material':
specifier: ^5.14.15 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) 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)
'@react-oauth/google':
specifier: ^0.11.1
version: 0.11.1(react-dom@18.2.0)(react@18.2.0)
axios: axios:
specifier: ^1.5.1 specifier: ^1.5.1
version: 1.5.1 version: 1.5.1
@ -38,6 +41,9 @@ dependencies:
gapi-script: gapi-script:
specifier: ^1.2.0 specifier: ^1.2.0
version: 1.2.0 version: 1.2.0
jwt-decode:
specifier: ^4.0.0
version: 4.0.0
react: react:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
@ -1115,6 +1121,16 @@ packages:
react: 18.2.0 react: 18.2.0
dev: false dev: false
/@react-oauth/google@0.11.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-tywZisXbsdaRBVbEu0VX6dRbOSL2I6DgY97woq5NMOOOz+xtDsm418vqq+Vx10KMtra3kdHMRMf0hXLWrk2RMg==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@remix-run/router@1.10.0: /@remix-run/router@1.10.0:
resolution: {integrity: sha512-Lm+fYpMfZoEucJ7cMxgt4dYt8jLfbpwRCzAjm9UgSLOkmlqo9gupxt6YX3DY0Fk155NT9l17d/ydi+964uS9Lw==} resolution: {integrity: sha512-Lm+fYpMfZoEucJ7cMxgt4dYt8jLfbpwRCzAjm9UgSLOkmlqo9gupxt6YX3DY0Fk155NT9l17d/ydi+964uS9Lw==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@ -2584,6 +2600,11 @@ packages:
object.values: 1.1.7 object.values: 1.1.7
dev: true dev: true
/jwt-decode@4.0.0:
resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==}
engines: {node: '>=18'}
dev: false
/keyv@4.5.4: /keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
dependencies: dependencies:

View File

@ -62,16 +62,20 @@ const apiUserLogout = () => {
} }
// Function for Google login // Function for Google login
const googleLogin = async (accessToken) => { const googleLogin = async (accesstoken) => {
axios.defaults.withCredentials = true
let res = await axios.post( let res = await axios.post(
"http://localhost:8000/api/dj-rest-auth/google/", "http://localhost:8000/api/dj-rest-auth/google/",
{ {
access_token: accessToken, access_token: accesstoken,
id_token: accesstoken,
} }
); );
// console.log('service google login res: ', res);
return await res; return await res;
}; };
// Function to get 'hello' data // Function to get 'hello' data
const getGreeting = () => { const getGreeting = () => {
return axiosInstance return axiosInstance

View File

@ -1,19 +1,20 @@
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react"; import { useState } from "react";
import { import { AnimatePresence, motion } from "framer-motion";
SiFramer, import { SiFramer, SiTailwindcss, SiReact, SiJavascript, SiCss3 } from "react-icons/si";
SiTailwindcss,
SiReact,
SiJavascript,
SiCss3,
} from "react-icons/si";
import homeLogo from "../assets/home.png"; import homeLogo from "../assets/home.png";
import calendarLogo from "../assets/calendar.png"; import calendarLogo from "../assets/calendar.png";
import planLogo from "../assets/planning.png"; import planLogo from "../assets/planning.png";
import pieLogo from "../assets/pie-chart.png"; import pieLogo from "../assets/pie-chart.png";
import plusLogo from "../assets/plus.png"; import plusLogo from "../assets/plus.png";
const menuItems = [
{ id: 0, icon: <homeLogo />, logo: homeLogo },
{ id: 1, icon: <calendarLogo />, logo: calendarLogo },
{ id: 2, icon: <planLogo />, logo: planLogo },
{ id: 3, icon: <pieLogo />, logo: pieLogo },
{ id: 4, icon: <plusLogo />, logo: plusLogo },
];
const IconSideNav = () => { const IconSideNav = () => {
return ( return (
<div className="bg-slate-900 text-slate-100 flex"> <div className="bg-slate-900 text-slate-100 flex">
@ -26,34 +27,6 @@ const IconSideNav = () => {
const SideNav = () => { const SideNav = () => {
const [selected, setSelected] = useState(0); const [selected, setSelected] = useState(0);
const menuItems = [
{
id: 0,
icon: <homeLogo />,
logo: homeLogo,
},
{
id: 1,
icon: <calendarLogo />,
logo: calendarLogo,
},
{
id: 2,
icon: <planLogo />,
logo: planLogo,
},
{
id: 3,
icon: <pieLogo />,
logo: pieLogo,
},
{
id: 4,
icon: <plusLogo />,
logo: plusLogo,
},
];
return ( return (
<nav className="h-[500px] w-fit bg-slate-950 p-4 flex flex-col items-center gap-2"> <nav className="h-[500px] w-fit bg-slate-950 p-4 flex flex-col items-center gap-2">
{menuItems.map((item) => ( {menuItems.map((item) => (
@ -73,7 +46,7 @@ const SideNav = () => {
const NavItem = ({ icon, selected, id, setSelected, logo }) => { const NavItem = ({ icon, selected, id, setSelected, logo }) => {
return ( return (
<motion.button <motion.button
className="p-3 text-xl bg-slate-800 hover:bg-slate-700 rounded-md transition-colors relative" className="p-3 text-xl bg-slate-800 hover-bg-slate-700 rounded-md transition-colors relative"
onClick={() => setSelected(id)} onClick={() => setSelected(id)}
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -96,5 +69,4 @@ const NavItem = ({ icon, selected, id, setSelected, logo }) => {
); );
}; };
export default IconSideNav;
export default IconSideNav;

View File

@ -8,10 +8,8 @@ import { makeStyles } from '@material-ui/core/styles';
import Container from '@material-ui/core/Container'; import Container from '@material-ui/core/Container';
import axiosapi from '../api/axiosapi'; import axiosapi from '../api/axiosapi';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { GoogleLogin } from 'react-google-login'; import { useGoogleLogin } from '@react-oauth/google';
import { useHistory } from 'react-router-dom';
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
// Styles for various elements // Styles for various elements
@ -35,7 +33,7 @@ const useStyles = makeStyles((theme) => ({
})); }));
export default function Login() { export default function Login() {
const history = useNavigate(); const history = useHistory();
const classes = useStyles(); const classes = useStyles();
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
@ -79,7 +77,7 @@ export default function Login() {
const responseGoogle = async (response) => { const responseGoogle = async (response) => {
// Handle Google login response // Handle Google login response
let googleResponse = await axiosapi.googleLogin(response.accessToken); let googleResponse = await axiosapi.googleLogin(response.access_token);
console.log('Google Response:\n', googleResponse); console.log('Google Response:\n', googleResponse);
if (googleResponse.status === 200) { if (googleResponse.status === 200) {
@ -91,6 +89,13 @@ export default function Login() {
} }
} }
const googleLoginflow = useGoogleLogin({
onSuccess: async tokenResponse => {
console.log(tokenResponse);
responseGoogle(tokenResponse);
},
})
return ( return (
<Container component="main" maxWidth="xs"> <Container component="main" maxWidth="xs">
<CssBaseline /> <CssBaseline />
@ -147,13 +152,10 @@ export default function Login() {
</Button> </Button>
</form> </form>
<GoogleLogin
clientId={GOOGLE_CLIENT_ID} <button onClick={() => googleLoginflow()}>
buttonText="Login" Sign in with Google 🚀{' '}
onSuccess={responseGoogle} </button>
onFailure={responseGoogle}
cookiePolicy={'single_host_origin'}
/>
</div> </div>
</Container> </Container>
); );

View File

@ -1,7 +1,13 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom/client";
import App from "./App"; import App from "./App";
import { GoogleLogin, GoogleOAuthProvider} from '@react-oauth/google';
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID
ReactDOM.createRoot(document.getElementById("root")).render( ReactDOM.createRoot(document.getElementById("root")).render(
<GoogleOAuthProvider clientId={GOOGLE_CLIENT_ID}>
<App /> <App />
</GoogleOAuthProvider>
); );