Python Google OAuth2 login integration is one of the most requested features for modern web applications. By allowing users to sign in with their Google accounts, developers can boost conversion rates, reduce password fatigue, and leverage Google’s robust security infrastructure. In this guide we’ll walk through everything you need to know to implement a seamless Google OAuth2 login flow in a Python web app—covering setup in the Google Cloud Console, configuring Flask (or Django) back‑ends, handling tokens securely, and troubleshooting common pitfalls. Whether you’re building a small prototype or a production‑grade service, the step‑by‑step instructions below will get you up and running quickly.
Why Choose Google OAuth2 for Python Applications?
- Trusted security: Google handles authentication, multi‑factor verification, and account recovery.
- Reduced friction: Users can sign in with a single click, increasing signup completion rates.
- Rich user profile data: Access to email, name, picture, and custom scopes for Google APIs.
- Scalable and compliant: Built on OAuth 2.0 standards, meeting GDPR and CCPA requirements.
Prerequisites Before You Start
- A Google Cloud Platform (GCP) project with the OAuth consent screen configured.
- Python 3.8+ installed locally or on your server.
- A web framework such as
FlaskorDjango. This tutorial uses Flask for simplicity. - Basic knowledge of HTTP, redirects, and JSON handling.
Step 1: Create OAuth 2.0 Credentials in Google Cloud Console
1.1 Enable the Google Identity Services API
Navigate to the APIs & Services Library and enable Google Identity Services. This API provides the endpoints needed for token exchange and user info retrieval.
1.2 Configure the OAuth consent screen
Go to OAuth consent screen and fill in:
- App name, support email, and developer contact information.
- Scopes you intend to request (e.g.,
email,profile,openid). - Authorized domains (your production domain and any local development URLs like
localhost).
1.3 Generate client ID and client secret
Under Credentials → Create Credentials → OAuth client ID, select Web application. Add the following Authorized redirect URIs (adjust the port if needed):
http://localhost:5000/auth/callback
https://yourdomain.com/auth/callback
Save the generated Client ID and Client Secret. You’ll need them in your Python code.
Step 2: Set Up the Python Environment
2.1 Install required packages
pip install Flask requests-oauthlib python-dotenv
The requests-oauthlib library simplifies the OAuth 2.0 flow, while python-dotenv helps keep secrets out of source control.
2.2 Create a .env file
GOOGLE_CLIENT_ID=YOUR_CLIENT_ID.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=YOUR_CLIENT_SECRET
SECRET_KEY=your_flask_secret_key
Load these variables in your Flask app using python-dotenv.
Step 3: Implement the OAuth Flow in Flask
3.1 Basic Flask app skeleton
from flask import Flask, redirect, url_for, session, request, jsonify
from requests_oauthlib import OAuth2Session
from dotenv import load_dotenv
import os
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
SCOPE = ['openid', 'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile']
3.2 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',
prompt='select_account'
)
# Store state in session to protect against CSRF
session['oauth_state'] = state
return redirect(authorization_url)
3.3 Callback route – exchange code for tokens
@app.route('/auth/callback')
def callback():
google = OAuth2Session(
client_id=os.getenv('GOOGLE_CLIENT_ID'),
redirect_uri=url_for('callback', _external=True),
state=session.get('oauth_state')
)
token = google.fetch_token(
TOKEN_URL,
client_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
authorization_response=request.url
)
# Save token securely (e.g., in a server‑side session or DB)
session['oauth_token'] = token
# Retrieve user profile
resp = google.get(USER_INFO_URL)
user_info = resp.json()
# Example: store user info in session
session['user'] = {
'id': user_info['sub'],
'email': user_info['email'],
'name': user_info['name'],
'picture': user_info['picture']
}
return redirect(url_for('profile'))
3.4 Protected profile page
@app.route('/profile')
def profile():
user = session.get('user')
if not user:
return redirect(url_for('login'))
return f"""
Welcome, {user['name']}!
Email: {user['email']}
Logout
"""
3.5 Logout route
@app.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
Step 4: Secure Token Management
- Never expose the client secret to the browser. Keep it on the server side only.
- Store access and refresh tokens in an encrypted database if you need long‑term access.
- Validate the
id_tokensignature using Google’s public keys (available athttps://www.googleapis.com/oauth2/v3/certs) for added security. - Implement token refresh logic: when the access token expires, use the refresh token to obtain a new one without prompting the user again.
Step 5: Extending the Integration – Accessing Google APIs
Once you have a valid access token, you can call any Google API that matches the scopes you requested. For example, to list the authenticated user’s Google Drive files:
import requests
def list_drive_files():
token = session.get('oauth_token')
headers = {'Authorization': f"Bearer {token['access_token']}"}
drive_api = 'https://www.googleapis.com/drive/v3/files'
response = requests.get(drive_api, headers=headers, params={'pageSize': 10})
return response.json()
Common Errors and How to Fix Them
Invalid redirect URI
Google will reject the request if the redirect_uri does not exactly match one of the URIs you entered in the Cloud Console. Double‑check for trailing slashes, HTTP vs. HTTPS, and port numbers.
CSRF state mismatch
If the state stored in the session differs from the one returned by Google, the callback will raise a InvalidStateError. Ensure you store session['oauth_state'] before the redirect and retrieve the same value in the callback.
Expired or revoked token
When an access token expires, Google returns a 401 Unauthorized. Use the stored refresh_token to request a new access token, or redirect the user to the login flow again if the refresh token is also invalid.
Testing Locally vs. Production
- Local development: Use
http://localhost:5000as an authorized domain. Some browsers block third‑party cookies on localhost; consider usingSameSite=None; Secureflags only in production. - Production: Enforce HTTPS, set
SESSION_COOKIE_SECURE = Truein Flask, and consider using a reverse proxy (e.g., Nginx) to terminate SSL. - Enable Google’s test users feature while the app is in “Testing” mode to avoid a public verification process.
Performance Tips for High‑Traffic Sites
- Cache the Google public keys for token verification (they rotate about once per hour).
- Store user sessions in a fast key‑value store like Redis instead of server memory.
- Limit the
Leave a Reply