I was using this tutorial: https://www.geeksforgeeks.org/python-django-google-authentication-and-fetching-mails-from-scratch/. However, the oauth2client library is deprecated and hasn't been touched in almost 9 years. I have looked for various tutorials, but they seem to be written by AI; they make no sense, and do things like tell you to include credentials in a piece of code that does not use credentials.
So my question is, how can I implement oauth2 into my Django application so that user's can go to the Django website and give permission to read their emails?
I will start from the beginning and describe a minimally working example without using the oauth2
libraries. This tutorial intentionally omits error handling and security work, for simplicity. You might want to try using django-allauth
, to implement google oauth2
authentication, check out documentation. Also, I suggest you read this document, this and as well as this and get everything set up (if you haven't already).
To work, we need some high-level python library to make http
requests, for example: request
or httpx
. In this example, I will use httpx
. In addition, since we will be working with gmail.api
in test mode, you need to add Test users
here, for example, your gmail
account will do.
Here's a minimal working example to get things working:
# views.py
from typing import Self, Sequence
from urllib.parse import urlencode
import httpx
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import render
GOOGLE_OAUTH2_CREDENTIALS = {
'client_id': 'your_client_id',
'client_secret': 'your_client_secret',
'scope': 'profile email https://mail.google.com/'.split(),
'redirect_uri': 'your_redirect_uri',
}
class GoogleOauthBackend:
AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
ACCESS_TOKEN_URL = 'https://oauth2.googleapis.com/token'
USER_INFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo'
def __init__(
self,
client_id: str,
client_secret: str,
scope: Sequence[str],
redirect_uri: str,
**optional) -> None:
self.client_id = client_id
self.client_secret = client_secret
self.scope = scope
self.redirect_uri = redirect_uri
self.optional = optional
@classmethod
def from_credentials(cls, credentials: dict) -> Self:
return cls(
client_id=credentials['client_id'],
client_secret=credentials['client_secret'],
scope=credentials['scope'],
redirect_uri=credentials['redirect_uri'],
)
def get_auth_url(self) -> str:
params = {
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'response_type': 'code',
'scope': ' '.join(self.scope),
**self.optional,
}
return f'{self.AUTH_URL}?{urlencode(params)}'
def get_user_info(self, access_token: str, token_type: str) -> dict:
response = httpx.get(
url=self.USER_INFO_URL,
headers={
'Authorization': f'{token_type} {access_token}',
},
)
return response.json()
def get_access_token(self, code: str) -> tuple[str, str]:
params = {
'client_id': self.client_id,
'client_secret': self.client_secret,
'code': code,
'redirect_uri': self.redirect_uri,
'grant_type': 'authorization_code',
}
response = httpx.post(url=self.ACCESS_TOKEN_URL, data=params)
data = response.json()
return data['access_token'], data['token_type']
class GoogleGmailClient:
API_URL = 'https://gmail.googleapis.com'
def __init__(self, user_id: int, access_token: str, token_type: str):
self.user_id = user_id
self.access_token = access_token
self.token_type = token_type
def get_user_mail_messages(self):
url = f'{self.API_URL}/gmail/v1/users/{self.user_id}/messages'
return httpx.get(url=url, headers=self.headers).json()['messages']
def get_user_mail_message_details(self, mail_id: str):
url = f'{self.API_URL}/gmail/v1/users/{self.user_id}/messages/{mail_id}'
return httpx.get(url=url, headers=self.headers).json()
@property
def headers(self):
return {
'Authorization': f'{self.token_type} {self.access_token}',
}
def main_page(request):
return render(request=request, template_name='main_page.html')
def google_login(request):
google_oauth_backend = GoogleOauthBackend.from_credentials(
credentials=GOOGLE_OAUTH2_CREDENTIALS,
)
return HttpResponseRedirect(google_oauth_backend.get_auth_url())
def google_callback(request):
code = request.GET.get('code')
google_oauth_backend = GoogleOauthBackend.from_credentials(
credentials=GOOGLE_OAUTH2_CREDENTIALS,
)
access_token, token_type = google_oauth_backend.get_access_token(code=code)
user_info = google_oauth_backend.get_user_info(
access_token=access_token,
token_type=token_type,
)
user_id = user_info['id']
first_user_mail_details = get_user_first_mail_message_details(
GoogleGmailClient(
user_id=user_id,
access_token=access_token,
token_type=token_type,
),
)
return JsonResponse(data=first_user_mail_details)
def get_user_first_mail_message_details(gmail_client: GoogleGmailClient):
mails = gmail_client.get_user_mail_messages()
first_mail_id = mails[0]['id']
return gmail_client.get_user_mail_message_details(mail_id=first_mail_id)
{#main_page.html#}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form method="POST" action="{% url 'google_login' %}">
{% csrf_token %}
<button type="submit">GOOGLE</button>
</form>
</body>
</html>
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.main_page),
path('google-login/', views.google_login, name='google_login'),
path('google-callback/', views.google_callback),
]
So. Essentially, there are three endpoints in this example. The home page loads HTML
, with a GOOGLE
button, clicking on the button will call the google-login/
endpoint, which redirects the user to log in via Google. After that google will invoke the google-callback/
endpoint, where if everything went smoothly, you will receive code
which is exchanged for an access token that will allow you to make authenticated requests on behalf of the user to api
google.
In addition, here are some useful links:
This is an example, not for production, as I wrote above that many things are highly simplified, however it should give some insight for you and lots of links including off-the-shelf solutions. I hope this will be helpful to you.