🐍 Google OAuthLib Django Quickstart
Google OAuthLib Django is a Django boilerplate app implementing a full Google OAuth2 authentication flow using google-auth-oauthlib
with Sessions.
This is not intended as a replacement for Django’s users sign up/in mechanism. Django AllAuth is more suited for such use case.
The App uses basic Django Sessions to let users make authenticated requests that target their Google accounts’ resources. No user data is kept after logout.
messages
, which is added as a variable to template’s context, and then rendered on the frontend./login
and /auth
.@require_auth
decorator for views.The code was tested in the following environments (all OSs are x64):
Windows 10 | CentOS 7 | CentOS 8 Stream | Ubuntu 16.04 | Ubuntu 18.04 | Ubuntu 20.04 | |
---|---|---|---|---|---|---|
Python |
3.9.1 |
3.6.8 |
3.9.2 |
- | - | - |
Django |
3.2 - 3.1.8 |
3.2 [*] |
3.2 |
- | - | - |
Google API Python Client |
2.1.0 - 1.12.8 |
2.1.0 |
2.2.0 |
- | - | - |
Google Auth OAuthLib |
0.4.4 - 0.4.2 |
0.4.4 |
0.4.4 |
- | - | - |
Create a GCP project, enable Drive API, add the following scopes and then download webapp Client ID credentials file from the GCP console:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
openid
Create a virtual environment and install required libraries:
virtualenv venv
source venv/Scripts/activate
pip install Django google-api-python-client google-auth-oauthlib
## OR: 'pip install -r requirements.txt'
## You might also need to install 'pysqlite3-binary' (check note below)
Since Django 2.2, the minimum supported version of SQLite is 3.8.3. So if your system does not meet this requirement (like CentOS 7), you can either make a system-wide upgrade of the SQLite3 package, or (better) install pysqlite3-binary
in your Python virtual environment and override the sqlite3 module by prepending the following lines to settings.py
:
__import__('pysqlite3')
import sys
sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')
Clone the repository, create the Django database and launch the dev server:
git clone git@github.com:amindeed/Google-OAuthLib-Django.git
cd Google-OAuthLib-Django
# Migrate changes to Django's DB (only in first time run)
python manage.py migrate
python manage.py runserver
Diffing changes between the code in this repository and a newly created Django project (django-admin startproject my_django_app .
):
credentials.json
(GCP project Client ID credentials)my_django_app/templates/home.html
( templates/home.html
sample base template)my_django_app/views.py
(views.py
)db.sqlite3
(automatically created by Django by running python manage.py migrate
)Modify my_django_app/settings.py
: Django admin site and static files apps are disabled. For middleware, only SessionMiddleware
is kept, which is enough for the intended use case.
@@ -8,7 +8,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
+SECRET_KEY = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
@@ -19,12 +19,12 @@ ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
- 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
- 'django.contrib.staticfiles',
+
+ 'my_django_app',
]
MIDDLEWARE = [
@@ -32,8 +32,6 @@ MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Modify my_django_app/urls.py
: All lines of code related to the Django admin site are removed.
-from django.contrib import admin
from django.urls import path
+from my_django_app import views
urlpatterns = [
- path('admin/', admin.site.urls),
+ path('', views.home),
+ path('login/', views.login),
+ path('auth/', views.auth, name='auth'),
+ path('logout/', views.logout_view),
]
The code in this repository was a result of my researches for another project: GMail-AutoResponder
.
The structure of the code was built upon the AuthLib library demo for Django and the Google Apps Script API Python Quickstart (and later, the Drive V3 Python Quickstart, for simpler API calls):
AuthLib would have been my goto library, but I dropped it after many tries, due to confusing instructions about OAuth2 refresh token support for Django (at least, up until February 2021):
client_credentials
won’t issue refresh token. You need to use authorization_code flow to get the refresh token.
- A OAuth server-side configuration seems to be needed: stackoverflow.com/questions/51305430/…
Also be aware, unless you’re on authlib 0.14.3 or later, the django integration is broken for refresh (If you’re using the metadata url): RemoteApp.request fails to use token_endpoint to refresh the access token · Issue #193 · lepture/authlib
credentials.json
contains OAuth client ID credentials of the GCP project, that will provide access to Google user’s resources for the Django app.Flow
class was used instead of InstalledAppFlow
.Instead of pickles, JSON Serialization of Credentials is used: OAuth2 token (a Credentials
instance) is converted to a dictionary and saved to the session (as a session key named token
):
{
'token': 'XXXXXXXXX',
'refresh_token': 'YYYYYYY',
'token_uri': 'https://oauth2.googleapis.com/token',
'client_id': 'a0a0a0a0a0a0a0a0a0.apps.googleusercontent.com',
'client_secret': 'ZZZZZZZZZ',
'scopes': [
'https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid'
],
'expiry': '2021-03-06T12:34:52.214490Z'
}
Value of the session key user
(which identifies the logged in user) is retrieved by parsing the Open ID Connect ID Token contained in the Credentials object resulting from a complete and successful OAuth2 flow.
{
'iss': 'https://accounts.google.com',
'azp': 'xxxxxxxxxxx.apps.googleusercontent.com',
'aud': 'yyyyyyyyyyy.apps.googleusercontent.com',
'sub': '0000000000000000000',
'hd': 'mydomain.com',
'email': 'user@mydomain.com',
'email_verified': True,
'at_hash': 'ZZZZZZZZZZZZZ',
'name': 'Awesome User',
'picture': 'https://xy0.googleusercontent.com/aa/bb/photo.jpg',
'given_name': 'Awesome',
'family_name': 'User',
'locale': 'en',
'iat': 111111111,
'exp': 222222222
}
Flow.fetch_token(code=code)
method of the google_auth_oauthlib.flow.Flow
class (google-auth-oauthlib 0.4.1
documentation).Flow.credentials
constructs a google.oauth2.credentials.Credentials
class, which in turn is a child class of google.auth.credentials.Credentials
Key google.auth.credentials.Credentials
members, that are inherited by the google_auth_oauthlib.flow.Flow.credentials
class:
to_json()
: Returns A JSON representation of this instance. When converted into a dictionary, it can be passed to from_authorized_user_info()
to create a new Credentials instance.id_token
: can be verified and decoded (parsed) using google.oauth2.id_token.verify_oauth2_token()
Featured Google OAuth implementations and libraries:
📦 oauth2client
(googleapis/oauth2client): deprecated in favor of google-auth
.
💻 $ pip install --upgrade oauth2client
🐍 from oauth2client.client import GoogleCredentials
📦 google-auth
(googleapis/google-auth-library-python): provides the ability to authenticate to Google APIs using various methods. It comprises two sub-packages: google.auth
and google.oauth2
.
💻 $ pip install google-auth
🐍 from google.auth.transport.requests import Request
🐍 from google.oauth2.credentials import Credentials
📦 OAuthLib
(oauthlib/oauthlib): a Python framework which implements the logic of OAuth1 or OAuth2 without assuming a specific HTTP request object or web framework.
💻 $ pip install oauthlib
🐍 from oauthlib.oauth2 import WebApplicationClient
📦 google-auth-oauthlib
(googleapis/google-auth-library-python-oauthlib): used for this project. It contains experimental OAuthLib
integration with google-auth
.
💻 $ pip install google-auth-oauthlib
🐍 from google_auth_oauthlib.flow import Flow
MIT license.