logo
Published on

[TIL] Google Oauth2 with ReactJS x Django - The easy way

Learn how to implement Google OAuth2 authentication with a Django backend and ReactJS frontend. This comprehensive guide walks you through setting up Google API credentials, handling user login and consent, and retrieving user data from Google. Follow detailed steps for integrating Google login using @react-oauth/google in ReactJS and creating secure backend APIs with Django to manage JWT tokens and user information. Perfect for developers looking to integrate Google authentication into their web applications, this tutorial includes practical code examples and best practices for seamless user authentication.

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.

Prerequisite

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:

GOOGLE_OAUTH2_CLIENT_ID = "Your_google_client_id"
GOOGLE_OAUTH2_CLIENT_SECRET = "Your_google_client_secret"

Django

  • rest_framework
  • rest_framework_simplejwt
  • requests

Implementation

Frontend ReactJS

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

const 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 code
      code,
    })

    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

Backend Django

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

# serializers.py
from rest_framework import serializers

class AuthSerializer(serializers.Serializer):
    code = serializers.CharField(required=True, allow_null=False, allow_blank=False)

The post method handles the request:

  • Validates the incoming data using AuthSerializer.
  • Retrieves user data from Google.
  • Checks if the user exists in the database; if not, creates a new user.
  • Generates a JWT token and sends it back to the frontend.
# views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from libs.google import google_get_user_data

from ..models import User
from ..serializers import AuthSerializer

logger = logging.getLogger(__name__)


class GoogleLoginView(APIView):
    serializer_class = AuthSerializer

    def 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 google
        user_data = google_get_user_data(validated_data)

        # Creates user in DB if first time login
        user, _ = 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 user
        token = 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

# libs/google.py
from typing import Any
from typing import Dict

import requests
from django.conf import settings
from rest_framework.exceptions import APIException

GOOGLE_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#obtainingaccesstokens
def 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#callinganapi
def 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/252
    redirect_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)
  • google_get_access_token Exchanges the Authorization Code for an access_token.
  • google_get_user_info Retrieves user information using the access_token.

Summary

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.

Reference

Other articles
More articles ➜