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.
- Full OAuth2 authentication flow, with automatic access token refresh.
- Supporting two mechanisms:
- Identifying users by parsing Open ID Connect (OIDC) ID tokens.
- Handling backend errors, warnings and infos, and storing their respective messages to the session key
messages, which is added as a variable to template's context, and then rendered on the frontend. - Handling access to some special paths like
/loginand/auth. @require_authdecorator for views.- logout view function, that clears browser cookies along with the corresponding session from Django's database.
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-binaryin your Python virtual environment and override the sqlite3 module by prepending the following lines tosettings.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 [email protected]: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 runserverDiffing 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.htmlsample base template)my_django_app/views.py(views.py)db.sqlite3(automatically created by Django by runningpython manage.py migrate)
-
Modify
my_django_app/settings.py: Django admin site and static files apps are disabled. For middleware, onlySessionMiddlewareis 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):
- python - Getting refresh_token with lepture/authlib - Stack Overflow
-
client_credentialswon'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/β¦
-
- Refresh and Auto Update Token Β· Issue #245 Β· lepture/authlib
-
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.jsoncontains OAuth client ID credentials of the GCP project, that will provide access to Google user's resources for the Django app. -
Flowclass was used instead ofInstalledAppFlow. -
Instead of pickles, JSON Serialization of Credentials is used: OAuth2 token (a
Credentialsinstance) is converted to a dictionary and saved to the session (as a session key namedtoken):{ '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': '[email protected]', '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 thegoogle_auth_oauthlib.flow.Flowclass (google-auth-oauthlib 0.4.1documentation). -
Flow.credentialsconstructs agoogle.oauth2.credentials.Credentialsclass, which in turn is a child class ofgoogle.auth.credentials.Credentials -
Key
google.auth.credentials.Credentialsmembers, that are inherited by thegoogle_auth_oauthlib.flow.Flow.credentialsclass:to_json(): Returns A JSON representation of this instance. When converted into a dictionary, it can be passed tofrom_authorized_user_info()to create a new Credentials instance.id_token: can be verified and decoded (parsed) usinggoogle.oauth2.id_token.verify_oauth2_token()
-
Featured Google OAuth implementations and libraries:
-
π¦
oauth2client(googleapis/oauth2client): deprecated in favor ofgoogle-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.authandgoogle.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 experimentalOAuthLibintegration withgoogle-auth.
βββπ» $ pip install google-auth-oauthlib
βββπ from google_auth_oauthlib.flow import Flow
-
MIT license.

