J. Woods
J. Woods

Reputation: 23

Python Social Auth, Google, and refresh token

In a personal project, I am trying to use Django as my front end and then allow data entered by users in a particular form to be copied to google sheets.

Google's own docs recommend using https://github.com/google/oauth2client which is now deprecated, and the docs have not been updated. With this, I have started attempting to use Python Social Auth and Gspread. For Gspread to be able to function correctly, I need to be able to pass it not only an access token but also a refresh token. Python Social Auth however is not persisting the refresh token along with the rest of the "extra data". Looking at the data preserved and the URLs routed to, it seems to me more like somewhere it is routing through Google+.

I have the following configurations in my Django settings files:

AUTHENTICATION_BACKENDS = (
    'social_core.backends.google.GoogleOAuth2',
    'django.contrib.auth.backends.ModelBackend',
)

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
    'social_core.pipeline.social_auth.associate_by_email',
)    

SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '...'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '...'
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['https://www.googleapis.com/auth/spreadsheets']

Upvotes: 1

Views: 2330

Answers (3)

Shashank Hegde
Shashank Hegde

Reputation: 2579

Just provide this in your settings.py:

SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = { 'access_type': 'offline', 'hd': 'xyzabc.com', 'approval_prompt':'force' } remeber there is {'approval_prompt' : 'force'} which will force the user to select the gmail account, this way you will get refresh token.

Upvotes: 2

rusheb
rusheb

Reputation: 1398

You can send extra parameters to the OAuth2 provider using the variable

SOCIAL_AUTH_<PROVIDER>_AUTH_EXTRA_ARGUMENTS

For Google, you can see the extra parameters they accept in their documentation (scroll down to "parameters"). The one we are looking for is access_type:

access_type: Indicates whether your application can refresh access tokens when the user is not present at the browser. Valid parameter values are online, which is the default value, and offline.

So we can add the following to settings.py, to indicate that we want to receive a refresh token:

SOCIAL_AUTH_GOOGLE_OAUTH2_EXTRA_ARGUMENTS = {"access_type: offline"}

The results from EXTRA_ARGUMENTS will be stored in extra_data, so the refresh token can be accessed like this:

refresh_token = user.social_auth.get(provider="google-oauth2").extra_data["refresh_token"]

One possible solution is to store the refresh token alongside the user in a UserProfile model, by adding a custom function to the social-auth pipeline:

  1. Create the model
# models.py

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
    refresh_token = models.CharField(max_length=255, default="")
  1. Add a function to access store the refresh token
# pipeline.py

from .models import UserProfile

def store_refresh_token(user=none, *args, **kwargs):
    extra_data = user.social_auth.get(provider="google-oauth2").extra_data
    UserProfile.objects.get_or_create(
        user=user, defaults={"refresh_token": extra_data["refresh_token"]}
    )
  1. Add our new function to the social-auth pipeline.
# settings.py

...

SOCIAL_AUTH_PIPELINE = (
    ...
    "my-app.pipeline.store_refresh_token"
)  

SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
    'https://www.googleapis.com/auth/spreadsheets'
    # any other scopes you need
]

...

The token is now stored alongside the user and can be used to initialise the sheets client or whatever else you need.

Upvotes: 1

omab
omab

Reputation: 3701

It's true that python-social-auth will use some bits of the Google+ platform, at least the API to retrieve details about the user to fill in the account.

From your settings, I see you have associate_by_email at the bottom, at that point, at that point it has no use since the user is already be created, if you really plan to use it, it must be before the create_user one, you can check the DEFAULT_PIPELINE as a reference.

In order to get a refresh_token from google, you need to tell it that you want one, to do that you need to set the offline access type:

SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {
  'access_type': 'offline'
}

With that setting Google will give you a refresh_token and it will automatically stored in extra_data.

Upvotes: 2

Related Questions