The simplest way to add Google sign-in to your Django app ✍️

Photo of Tom Dekan
by Tom Dekan
Updated: Fri 19 July 2024

All the guides I've seen about adding Google Sign to Django make it overly complicated (e.g., Django-allauth or Django-social-auth).

We'll add Google sign in the simplest way by:

  1. rendering an HTML page with a Google script tag that adds a sign-in button. When clicked, the user will see a Google sign-in pop up.
  2. after sign in, Google will redirect the user (with a POST) to a specified page on our site.
  3. we'll get the user's Google information (e.g., email) from the POST request

👉 Here's an online example of our final outcome here from my product to generate UI quickly.


The finished product using Django and Google sign-in will look like this ✍️:


I've also made a simple video guide (featuring me 🏇🏿) that follows the below instructions. Here's the video: (Edit: You'll need to follow the instructions to use a popup instead of a redirect in the video. The redirect option no longer works in local development (e.g., localhost), despite working in production.

Let's get started 🚀

0. Setup our Django app

  • Install packages and create our Django app
pip install django python-dotenv google-api-python-client
django-admin startproject core .
python manage.py startapp sim
  • Add our app sim to the INSTALLED_APPS in settings.py:
# settings.py
INSTALLED_APPS = [
    'sim',
    ...
]

This might be foreign to you, but it's quick to do (despite Google Cloud Platform's confusing documentation).

a. Create a project

  • Open the Credentials page of the Google APIs console
  • Create a Google APIs project.
  • Select your project. This is a gotcha: you need to select your project after you create it. Select your project
  • Click Credentials > Create credentials > OAuth client ID.
  • Click Configure the consent screen > External > Create. Complete the form (I added example.com as the Authorized domain).(Note on deploying to production: for this to work in production, you will need to add your production URL to the Authorized domains in the future).
  • Add scopes. This determines what information users may give your (I clicked userinfo.email) > Update > Save and Continue.
  • Add Test users (I added my email. These need to be Google users) > Add > Save and Continue.
  • Click Back to Dashboard

c. Create an OAuth client ID

  • Click Credentials > Create credentials > OAuth client ID > Web application (Don't click create yet).

Add your authorized Javascript origins

For our local development, we add both http://localhost and http://localhost:8000 to Authorized JavaScript origins.

Substitute the port number with the port number you use for your local development server.

Note on deploying to production: When deploying to production, you'll need to add the URI of your website to Authorized JavaScript origins. The URI includes the scheme and fully qualified hostname only. For example, https://www.example.com.) If you don't do this, Google will return an error when you try to sign in.

Add your Authorized redirect URIs

  • Add http://localhost:8000/auth-receiver to Authorized redirect URIs.

This is the URL to which Google will send credentials and redirect the user after they have authenticated. Substitute the port number with the port number you use for your local development server.Substitute the port number with the port number you use for your local development server.

  • Click create
  • Copy the Client ID and Client Secret. Client ID and Client Secret modal

d. Add the Client ID to our Django app

  • Create a file called .env at core/.env and add the below to it. We'll use this to store our environment variables, which we won't commit to version control.
GOOGLE_OAUTH_CLIENT_ID=<your_client_id>
  • Update the top of your core/settings.py to look like the below. This loads the environment variables from the .env file when the server restarts. We also add a check to ensure that the GOOGLE_OAUTH_CLIENT_ID environment variable is set, and add settings to allow the Google sign in popup.
import os
from dotenv import load_dotenv
from pathlib import Path


load_dotenv()


GOOGLE_OAUTH_CLIENT_ID = os.environ.get('GOOGLE_OAUTH_CLIENT_ID')
if not GOOGLE_OAUTH_CLIENT_ID:
    raise ValueError(
        'GOOGLE_OAUTH_CLIENT_ID is missing.' 
        'Have you put it in a file at core/.env ?'
    )

# We need these lines below to allow the Google sign in popup to work.
SECURE_REFERRER_POLICY = 'no-referrer-when-downgrade'
SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin-allow-popups"

3. Create your Google sign in flow

How your Google sign in flow will work

Our sign in flow is simple and effective. We'll:

  1. render an HTML page, containing a script tag from Google that runs when the user visits the page and adds the Google sign in button
  2. after the user signs in on Google, Google will then redirect the user to a url on our site ('auth-reciever') with a POST request containing a token.
  3. we'll check that the token is valid and get the user's information from it

So, let's create this.

a. Create a sign in page

  • Create a folder at sim/templates and create a file called sign_in.html in it.
  • Add the below to sign_in.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login Page</title>
    <script src="https://accounts.google.com/gsi/client" async></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }

        .container {
            background-color: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            text-align: center;
        }

        img {
            border-radius: 50%;
            width: 100px;
            height: 100px;
            object-fit: cover;
            margin-top: 10px;
        }

        p {
            color: #333;
            margin: 10px 0;
        }

        a {
            color: #007bff;
            text-decoration: none;
            font-weight: bold;
        }

        a:hover {
            text-decoration: underline;
        }

        .g_id_signin {
            margin-top: 20px;
        }
    </style>
