Google OAuth RefreshError even with Refresh Token present in the Django app

Viewed 301

My app running on Django with Django allauth allows signup/login only via Google OAuth. Therefore when a user is registered, his social oauth details are saved to All-auth Social Accounts model and the token data saved in Social Application Tokens model.

We want the users to be logged-in as long as possible, without granting/revoking access regularly.

Question 1: The default expiry is set as 1 hour from the registration time. How can I extend this programmatically and for how long (can it be never-expire?)

Currently, a user logged in at 5:00 and I get the following data from credentials.to_json():

{
"token": "ya29.A0ARrdaM8YXPM35RXPv7UK-pXLZvWG49T-MgCZ5wMse2ADMXOZJOWFJKMaq1PkobADLptM5YX5mnrliS2yCCESqCk0NTaZJkfe6inK94j6WQMFZWIT_xRyBTOX4W3dUEiuLhHFpQcD5vS-x_Y22pUzxwgI23pp",
"refresh_token": "1//0gYC8oucHhTBVCgYIARAAGBASNwF-L9IrCG7c5IJCBMVznUrytGEFsJbsObAFvmNBoQbHHGA1KESyBWgmudEVogbes8ski87q_5g",
"client_id": "blablabla.apps.googleusercontent.com",
"client_secret": "xyzsecret"}

No other data is returned.

At 6:05, the credentials.to_json() is exactly the SAME AS ABOVE. But to fetch any Google/Youtube API data, I get the following error in server logs:

google.auth.exceptions.RefreshError: The credentials do not contain the necessary fields need to refresh the access token.

Question 02: When there's a Refresh Token available already, why the error? As per the docs, it refreshes the token automatically few minutes before the expiry. What am I missing?

I already had "access_type": "offline" in the providers settings. I also tried adding "prompt": "consent", but no effect.

Django Settings:

INSTALLED_APPS = [
    "allauth",
    "allauth.account",
    "allauth.socialaccount",
    "allauth.socialaccount.providers.google",
    ...
]

AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "allauth.account.auth_backends.AuthenticationBackend",
]

SOCIALACCOUNT_PROVIDERS = {
    "google": {
        "SCOPE": [
            "profile",
            "email",
            "https://www.googleapis.com/auth/youtube",
            "https://www.googleapis.com/auth/youtube.readonly",
            "https://www.googleapis.com/auth/youtube.upload",
            "https://www.googleapis.com/auth/youtube.force-ssl",
        ],
        "AUTH_PARAMS": {
            "access_type": "offline",
            "prompt": "consent",
        },
    },
}

And the Django Views snippet related to OAuth:

import googleapiclient.discovery
import googleapiclient.errors
from allauth.socialaccount.models import SocialAccount, SocialApp
from google.oauth2.credentials import Credentials

def get_credentials_google(user):
    app_google = SocialApp.objects.get(provider="google")
    account = SocialAccount.objects.get(user=user)
    user_tokens = account.socialtoken_set.first()
    creds = Credentials(
        token=user_tokens.token,
        refresh_token=user_tokens.token_secret,
        client_id=app_google.client_id,
        client_secret=app_google.secret,
    )
    return creds

def get_youtube_account(user):
    api_service_name = "youtube"
    api_version = "v3"
    credentials = get_credentials_google(user)
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials
    )
    return youtube

def get_youtube_videos(request):
    youtube = get_youtube_account(request.user)
    request = youtube.liveBroadcasts().list(
        part="id, snippet, contentDetails, status",
        broadcastStatus="completed",
        broadcastType="all"
    )
    response = request.execute()
    return response

Note: There's no front-end framework I'm using django with django template UI.

1 Answers

How can I extend this programmatically and for how long (can it be never-expire?)

You cant access tokens expire after one hour this is standard. You can use the refresh token to request a new access token as you need

When there's a Refresh Token available already, why the error? As per the docs, it refreshes the token automatically few minutes before the expiry. What am I missing?

Refresh tokens are used to request a new access token approximately five minutes before its due to expire.

google.auth.exceptions.RefreshError: The credentials do not contain the necessary fields need to refresh the access token.

Sounds like your not setting the client object properly. Something here is not set right. Also remember if your app is still in testing your refresh token will expire after seven days and you will need to reauthorize your test users.

creds = Credentials(
    token=user_tokens.token,
    refresh_token=user_tokens.token_secret,
    client_id=app_google.client_id,
    client_secret=app_google.secret,
)
Related