How do I refresh the Google access token with Django allauth?

The access token for google typically lasts 1 hour. How do I refresh the token without having the user login again?

github code for project: https://github.com/theptrk/learn_gcal

django-allauth Google login is set up the normal way with an added scope

THIRD_PARTY_APPS = [
  # ...
  "allauth.socialaccount",
  "allauth.socialaccount.providers.google",

# This allows you to assign a site to a "social application"
SITE_ID = 1

SOCIALACCOUNT_PROVIDERS = {
    "google": {
        "SCOPE": [
                        # minimum scopes
            "profile",
            "email",
                        # additional scopes
            "https://www.googleapis.com/auth/calendar.readonly",
        ],
        "AUTH_PARAMS": {
            "access_type": "offline",
        },
    }
}

# https://django-allauth.readthedocs.io/en/latest/configuration.html
# Use this for additional scopes: This defaults to false
SOCIALACCOUNT_STORE_TOKENS = True

Creating the credentials is different than the docs since the docs use a "secrets file" instead of getting tokens from the db

# get user and client credentials
token = SocialToken.objects.get(
  account__user=request.user, account__provider="google"
)
client_id = env("GOOGLE_CLIENT_ID", default="")
client_secret = env("GOOGLE_CLIENT_SECRET", default="")

# create Credentials object
from google.oauth2.credentials import Credentials
creds=Credentials(
    token=token.token,
    refresh_token=token.token_secret,
    token_uri="https://oauth2.googleapis.com/token",
    client_id=client_id,
    client_secret=client_secret,
)

Trying to retrieve events is totally fine if the access token is not expired

# retrieve google calendar events
from googleapiclient.discovery import build
from datetime import datetime
service = build("calendar", "v3", credentials=creds)
try:
    events_result = (
      service.events()
      .list(
          calendarId="primary",
          timeMin=datetime.now().date().isoformat() + "Z",
          maxResults=30,
          singleEvents=True,
          orderBy="startTime",
      )
      .execute()
    )
except Exception as e:
  print("Google API Error")
  if e.status_code == 403:
    if e.reason == "Request had insufficient authentication scopes.":
      print("user needs to grant calendar scopes")

  print("Token is likely expired at this point")
  # I've tried:
  # creds.refresh(...) but it hasnt worked

From the docs it seems that if you specify the access type as offline, the refresh token will be received on the first login and on reauthentication requests. So from my understanding, the library will handle the auth token refreshing in the background, when needed.

If the token expires, you will receive a from google.auth.exceptions import RefreshError error and you must implement a flow for the user to authorize your app again.

# retrieve google calendar events
from googleapiclient.discovery import build
from datetime import datetime
from google.auth.exceptions import RefreshError
from googleapiclient.errors import HttpError
service = build("calendar", "v3", credentials=creds)
try:
    events_result = (
      service.events()
      .list(
          calendarId="primary",
          timeMin=datetime.now().date().isoformat() + "Z",
          maxResults=30,
          singleEvents=True,
          orderBy="startTime",
      )
      .execute()
    )
except HttpError as e:
  print("Google API Error")
  if e.status_code == 403:
    if e.reason == "Request had insufficient authentication scopes.":
      print("user needs to grant calendar scopes")
except RefreshError as e:
  # refresh token expired, start the flow for the user authorization

There might be multiple reasons for offline access refresh token expiration/revocation. More info about that here.

Hope this helps a bit!

Back to Top