Google Authentication and Fetching Mails from Scratch - Python Django
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.
Step 1: Create and Configure a Django Project
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.
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.
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.
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:
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
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:
<!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:
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:
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:

Clicking on the "Login with Google" hyperlink, we will be directed to login page:

You 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:
