I store the token and its expiry encrypted in the database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # ====================================================================== # Q: How do you refresh an OAuth2 access token without 50 workers doing it at # the same time? # # I store the token and its expiry encrypted in the database. I refresh shortly # before it expires. To avoid a double refresh (some providers invalidate the # old refresh token), I lock the row with select_for_update and re-check inside # the lock. # If the refresh fails with invalid_grant (the user revoked access), I mark the # integration as disconnected and notify the customer, instead of retrying # forever. # ====================================================================== def get_access_token(integration_id): integ = Integration.objects.get(pk=integration_id) if integ.expires_at > now() + timedelta(minutes=2): return integ.access_token # fast path with transaction.atomic(): integ = Integration.objects.select_for_update().get(pk=integration_id) if integ.expires_at > now() + timedelta(minutes=2): return integ.access_token # another worker already refreshed it tokens = oauth_provider.refresh(integ.refresh_token) integ.access_token = tokens['access_token'] integ.refresh_token = tokens.get('refresh_token', integ.refresh_token) integ.expires_at = now() + timedelta(seconds=tokens['expires_in']) integ.save(update_fields=['access_token', 'refresh_token', 'expires_at']) return integ.access_token |