question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Refresh and Auto Update Token

See original GitHub issue

I am trying to use signal for automatic token update . As mentioned in the docs we can use signals to do so. Here is my code for the client in Django.

@receiver(token_update)
def on_token_update(sender, token, refresh_token=None, access_token=None,**kwargs):
    print('inside token update client views')
    if refresh_token:
        item = OAuth2Token.find(name=name, refresh_token=refresh_token)
    elif access_token:
        item = OAuth2Token.find(name=name, access_token=access_token)
    else:
        return

    # update old token
    item.access_token = token['access_token']
    item.refresh_token = token.get('refresh_token')
    item.expires_at = token['expires_at']
    item.save()

oauth = OAuth()

oauth.register(
    name = '{{  client name }}',
    client_id = "{{  client id }}",
    client_secret = "{{ client secret }}",
    update_token = on_token_update,
    access_token_url = "https://127.0.0.1:8000/oauth/token/",
    refresh_token_url = "https://127.0.0.1:8000/oauth/token/",
    authorize_url = "https://127.0.0.1:8000/oauth/authorize",
    redirect_url = "https://127.0.0.2/authorize",
    grant_type = "authorization_code",
    id_token_signing_alg_values_supported = ['RS256'],
    authorize_params= {'access_type': 'offline'},

    client_kwargs = {
        'scope':'openid profile',
        'token_endpoint_auth_method': 'client_secret_basic',
    },
    jwks = {
        {{  key set values }}
    },
    prompt = "consent"
)

import time
def home(request):
    user = request.session.get('user')
    print('client user: session',user)
    if user:
        user = json.dumps(user)
    return render(request, 'home.html', context={'user': user})


def login(request):
    eyantra = oauth.create_client('eyantra')
    redirect_uri = "https://127.0.0.2:8001/authorize"
    return eyantra.authorize_redirect(request,redirect_uri)
    #return HttpResponse("login")


def auth(request):
    print('inside callback view in client')
    eyantra = oauth.create_client('eyantra')
    token = eyantra.authorize_access_token(request,verify=False,redirect_uri="https://127.0.0.2:8001/authorize",grant_type="authorization_code")
    print('token is :',token)
    user = eyantra.parse_id_token(request, token)
  
    request.session['user'] = user.get('name')
    return redirect('/')

def logout(request):
    request.session.pop('user',None)
    return redirect('/')

have added the update_token parameter inside oauth register function.Also the refresh_token_url. But token is not being updated after the respective expire_in time. Am I missing out something ?

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:8 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
dragonpawcommented, Aug 6, 2021

The sample in the docs simply doesn’t work. (I stumbled across this as well.) But you should be able to modify the below to work for you:

from authlib.integrations.django_client.integration import token_update
from django.dispatch import receiver
import pendulum

import logging
logger = logging.getLogger(__name__)

class TokenManager(models.Manager):
    def update_token(self, name, token, refresh_token=None, access_token=None):
        logger.debug("Auto-update of token(%s) started", name)
        # This assumes the unique name as seen on the model below. 
        item = self.get(name=name)

        # update old token
        item.update(token)
        logger.debug("Update complete.")


class Token(models.Model):
    objects = TokenManager()

    name = models.CharField(max_length=20, unique=True)

    # Oauth tokens:
    token_type = models.CharField(max_length=40)
    access_token = models.CharField(max_length=5000)
    refresh_token = models.CharField(max_length=5000, null=True)
    expires_at = PendulumDateTimeField()
    refresh_url = models.URLField(null=True)

    def __str__(self):
        return f"{self.name}: a_t:{self.access_token[0:4]}..., exp:{self.expires_at}"

    def update(self, token):
        self.access_token = token["access_token"]
        self.refresh_token = token.get("refresh_token")
        self.expires_at = pendulum.from_timestamp(token["expires_at"])
        self.token_type = token["token_type"]
        self.save()

    @property
    def expired(self):
        return self.expires_at < pendulum.now()

    @property
    def expiration_remaining(self):
        return self.expires_at - pendulum.now()

    def to_token(self):
        return dict(
            access_token=self.access_token,
            token_type=self.token_type,
            refresh_token=self.refresh_token,
            expires_at=int(self.expires_at.timestamp()),
        )


@receiver(token_update)
def update_token(
    name, token, refresh_token=None, access_token=None, **kwargs
):  # pylint: disable=unused-argument
    Token.objects.update_token(name, token, refresh_token, access_token)

Admittedly I do the update in the manager as I prefer as much as possible for code to be on my model and not elsewhere, in case I want to update it later. So my receiver is just one line redirecting to the manager responsible. (And you probably want to use generic datetimefields, not the pendulum-backed ones I use.)

Also be aware, unless you’re on authlib 0.14.3 or later, the django integration is broken for refresh: #193 (If you’re using the metadata url)

1reaction
mklassencommented, Apr 13, 2022

In the flask client, Authlib uses current_app as the signal sender, contrary to the documentation which explicit states, “Never pass current_app as sender to a signal.” (https://flask.palletsprojects.com/en/2.1.x/signals/?highlight=signals#sending-signals). As explained in the references documentation, Authlib should be using current_app._get_current_object() as the sender instead. The effective result is that Authlib currently does not reliably support signals in the flask client.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Should access tokens be refreshed automatically or ...
Should the backend automatically refresh it (if a refresh token was provided), or the refreshing should only be done at a refresh endpoint?...
Read more >
What Are Refresh Tokens and How to Use Them Securely
Once they expire, client applications can use a refresh token to "refresh" the access token. That is, a refresh token is a credential...
Read more >
How to update Refresh token automatically once it
In case of refresh token there is no way to update it automatically. I provided life time to 1 year for refresh token...
Read more >
Refreshing the Access Token
The client SDK will automatically refresh the access token if the feature is enabled. When you execute an API of the SDK and...
Read more >
OAuth 2.0 Refresh Token Best Practices
Refresh token rotation is a security measure offered to mitigate risks associated with leaked refresh tokens, single page applications (SPA) are ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found