Open In App

Google Authentication and Fetching Mails from Scratch - Python Django

Last Updated : 17 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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:

  1. Go to Google Cloud Console
  2. Create a new project (or select an existing one)
  3. Enable the Gmail API
  4. Navigate to APIs & Services- Credentials
  5. Click Create Credentials- OAuth client ID
  6. Choose:
    • Application type: Web application
    • Authorized redirect URI: http://localhost:8000/google/callback/
  7. 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:

django15
Home page of the app

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

django16
Login with Google

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:

django17
5 most recent mails

Next Article
Article Tags :
Practice Tags :

Similar Reads