Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Generic] Implement refresh_user to periodically refresh OAuth tokens #475

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions oauthenticator/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
import base64
import os
import time
from urllib.parse import urlencode

from jupyterhub.auth import LocalAuthenticator
Expand Down Expand Up @@ -143,11 +144,18 @@ def _create_auth_state(token_response, user_data_response):
if isinstance(scope, str):
scope = scope.split(' ')

try:
expires_in = int(token_response.get('expires_in'))
expires_at = time.time() + expires_in # seconds
except (KeyError, TypeError):
expires_at = None

return {
'access_token': access_token,
'refresh_token': refresh_token,
'oauth_user': user_data_response,
'scope': scope,
'expires_at': expires_at,
}

@staticmethod
Expand Down Expand Up @@ -216,6 +224,46 @@ async def authenticate(self, handler, data=None):

return user_info

# Refresh user access and refresh tokens (called periodically)
async def refresh_user(self, user, handler=None):
# Retrieve user authentication info and check if refresh is needed
auth_state = await user.get_auth_state()
expires_at = auth_state.get('expires_at', None)
refresh_token = auth_state.get('refresh_token', None)

# If no refresh_token, return success
if not refresh_token:
return True

# If no expiration, return success
if not expires_at:
return True

# If over 2x auth_refresh_age intervals from expiration, return success
if (time.time() + 2 * self.auth_refresh_age) < expires_at:
return True

self.log.info('Refreshing tokens for user %s', user.name)

# Attempt renewal
params = dict(
client_id=self.client_id,
client_secret=self.client_secret,
refresh_token=refresh_token,
grant_type='refresh_token',
)

headers = self._get_headers()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one is wrong. It has to be
headers = { "Content-Type": "application/x-www-form-urlencoded" }


token_resp_json = await self._get_token(headers, params)
user_data_resp_json = await self._get_user_data(token_resp_json)

user_info = {
'auth_state': self._create_auth_state(token_resp_json, user_data_resp_json),
}

return user_info


class LocalGenericOAuthenticator(LocalAuthenticator, GenericOAuthenticator):
"""A version that mixes in local system user creation"""
Expand Down