Google Authentication and Fetching Mails from Scratch - Python Django
Last Updated :
17 May, 2025
Google OAuth2 is a secure authorization protocol that allows applications to access user data without exposing their login credentials. Instead of asking for a username and password, OAuth2 lets users log in via Google and gives your app permission to access specific services (like Gmail) on their behalf.

In this tutorial, we’ll build a Django app from scratch that uses Google OAuth2 to authenticate users and access their Gmail inbox. We’ll use official Google libraries to handle the OAuth flow and interact with the Gmail API.
First, create a new Django project and app:
django-admin startproject gfg_auth_project
cd gfg_auth_project
python manage.py startapp gfg_auth_app
Then, add the new app to INSTALLED_APPS in gfg_auth_project/settings.py:
INSTALLED_APPS = [
...
'gfg_auth_app',
]
Now install the required packages:
pip install django google-auth google-auth-oauthlib google-api-python-client
Then make the initial migrations and apply them:
python manage.py migrate
Step 2: Get Google OAuth Credentials
OAuth credentials allow your Django app to securely communicate with Google's servers. These credentials contain your app's identity and permissions. You'll need a credentials.json file that includes your client ID, client secret, and redirect URIs, this file is used in the OAuth 2.0 flow.
Follow these steps to get the credentials file:
- Go to Google Cloud Console
- Create a new project (or select an existing one)
- Enable the Gmail API
- Navigate to APIs & Services- Credentials
- Click Create Credentials- OAuth client ID
- Choose:
- Application type: Web application
- Authorized redirect URI: http://localhost:8000/google/callback/
- Download the credentials.json file and place it in your Django project's root directory.
Step 3: Modify manage.py to Allow Insecure OAuth
To allow OAuth over HTTP (for development only), you must set an environment variable.
Set the OAUTHLIB_INSECURE_TRANSPORT variable in manage.py:
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # Allow HTTP for OAuth (development only)
Add this inside the main() function before execute_from_command_line(...).
Step 4: Define Views to Handle Google OAuth and Gmail Fetching
Now let’s define views for login, callback, and email fetching.
1. home view
Displays a simple login link.
Python
def home(request):
return HttpResponse("Welcome! <a href='/google/login/'>Login with Google</a>")
2. google_login view
This initializes the OAuth2 flow and redirects users to the Google consent page.
Python
def google_login(request):
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
flow.redirect_uri = REDIRECT_URI
authorization_url, state = flow.authorization_url(access_type='offline', prompt='consent')
request.session['state'] = state # Store state in session
return redirect(authorization_url)
This view sets up the OAuth flow and redirects the user to Google's login/authorization page.
3. google_callback view
Handles the response from Google and fetches recent emails using the Gmail API.
Python
def google_callback(request):
flow = Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE,
scopes=SCOPES,
redirect_uri=REDIRECT_URI,
)
flow.fetch_token(authorization_response=request.build_absolute_uri())
credentials = flow.credentials
service = build('gmail', 'v1', credentials=credentials)
result = service.users().messages().list(userId='me', maxResults=5).execute()
messages = result.get('messages', [])
email_data = []
for msg in messages:
msg_detail = service.users().messages().get(userId='me', id=msg['id']).execute()
headers = msg_detail['payload'].get('headers', [])
subject = next((h['value'] for h in headers if h['name'] == 'Subject'), 'No Subject')
sender = next((h['value'] for h in headers if h['name'] == 'From'), 'Unknown')
snippet = msg_detail.get('snippet', '')
email_data.append({
'subject': subject,
'sender': sender,
'snippet': snippet,
})
return render(request, 'emails.html', {'emails': email_data})
This view processes the OAuth response, saves access credentials, and uses the Gmail API to fetch and display the user's latest 5 emails you can modify the number of emails fetched by changing maxResults parameter.
4. fetch_emails view
Allows accessing the emails again if valid credentials (token.json) already exist:
Python
def fetch_emails(request):
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
return HttpResponse("No valid credentials. Please log in first.")
service = build('gmail', 'v1', credentials=creds)
try:
results = service.users().messages().list(userId='me', maxResults=5).execute()
messages = results.get('messages', [])
email_subjects = []
for message in messages:
msg = service.users().messages().get(userId='me', id=message['id']).execute()
snippet = msg.get('snippet', '(No snippet)')
email_subjects.append(snippet)
return HttpResponse('<br><br>'.join(email_subjects))
except Exception as e:
return HttpResponse(f"An error occurred: {str(e)}")
Complete code for gfg_auth_app/views.py
Python
from django.shortcuts import render, redirect
import os
import json
from django.urls import reverse
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from django.shortcuts import redirect
from django.http import HttpResponse
from django.conf import settings
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import Flow
# Google OAuth2 client ID and secret
CLIENT_SECRETS_FILE = os.path.join(settings.BASE_DIR, 'credentials.json')
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
REDIRECT_URI = 'http://localhost:8000/google/callback/'
def home(request):
return HttpResponse("Welcome! <a href='/google/login/'>Login with Google</a>")
def google_login(request):
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
flow.redirect_uri = 'http://localhost:8000/google/callback/'
authorization_url, state = flow.authorization_url(access_type='offline', prompt='consent')
# Store the state in the session for later use
request.session['state'] = state
return redirect(authorization_url)
def google_callback(request):
flow = Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE,
scopes=SCOPES,
redirect_uri=REDIRECT_URI,
)
flow.fetch_token(authorization_response=request.build_absolute_uri())
credentials = flow.credentials
service = build('gmail', 'v1', credentials=credentials)
# Fetch messages
result = service.users().messages().list(userId='me', maxResults=5).execute()
messages = result.get('messages', [])
email_data = []
for msg in messages:
msg_detail = service.users().messages().get(userId='me', id=msg['id']).execute()
headers = msg_detail['payload'].get('headers', [])
subject = next((h['value'] for h in headers if h['name'] == 'Subject'), 'No Subject')
sender = next((h['value'] for h in headers if h['name'] == 'From'), 'Unknown')
snippet = msg_detail.get('snippet', '')
email_data.append({
'subject': subject,
'sender': sender,
'snippet': snippet,
})
return render(request, 'emails.html', {'emails': email_data})
def fetch_emails(request):
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
return HttpResponse("No valid credentials. Please log in first.")
service = build('gmail', 'v1', credentials=creds)
try:
results = service.users().messages().list(userId='me', maxResults=5).execute()
messages = results.get('messages', [])
email_subjects = []
if not messages:
email_subjects.append('No messages found.')
else:
for message in messages:
msg = service.users().messages().get(userId='me', id=message['id']).execute()
snippet = msg.get('snippet', '(No snippet)')
email_subjects.append(snippet)
return HttpResponse('<br><br>'.join(email_subjects))
except Exception as e:
return HttpResponse(f"An error occurred: {str(e)}")
Step 5: Create Template to Show Emails
This template will display the subject, sender, and snippet of each fetched email. If the templates folder does not exists in the gfg_auth_app folder then create one and in the templates folder, create an emails.html file and paste the following code:
HTML
<!DOCTYPE html>
<html>
<head>
<title>Your Gmail Inbox</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
}
.email {
border: 1px solid #ccc;
padding: 12px;
margin-bottom: 15px;
border-radius: 6px;
}
.subject {
font-weight: bold;
color: #2c3e50;
}
.sender {
color: #555;
}
</style>
</head>
<body>
<h1>Fetched Emails</h1>
{% if emails %}
{% for email in emails %}
<div class="email">
<div class="subject">{{ email.subject }}</div>
<div class="sender">From: {{ email.sender }}</div>
<p>{{ email.snippet }}</p>
</div>
{% endfor %}
{% else %}
<p>No emails found.</p>
{% endif %}
</body>
</html>
Step 6: Define URL Patterns
Define URL patterns that map specific URL paths to the corresponding views created earlier. This is essential so Django knows how to route incoming requests. We'll create the URL configuration in our app and include it in the project's root configuration.
gfg_auth_app/urls.py:
Python
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('google/login/', views.google_login, name='google_login'),
path('google/callback/', views.google_callback, name='google_callback'),
path('google/emails/', views.fetch_emails, name='fetch_emails'),
]
gfg_auth_project/urls.py:
Python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('gfg_auth_app.urls')),
]
Now, when users visit the app URLs, Django will call the right view functions accordingly.
Final Step: Run the Project
Start the development server using command:
python manage.py runserver
Output:
Home page of the appClicking on the "Login with Google" hyperlink, we will be directed to login page:
Login with GoogleYou can choose from the already logged in accounts or you can even use another account. After selecting the account click on allow button and you will be redirected to the page that will display the 5 most recent mails of the selected account:
5 most recent mails
Similar Reads
Django Tutorial | Learn Django Framework
Django, built with Python, is designed to help developers build secure, scalable, and feature-rich web applications quickly and efficiently. Whether you're a beginner looking to create your first dynamic website or an experienced developer aiming to enhance your skills, this tutorial will guide you
11 min read
Django view
Views In Django | Python
Django Views are one of the vital participants of the MVT Structure of Django. As per Django Documentation, A view function is a Python function that takes a Web request and returns a Web response. This response can be the HTML contents of a Web page, a redirect, a 404 error, an XML document, an ima
6 min read
Django Function Based Views
Django is a Python-based web framework which allows you to quickly create web application without all of the installation or dependency problems that you normally will find with other frameworks. Django is based on MVT (Model View Template) architecture and revolves around CRUD (Create, Retrieve, Up
7 min read
Django Class Based Views
Class-Based Views (CBVs) allow developers to handle HTTP requests in a structured and reusable way. With CBVs, different HTTP methods (like GET, POST) are handled as separate methods in a class, which helps with code organization and reusability.Advantages of CBVsSeparation of Logic: CBVs separate d
6 min read
Class Based vs Function Based Views - Which One is Better to Use in Django?
Django, a powerful Python web framework, has become one of the most popular choices for web development due to its simplicity, scalability and versatility. One of the key features of Django is its ability to handle views and these views can be implemented using either Class-Based Views (CBVs) or Fun
6 min read
Django Templates
Templates are the third and most important part of Django's MVT Structure. A Django template is basically an HTML file that can also include CSS and JavaScript. The Django framework uses these templates to dynamically generate web pages that users interact with. Since Django primarily handles the ba
7 min read
Django Static File
Static Files such as Images, CSS, or JS files are often loaded via a different app in production websites to avoid loading multiple stuff from the same server. This article revolves around, how you can set up the static app in Django and server Static Files from the same.Create and Activate the Virt
3 min read
Django Model
Django Models
A Django model is the built-in feature that Django uses to create tables, their fields, and various constraints. In short, Django Models is the SQL Database one uses with Django. SQL (Structured Query Language) is complex and involves a lot of different queries for creating, deleting, updating, or a
10 min read
Django model data types and fields list
Django models represent the structure of your database tables, and fields are the core components of those models. Fields define the type of data each database column can hold and how it should behave. This artcle covers all major Django model field types and their usage.Defining Fields in a ModelEa
4 min read
Built-in Field Validations - Django Models
Built-in Field Validations in Django models are the default validations that come predefined to all Django fields. Every field comes in with built-in validations from Django validators. For example, IntegerField comes with built-in validation that it can only store integer values and that too in a p
3 min read
How to use User model in Django?
The Djangoâs built-in authentication system is great. For the most part we can use it out-of-the-box, saving a lot of development and testing effort. It fits most of the use cases and is very safe. But sometimes we need to do some fine adjustment so to fit our Web application. Commonly we want to st
3 min read
Meta Class in Models - Django
Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development, so you can focus on writing your app without needing to reinvent the wheel. Itâs free and open source. D
3 min read
get_object_or_404 method in Django Models
Some functions are hard as well as boring to code each and every time. But Django users don't have to worry about that because Django has some awesome built-in functions to make our work easy and enjoyable. Let's discuss get_object_or_404() here. What is get_object_or_404 in Django?get_object_or_404
2 min read
Django Admin Interface - Python
Prerequisites: Django Introduction and Installation Creating a ProjectThe Django Admin Interface is one of the most powerful features of the Django framework. It provides a ready-to-use interface for managing project data through models, allowing developers and site administrators to perform Create,
3 min read
More topics on Django
Handling Ajax request in Django
AJAX (Asynchronous JavaScript and XML) is a web development technique that allows a web page to communicate with the server without reloading the entire page. In Django, AJAX is commonly used to enhance user experience by sending and receiving data in the background using JavaScript (or libraries li
3 min read
Python | User groups with Custom permissions in Django
Let's consider a trip booking service, how they work with different plans and packages. There is a list of product which subscriber gets on subscribing to different packages, provided by the company. Generally, the idea they follow is the level-wise distribution of different products. Let's see the
4 min read
Django Admin Interface - Python
Prerequisites: Django Introduction and Installation Creating a ProjectThe Django Admin Interface is one of the most powerful features of the Django framework. It provides a ready-to-use interface for managing project data through models, allowing developers and site administrators to perform Create,
3 min read
Python | Extending and customizing django-allauth
Prerequisite: Django-allauth setup and Configuration Let's deal with customizing django-allauth signup forms, and intervening in registration flow to add custom processes and validations. Extending the Signup Form or adding custom fields in Django-allauth: One of the most common queries about allaut
4 min read
Django - Dealing with Unapplied Migration Warnings
Django is a powerful web framework that provides a clean, reusable architecture to build robust applications quickly. It embraces the DRY (Don't Repeat Yourself) principle, allowing developers to write minimal, efficient code.Create and setup a Django project:Prerequisite: Django - Creating projectA
2 min read
Sessions framework using django - Python
Django sessions let us store data for each user across different pages, even if theyâre not logged in. The data is saved on the server and a small cookie (sessionid) is used to keep track of the user.A session stores information about a site visitor for the duration of their visit (and optionally be
3 min read
Django Sign Up and login with confirmation Email | Python
Django provides a built-in authentication system that handles users, login, logout, and registration. In this article, we will implement a user registration and login system with email confirmation using Django and django-crispy-forms for elegant form rendering.Install crispy forms using the termina
7 min read