- Published on
[TIL] Google Oauth2 with ReactJS x Django - The easy way
Overview of the Flow
1. The user clicks “Continue/Login with Google.”
2. The frontend redirects the user to the Google User Consent Screen.
3. The user signs in to Google.
4. Google returns an Authentication Code.
5. The frontend sends this Authentication Code to the backend API.
6. The backend uses the Google API to exchange the Authentication Code for user information.
7. If the user does not already exist, the backend creates a new user.
8. The backend generates a JWT token and returns it to the frontend.
First thing, you need to create a project in Google Console.
In that project, create an API credential Api -> Console and retrieve the client_id and client_secret to your settings.py:
pythonGOOGLE_OAUTH2_CLIENT_ID = "Your_google_client_id"GOOGLE_OAUTH2_CLIENT_SECRET = "Your_google_client_secret"
Django
To implement the login button, I use the @react-oauth/google package to design the button and handle the redirection to the Google User Consent Screen. When the user clicks “Login with Google,” they are directed to the User Consent Screen, where they choose their account and grant permissions to your app.
You can see the demo here
I choose the Authorization Code Flow with backend API.
Frontend implementation
javascriptconst googleLogin = useGoogleLogin({onSuccess: async ({ code }) => {const tokens = await axios.post('http://localhost:5000/auth/google', { // http://localhost:5000/auth/google backend that will exchange the codecode,});console.log(tokens);},flow: 'auth-code',});
Note, this flow use postmessage as redirect_uri as mentioned here https://github.com/MomenSherif/react-oauth/issues/252#issuecomment-1514552320
Retrieving User Data from Google
Once the user logs in with their Google account, the frontend sends an authorization code to the backend (http://localhost:5000/auth/google). This code is then used to request the user’s data from Google.
Serializer for Authorization Code
python# serializers.pyfrom rest_framework import serializersclass AuthSerializer(serializers.Serializer):code = serializers.CharField(required=True, allow_null=False, allow_blank=False)
The post method handles the request:
python# views.pyfrom rest_framework.response import Responsefrom rest_framework.views import APIViewfrom rest_framework_simplejwt.tokens import RefreshTokenfrom libs.google import google_get_user_datafrom ..models import Userfrom ..serializers import AuthSerializerlogger = logging.getLogger(__name__)class GoogleLoginView(APIView):serializer_class = AuthSerializerdef post(self, request, *args, **kwargs):auth_serializer = self.serializer_class(data=request.data)auth_serializer.is_valid(raise_exception=True)validated_data = auth_serializer.validated_data# get user data from googleuser_data = google_get_user_data(validated_data)# Creates user in DB if first time loginuser, _ = User.objects.get_or_create(email=user_data.get('email'),first_name=user_data.get('given_name'),last_name=user_data.get('given_name'),provider=User.PROVIDER_GOOGLE,)# generate jwt token for the usertoken = RefreshToken.for_user(user)response = {'access': str(token.access_token),'refresh': str(token),}return Response(response, status=200)
Get user data by using that Authorization Code
python# libs/google.pyfrom typing import Anyfrom typing import Dictimport requestsfrom django.conf import settingsfrom rest_framework.exceptions import APIExceptionGOOGLE_ID_TOKEN_INFO_URL = 'https://www.googleapis.com/oauth2/v3/tokeninfo'GOOGLE_ACCESS_TOKEN_OBTAIN_URL = 'https://accounts.google.com/o/oauth2/token'GOOGLE_USER_INFO_URL = 'https://www.googleapis.com/oauth2/v3/userinfo'# Exchange authorization token with access token# https://developers.google.com/identity/protocols/oauth2/web-server#obtainingaccesstokensdef google_get_access_token(code: str, redirect_uri: str) -> str:data = {'code': code,'client_id': settings.GOOGLE_OAUTH2_CLIENT_ID,'client_secret': settings.GOOGLE_OAUTH2_CLIENT_SECRET,'redirect_uri': redirect_uri,'grant_type': 'authorization_code',}response = requests.post(GOOGLE_ACCESS_TOKEN_OBTAIN_URL, data=data)if not response.ok:raise APIException(f'Could not get access token from Google: {response.json()}')access_token = response.json()['access_token']return access_token# Get user info from google# https://developers.google.com/identity/protocols/oauth2/web-server#callinganapidef google_get_user_info(access_token: str) -> Dict[str, Any]:response = requests.get(GOOGLE_USER_INFO_URL,params={'access_token': access_token},)if not response.ok:raise APIException('Could not get user info from Google: {response.json()}')return response.json()def google_get_user_data(validated_data):# https://github.com/MomenSherif/react-oauth/issues/252redirect_uri = 'postmessage'code = validated_data.get('code')access_token = google_get_access_token(code=code, redirect_uri=redirect_uri)return google_get_user_info(access_token=access_token)
This blog covers the implementation of Google OAuth2 authentication using Django for the backend and ReactJS for the frontend. I hope it can explain the complete flow of user authentication, including how to create Google API credentials, handle user consent, and retrieve user data from Google.
- Published on