Python GitHub OAuth authentication flow is the modern way to let users log in to your web app using their GitHub credentials, while keeping passwords safely out of your hands. In this guide we’ll walk through the theory behind OAuth 2.0, the exact steps to set up a GitHub‑based login in Python, and best‑practice tips that keep your integration secure and SEO‑friendly. Whether you’re building a Flask micro‑service or a full‑blown Django portal, the patterns described here will work out of the box.
Understanding OAuth 2.0 and GitHub’s Role
What is OAuth 2.0?
OAuth 2.0 is an open‑standard authorization framework that enables third‑party applications to obtain limited access to an HTTP service—such as GitHub—on behalf of a resource owner. The flow typically involves three parties:
- Client – your Python web app.
- Authorization Server – GitHub’s OAuth endpoint.
- Resource Server – the GitHub API that serves user data.
The client never sees the user’s password. Instead, it receives a short‑lived access_token that can be used to call protected endpoints.
Why Choose GitHub for Authentication?
GitHub is a trusted identity provider for developers. By leveraging GitHub OAuth, you gain:
- Instant credibility—users recognize the familiar GitHub login screen.
- Rich profile data (username, email, public repos) without building a custom user database.
- Fine‑grained permission scopes (e.g.,
read:user,repo) that let you request only what you need.
Prerequisites for a Python GitHub OAuth Flow
- A GitHub account and access to GitHub Developer Settings.
- Python 3.8+ installed locally.
- One of the following web frameworks: Flask (lightweight) or Django (full‑stack).
- Basic knowledge of HTTP, redirects, and JSON handling.
Step‑by‑Step Implementation with Flask
1. Register Your Application on GitHub
Navigate to Developer Settings → OAuth Apps → New OAuth App and fill in:
- Application name – e.g., “MyPythonApp”.
- Homepage URL – the public URL of your app.
- Authorization callback URL – e.g.,
http://localhost:5000/callbackfor local testing.
After saving, GitHub provides a Client ID and Client Secret. Keep the secret out of source control!
2. Install Required Packages
pip install Flask requests
3. Create the Flask App and Configuration
from flask import Flask, redirect, request, session, url_for, jsonify
import requests
import os
app = Flask(__name__)
app.secret_key = os.urandom(24) # Secure session cookies
# Load credentials from environment variables
CLIENT_ID = os.getenv('GITHUB_CLIENT_ID')
CLIENT_SECRET = os.getenv('GITHUB_CLIENT_SECRET')
AUTH_BASE_URL = 'https://github.com/login/oauth/authorize'
TOKEN_URL = 'https://github.com/login/oauth/access_token'
API_USER_URL = 'https://api.github.com/user'
4. Build the Authorization URL
When a user clicks “Login with GitHub”, redirect them to GitHub’s consent page.
@app.route('/login')
def login():
params = {
'client_id': CLIENT_ID,
'redirect_uri': url_for('callback', _external=True),
'scope': 'read:user user:email',
'state': os.urandom(8).hex() # CSRF protection
}
session['oauth_state'] = params['state']
auth_url = f"{AUTH_BASE_URL}?{requests.compat.urlencode(params)}"
return redirect(auth_url)
5. Handle the Callback and Exchange Code for Token
GitHub redirects back with code and state. Verify the state, then request an access token.
@app.route('/callback')
def callback():
# Verify state to prevent CSRF attacks
if request.args.get('state') != session.get('oauth_state'):
return 'Invalid state parameter', 400
code = request.args.get('code')
token_response = requests.post(
TOKEN_URL,
headers={'Accept': 'application/json'},
data={
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'code': code,
'redirect_uri': url_for('callback', _external=True),
'state': session['oauth_state']
}
)
token_json = token_response.json()
access_token = token_json.get('access_token')
if not access_token:
return 'Failed to obtain access token', 400
session['github_token'] = access_token
return redirect(url_for('profile'))
6. Access the GitHub API with the Token
Now you can fetch the authenticated user’s profile.
@app.route('/profile')
def profile():
token = session.get('github_token')
if not token:
return redirect(url_for('login'))
user_resp = requests.get(
API_USER_URL,
headers={'Authorization': f'token {token}',
'Accept': 'application/vnd.github.v3+json'}
)
user_data = user_resp.json()
return jsonify({
'login': user_data.get('login'),
'name': user_data.get('name'),
'avatar_url': user_data.get('avatar_url'),
'public_repos': user_data.get('public_repos')
})
Alternative: Using Django with social-auth-app-django
If you prefer Django, the social-auth-app-django package abstracts most of the flow. After installing:
pip install social-auth-app-django
add the following to settings.py:
AUTHENTICATION_BACKENDS = (
'social_core.backends.github.GithubOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
SOCIAL_AUTH_GITHUB_KEY = os.getenv('GITHUB_CLIENT_ID')
SOCIAL_AUTH_GITHUB_SECRET = os.getenv('GITHUB_CLIENT_SECRET')
SOCIAL_AUTH_GITHUB_SCOPE = ['read:user', 'user:email']
Include the URLs:
urlpatterns = [
path('auth/', include('social_django.urls', namespace='social')),
# … other routes …
]
Then a simple template link <a href="{% url 'social:begin' 'github' %}">Login with GitHub</a> launches the same OAuth flow without writing any request‑handling code yourself.
Common Pitfalls and How to Debug Them
- Mismatched redirect URI – GitHub will reject the request if the
redirect_uridoes not exactly match the one registered. - Missing
stateverification – Skipping CSRF protection can expose your app to token‑theft attacks. - Using the wrong token endpoint – Always POST to
https://github.com/login/oauth/access_tokenwith anAccept: application/jsonheader. - Expired or revoked tokens – Implement graceful handling (e.g., redirect to
/loginwhen a 401 is returned
Leave a Reply