Python Google Oauth2 Login Integration

Written by

in

Integrating Google OAuth2 login into a Python web application not only provides a seamless sign‑in experience for users, but also boosts your site’s credibility and SEO by leveraging a trusted authentication provider. In this guide we’ll walk through every step—from creating credentials in the Google Cloud Console to writing clean, production‑ready Python code—so you can launch a secure “Sign in with Google” button in minutes.

Why Choose Google OAuth2 for Python Apps?

  • Universal access: Over a billion Google accounts mean users rarely need to create a new password.
  • Built‑in security: Google handles password storage, multi‑factor authentication, and token revocation.
  • SEO advantage: Faster sign‑in reduces bounce rates, which search engines interpret as a quality signal.
  • Scalable: OAuth2 works the same for small Flask projects and large Django deployments.

Prerequisites

  • Python 3.8+ installed on your development machine.
  • A Google account to access the Cloud Console.
  • Basic familiarity with Flask or Django (the examples use Flask for brevity).
  • Virtual environment tool (venv, pipenv, or poetry) to keep dependencies isolated.

Step 1 – Create OAuth2 Credentials in Google Cloud Console

  1. Navigate to Google Cloud Console → APIs & Services → Credentials.
  2. Click Create Credentials → OAuth client ID.
  3. Select Web application as the application type.
  4. Enter a name (e.g., MyPythonApp).
  5. Under Authorized JavaScript origins, add your development URL (e.g., http://localhost:5000).
  6. Under Authorized redirect URIs, add the callback endpoint that will receive the authorization code, such as http://localhost:5000/auth/callback.
  7. Save. Google will display a Client ID and Client Secret**—store them securely (e.g., in a .env file).

Step 2 – Install Required Python Packages

python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install Flask requests-oauthlib python-dotenv

Flask provides the web framework, requests‑oauthlib handles the OAuth2 flow, and python‑dotenv loads environment variables securely.

Step 3 – Configure Environment Variables

Create a .env file in the project root:

GOOGLE_CLIENT_ID=YOUR_CLIENT_ID.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=YOUR_CLIENT_SECRET
SECRET_KEY=your_flask_secret_key

Step 4 – Implement the OAuth2 Flow with Flask

4.1 Initialize Flask and OAuth Session

from flask import Flask, redirect, url_for, session, request, jsonify
from requests_oauthlib import OAuth2Session
from urllib.parse import urlparse, urljoin
import os
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
app.secret_key = os.getenv('SECRET_KEY')

# Google OAuth2 endpoints
AUTHORIZATION_BASE_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
TOKEN_URL = 'https://oauth2.googleapis.com/token'
USER_INFO_URL = 'https://www.googleapis.com/oauth2/v3/userinfo'

# Scopes we need – email and profile are typical
SCOPE = ['openid', 'https://www.googleapis.com/auth/userinfo.email',
         'https://www.googleapis.com/auth/userinfo.profile']

4.2 Helper to Verify Safe Redirects

def is_safe_url(target):
    host_url = request.host_url
    ref_url = urlparse(host_url)
    test_url = urlparse(urljoin(host_url, target))
    return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc

4.3 Login Route – Redirect to Google

@app.route('/login')
def login():
    google = OAuth2Session(
        client_id=os.getenv('GOOGLE_CLIENT_ID'),
        scope=SCOPE,
        redirect_uri=url_for('callback', _external=True)
    )
    authorization_url, state = google.authorization_url(
        AUTHORIZATION_BASE_URL,
        access_type='offline',        # Request refresh token
        prompt='consent'              # Force consent screen for fresh refresh token
    )
    # Store state in session to protect against CSRF
    session['oauth_state'] = state
    return redirect(authorization_url)

4.4 Callback Route – Exchange Code for Tokens

@app.route('/auth/callback')
def callback():
    # Verify state to mitigate CSRF attacks
    if request.args.get('state') != session.get('oauth_state'):
        return 'State mismatch', 400

    google = OAuth2Session(
        client_id=os.getenv('GOOGLE_CLIENT_ID'),
        redirect_uri=url_for('callback', _external=True),
        state=session['oauth_state']
    )
    token = google.fetch_token(
        TOKEN_URL,
        client_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
        authorization_response=request.url
    )
    # Save token (including refresh_token) securely – here we use session for demo
    session['google_token'] = token

    # Retrieve user profile
    resp = google.get(USER_INFO_URL)
    user_info = resp.json()
    session['user'] = {
        'id': user_info['sub'],
        'email': user_info['email'],
        'name': user_info.get('name')
    }
    # Redirect to a protected page
    next_url = request.args.get('next') or url_for('profile')
    return redirect(next_url) if is_safe_url(next_url) else redirect(url_for('profile'))

4.5 Protected Profile Page

@app.route('/profile')
def profile():
    if 'user' not in session:
        return redirect(url_for('login', next=request.path))
    user = session['user']
    return f"""
    

Welcome, {user['name']}

Email: {user['email']}

Google ID: {user['id']}

Logout """

4.6 Logout Route

@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

Step 5 – Managing Refresh Tokens

Google issues a refresh_token only the first time a user consents, unless you force the consent screen (as shown with prompt='consent'). Store the refresh token in a persistent store (database, encrypted vault) rather than the user session. When the access token expires, use the refresh token to obtain a new one:

def refresh_google_token(refresh_token):
    extra = {
        'client_id': os.getenv('GOOGLE_CLIENT_ID'),
        'client_secret': os.getenv('GOOGLE_CLIENT_SECRET'),
    }
    oauth = OAuth2Session(client_id=os.getenv('GOOGLE_CLIENT_ID'))
    new_token = oauth.refresh_token(
        TOKEN_URL,
        refresh_token=refresh_token,
        **extra
    )
    return new_token

Step 6 – Common Pitfalls & How to Avoid Them

  • State mismatch errors: Always verify the state parameter returned by Google against the value stored in the session.
  • Redirect URI mismatch: The URI you register in the Cloud Console must exactly match the redirect_uri used in code (including trailing slashes).
  • Missing scopes: If you need additional Google APIs (e.g., Drive), add the corresponding scopes to the SCOPE list.
  • Refresh token loss: Request access_type='offline' and prompt='consent' during the first login, then persist the refresh token.
  • Insecure storage of secrets: Never commit .env files to version control; use environment variables on production servers.

Step 7 – Security Best Practices

  1. Use HTTPS in production. OAuth2 tokens are bearer tokens; transmitting them over HTTP exposes them to interception.
  2. Validate ID tokens. Google returns an id_token (JWT). Verify its signature and claims (issuer, audience, expiration) using the google-auth library for added security.
  3. Implement token revocation. Provide a logout flow that calls https://oauth2.googleapis.com/revoke?token={token} to invalidate the token on Google’s side.
  4. Set short-lived access tokens. Rely on refresh tokens for long‑term sessions, reducing the window for token abuse.
  5. Apply SameSite cookie attribute. In Flask, set SESSION_COOKIE_SAMESITE = 'Lax' or 'Strict' to mitigate CSRF.

Step 8 – Testing the Integration

Before deploying, run the Flask app locally and verify each step:

export FLASK_APP=app.py
flask run

Visit http://localhost:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *