How is django-allauth
implemented to obtain authorization using Oauth2 for a Google API (in my case the Gmail API)? Additionally, I am looking to implement this separately from using django-allauth
to have users log in with Google, so I would need to store it separately, and also call it in a view.
Thanks!
How is django-allauth
implemented to obtain authorization using Oauth2 for a Google API (in my case the Gmail API)? Additionally, I am looking to implement this separately from using django-allauth
to have users log in with Google, so I would need to store it separately, and also call it in a view.
Thanks!
Share Improve this question asked Feb 6 at 20:34 Kovy JacobKovy Jacob 1,0278 silver badges24 bronze badges1 Answer
Reset to default 1I'm assuming you have already followed this document and this, and made all the necessary primary settings.
Then you can use django-allauth
to set up authentication via Google, with tokens saved. Specify the required parameters for gmail scope
. Then use these tokens to work with GmailAPI
.
First you need to set some settings, this is described in this document (SOCIALACCOUNT_STORE_TOKENS
constant). Something like this:
# settings.py
SOCIALACCOUNT_STORE_TOKENS = True
SOCIALACCOUNT_EMAIL_AUTHENTICATION = True
SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = True
Also configure “access_type”: “offline”
, in the google
provider settings, as described here:
You must set
AUTH_PARAMS['access_type']
tooffline
in order to receive a refresh token on first login and on reauthentication requests (which is needed to refresh authentication tokens in the background, without involving the user’s browser). When unspecified, Google defaults toonline
.
Now, after the user logs in to your system through google
account, a database entry will be created with his access_token
and refresh_token (token_secret in db_table)
. You can check this in the admin interface in this table:
To use GmailAPI
, you need the google
user ID and its access_token
, this can be found like this:
def get_user_google_tokens(request: HttpRequest, limit: int = 1) -> list[dict]:
from allauth.socialaccount.models import SocialToken
from django.db import models
user_google_tokens = list(
SocialToken.objects
.filter(account__provider='google', account__user_id=request.user.pk)
.annotate(google_user_id=models.F('account__uid'))
.order_by('-expires_at')
.values('token', 'token_secret', 'expires_at', 'google_user_id')[:limit]
)
print(user_google_tokens)
# [
# {'token': '...',
# 'token_secret': '...',
# 'expires_at': datetime.datetime(2025, 2, 7, 16, 38, 16, 611588, tzinfo=datetime.timezone.utc),
# 'google_user_id': '...'}
# ]
return user_google_tokens
Note that access_token
has a short validity of one hour, by default. You can read more about it here. When your access_token
expires in time, you will need to refresh it using refresh_token (token_secret)
, and write new access_token
to the database by modifying an existing entry. Here's an example of how to refresh. Also note that sometimes you will not be able to refresh tokens using refresh_token
.
Here is an example of a request to update a token using Postman
. But you will probably use some library for http
requests for python
. Or some off-the-shelf client, for that matter. django-allauth
doesn't seem to be able to update tokens, but I don't know for sure, I haven't checked that.
UPDATED
The answer to your question from the comments. Yes, you can use two google
social applications, with different client_id
, and different settings, but you would then need to use a custom DefaultSocialAccountAdapter
, and your own views to specify which config should be used. There may be a simpler option, but this is the first one I tried and it works.
There is also a much simpler option, you can have one configuration for the google
provider, but you can set dynamically the parameters to be used, for example scope
and access_type
.
Here's an example, you have a google
login endpoint, let's say it's the default google_login
endpoint in django-allauth
. So you also have one config set up for the google
provider. By default, you don't need to ask the user for permission to use their gmail
, in which case they just log in. Then, the user wants to use your functions that work with GmailAPI
, you ask them to authenticate again and provide additional permissions. In django-allauth
, these dynamic parameters can be passed using GET
parameters. Here's an example of how to do this using HTML
exclusively. You'll probably have this in different files, but here is the gist of how it's done:
{#Google Gmail Login Form#}
<form method="POST" action="{% url 'google_login' %}?scope=https://mail.google.com/&auth_params=access_type=offline">
{% csrf_token %}
<button type="submit">GOOGLE GMAIL</button>
</form>
{#Google Base Login Form#}
<form method="POST" action="{% url 'google_login' %}">
{% csrf_token %}
<button type="submit">GOOGLE</button>
</form>
Here's my basic google
config in settings:
SOCIALACCOUNT_PROVIDERS = {
'google': {
'APP': {
'client_id': '.....',
'secret': '.....',
'key': '.....',
'settings': {
'scope': [
'profile',
'email',
],
'auth_params': {
'access_type': 'online',
# it is important to leave it so that the `google`
# confirmation window is always displayed.
'prompt': 'consent',
},
'oauth_pkce_enabled': True,
},
},
'FETCH_USER_INFO': True,
},
}
The point is that when a user logs in via google
, the refresh_token
is not saved, but when logging in to google
, with additional parameters - ?scope=https://mail.google.com/&auth_params=access_type=offline
it will be saved in the database. In this way it will be possible to understand if the user has permissions to use GmailAPI
:
user_google_tokens = list(
SocialToken.objects
.filter(account__provider='google', account__user_id=some_user.id)
.exclude(token_secret='')
.annotate(google_user_id=models.F('account__uid'))
.order_by('-expires_at')
.values('token', 'token_secret', 'expires_at', 'google_user_id')[:1]
)
Here is an example method to handle dynamically passed auth_params
, from GET
request parameters. You can also check out the other methods to learn more.