Last Updated : 11 Jul, 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.
Step 1: Create and Configure a Django ProjectFirst, 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:
Step 2: Get Google OAuth Credentialspython manage.py migrate
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:
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 FetchingNow let’s define views for login, callback, and email fetching.
1. home viewDisplays 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 viewHandles 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 viewAllows 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 ProjectStart 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 mailsRetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4