</head>
<body>

<div class="container">
    {% if request.session.user_data %}
    <div>
        <p>Hi {{ request.session.user_data.given_name }} 🙂</p>
        <p>Your email is {{ request.session.user_data.email }}</p>
        <img src="{{ request.session.user_data.picture }}" alt="User picture">
        <p>Click here to <a href="/sign-out">Sign out</a></p>
    </div>
    {% else %}
    <div>
        <p>Hi there 🙂 </p>
        <p>Click below to sign in with Google</p>
        <!--            Paste the generated HTML code for Google sign in button goes here-->
    </div>
    {% endif %}
</div>

</body>
</html>

This includes the script tag to load the Google sign-in API and the button to trigger the sign-in flow.

b. Generate Google sign in button HTML

Now we'll generate the specific HTML to add the button for your app.

To do this, visit the Google HTML generator here.

It should look like this:

Google HTML generator

  • For Google client ID, enter your OAuth client ID.
  • For Login URL, enter: http://localhost:8000/auth-receiver (Production note: you'll need to change this to your production URL when deploying to production).
  • Click "Enable Sign in with Google button"
  • In the behaviour option, select 'popup'. (Note that we choose the 'popup' option. This is because the 'redirect' option doesn't work in local development (e.g., localhost), despite working in production (I've just spent 90 mins exhausting the options). In contrast, 'popup' works in development)
  • Select the sign in methods you want (I selected both 'Enable Sign in with Google button' and 'Enable Google One Tap').
  • Click 'Get code' and copy the HTML code.

Google HTML generator

c. Add the HTML code to your sign in page

  • Paste the HTML code that you've just generated into your sign_in.html file where you want the button to appear.

Create your views

  • Add the below views to sim/views.py:
import os

from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.views.decorators.csrf import csrf_exempt
from google.oauth2 import id_token
from google.auth.transport import requests


@csrf_exempt
def sign_in(request):
    return render(request, 'sign_in.html')


@csrf_exempt
def auth_receiver(request):
    """
    Google calls this URL after the user has signed in with their Google account.
    """
    print('Inside')
    token = request.POST['credential']

    try:
        user_data = id_token.verify_oauth2_token(
            token, requests.Request(), os.environ['GOOGLE_OAUTH_CLIENT_ID']
        )
    except ValueError:
        return HttpResponse(status=403)

    # In a real app, I'd also save any new user here to the database.
    # You could also authenticate the user here using the details from Google (https://docs.djangoproject.com/en/4.2/topics/auth/default/#how-to-log-a-user-in)
    request.session['user_data'] = user_data

    return redirect('sign_in')


def sign_out(request):
    del request.session['user_data']
    return redirect('sign_in')


Q. How would I save the user to the database in a production app? 🏭

In a real app, you'd want to save the user to the database after we authenticate them with Google.

I didn't add this to the main guide to keep things simpler. But Hope asked me about it on the Youtube video today.

So here's some code that I wrote today for Photon Designer to do that. Feel free to use it 👍




from django.http import HttpResponse, HttpRequest
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from rest_framework.views import APIView
from . import models

@method_decorator(csrf_exempt, name='dispatch')
class AuthGoogle(APIView):
    """
    Google calls this URL after the user has signed in with their Google account.
    """
    def post(self, request, *args, **kwargs):
        try:
            user_data = self.get_google_user_data(request)
        except ValueError:
            return HttpResponse("Invalid Google token", status=403)

        email = user_data["email"]
        user, created = models.User.objects.get_or_create(
            email=email, defaults={
                "username": email, "sign_up_method": "google",
                "first_name": user_data.get("given_name"),
            }
        )

        # Add any other logic, such as setting a http only auth cookie as needed here.
        return HttpResponse(status=200)

    @staticmethod
    def get_google_user_data(request: HttpRequest):
        token = request.POST['credential']
        return id_token.verify_oauth2_token(
            token, requests.Request(), os.environ['GOOGLE_OAUTH_CLIENT_ID']
        )

Connect your urls

  • Include the app's URLs in the core/urls.py file:
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('sim.urls')),
]
  • Create a urls.py file in the sim directory with the below code:
from django.urls import path
from . import views

urlpatterns = [
    path('', views.sign_in, name='sign_in'),
    path('sign-out', views.sign_out, name='sign_out'),
    path('auth-receiver', views.auth_receiver, name='auth_receiver'),
]

  • Run your migrations to create the database tables for our app (So that we can store the user's information):
python manage.py migrate

4 .Run your app and test your Google sign in

  • Run your app, making sure that the address and port number matches the port number you added to your Authorized Javascript origins and Authorized redirect URIs.
python manage.py runserver 8000


You should see your Google sign in on the page like this: Google sign in button
Click it, sign in with Google, and you should see your email and picture on the page.



5. Troubleshooting

Issue: You see a blank page after logging in. Opening your console shows the error: "[GSI_LOGGER]: The given origin is not allowed for the given client ID."
- Solution 1: Add the URL you're using to test to the 'Authorized Javascript origins' and 'Authorized redirect URIs' in your Google Cloud Platform project. - Solution 2: Use the popup option in the Google HTML generator instead of the redirect option. As of 2024-07-19 (I've just spent 90 minutes on it), the redirect option doesn't work in local development (e.g., localhost), despite working in production. In contrast, the popup option works in development.

For further reference, the docs from Google are here

Congrats - You've added Google sign in to your Django app 🎉

By reducing the effort to use your app, by adding Google sign in to your Django app, you'll increase the ratio of visitors who become users 👩‍🦱

You'll still need people to visit your app, and to provide them with loads of value. But you've increased your ability to demonstrate that value.

👉Here's the link to the full Github repo showing the final code.

Next steps: Overview of deploying to production

Deploying your Google sign in to production is also fast to do. As an overview, you'd need to:

  1. Add your production URL to the 'Authorized Javascript origins' and 'Authorized redirect URIs' in your Google Cloud Platform project (like we did earlier for localhost).
  2. Update your .env file with your production Client ID and Client Secret.
  3. Change the data-login_uri="http://localhost:8000/auth-receiver" in your sign_in.html file to your production URL (use a context variable that changes based on the environment).

Also, if you're looking for a specific tutorial on how to add Google sign in with Django REST Framework, check out Zach's article here.

P.S Build Django frontend faster than a cheetah chasing a gazelle? ⚡️

Do you dream of creating Django products so quickly they break the space-time continuum? Yeah, me too.

I'm building: Photon Designer. It's a tool for building UI that puts the 'fast' in 'very fast.'

Let's get visual.

Do you want to create beautiful frontends effortlessly?
Click below to book your spot on our early access mailing list (as well as early adopter prices).
Copied link to clipboard 📋

Made with care by Tom Dekan

© 2024 Photon Designer