make secret optional. add update_func to pass on token updates. (#70) * make secret optional. add update_func to pass on token updates. * fix typing * fix typing (try 2) * fix a few bugs when using / not using client secret
1.3.6 Automatically generated by python-semantic-release
@@ -2,6 +2,9 @@
|
|
2 |
|
3 |
<!--next-version-placeholder-->
|
4 |
|
|
|
|
|
|
|
5 |
## v1.3.5 (2022-08-05)
|
6 |
|
7 |
|
2 |
|
3 |
<!--next-version-placeholder-->
|
4 |
|
5 |
+
## v1.3.6 (2022-08-09)
|
6 |
+
|
7 |
+
|
8 |
## v1.3.5 (2022-08-05)
|
9 |
|
10 |
|
@@ -5,7 +5,7 @@
|
|
5 |
# is zero for an official release, positive for a development branch,
|
6 |
# or negative for a release candidate or beta (after the base version
|
7 |
# number has been incremented)
|
8 |
-
__version__ = "1.3.
|
9 |
version_info = (
|
10 |
int(__version__.split(".")[0]),
|
11 |
int(__version__.split(".")[1]),
|
5 |
# is zero for an official release, positive for a development branch,
|
6 |
# or negative for a release candidate or beta (after the base version
|
7 |
# number has been incremented)
|
8 |
+
__version__ = "1.3.6"
|
9 |
version_info = (
|
10 |
int(__version__.split(".")[0]),
|
11 |
int(__version__.split(".")[1]),
|
@@ -298,17 +298,19 @@ class OpenIDRestClient(RestClient):
|
|
298 |
token_url (str): base address of token service
|
299 |
refresh_token (str): initial refresh token
|
300 |
client_id (str): client id
|
301 |
-
client_secret (str): client secret
|
302 |
-
|
303 |
-
|
|
|
304 |
"""
|
305 |
def __init__(
|
306 |
self,
|
307 |
address: str,
|
308 |
token_url: str,
|
309 |
refresh_token: str,
|
310 |
client_id: str,
|
311 |
-
client_secret: str,
|
|
|
312 |
**kwargs: Any
|
313 |
) -> None:
|
314 |
super().__init__(address, **kwargs)
|
@@ -319,14 +321,15 @@ def __init__(
|
|
319 |
self.access_token = None
|
320 |
self.refresh_token: Optional[Union[str, bytes]] = refresh_token
|
321 |
self.token_func = True # type: ignore
|
|
|
322 |
self._get_token()
|
323 |
|
324 |
def _get_token(self) -> None:
|
325 |
if self.access_token:
|
326 |
# check if expired
|
327 |
try:
|
328 |
data = self.auth.validate(self.access_token)
|
329 |
-
if data['exp'] < time.time()-
|
330 |
raise Exception()
|
331 |
return
|
332 |
except Exception:
|
@@ -339,8 +342,9 @@ def _get_token(self) -> None:
|
|
339 |
'grant_type': 'refresh_token',
|
340 |
'refresh_token': self.refresh_token,
|
341 |
'client_id': self.client_id,
|
342 |
-
'client_secret': self.client_secret,
|
343 |
}
|
|
|
|
|
344 |
|
345 |
try:
|
346 |
r = requests.post(self.auth.token_url, data=args)
|
@@ -352,6 +356,8 @@ def _get_token(self) -> None:
|
|
352 |
self.logger.debug('OpenID token refreshed')
|
353 |
self.access_token = req['access_token']
|
354 |
self.refresh_token = req['refresh_token'] if 'refresh_token' in req else None
|
|
|
|
|
355 |
return
|
356 |
|
357 |
raise Exception('No token available')
|
298 |
token_url (str): base address of token service
|
299 |
refresh_token (str): initial refresh token
|
300 |
client_id (str): client id
|
301 |
+
client_secret (str): client secret (optional - required for refresh tokens)
|
302 |
+
update_func (callable): a function that gets called when the access and refresh tokens are updated (optional)
|
303 |
+
timeout (int): request timeout (optional)
|
304 |
+
retries (int): number of retries to attempt (optional)
|
305 |
"""
|
306 |
def __init__(
|
307 |
self,
|
308 |
address: str,
|
309 |
token_url: str,
|
310 |
refresh_token: str,
|
311 |
client_id: str,
|
312 |
+
client_secret: Optional[str] = None,
|
313 |
+
update_func: Optional[Callable[[Union[str, bytes], Optional[Union[str, bytes]]], None]] = None,
|
314 |
**kwargs: Any
|
315 |
) -> None:
|
316 |
super().__init__(address, **kwargs)
|
321 |
self.access_token = None
|
322 |
self.refresh_token: Optional[Union[str, bytes]] = refresh_token
|
323 |
self.token_func = True # type: ignore
|
324 |
+
self.update_func = update_func
|
325 |
self._get_token()
|
326 |
|
327 |
def _get_token(self) -> None:
|
328 |
if self.access_token:
|
329 |
# check if expired
|
330 |
try:
|
331 |
data = self.auth.validate(self.access_token)
|
332 |
+
if data['exp'] < time.time()-self._token_expire_delay_offset:
|
333 |
raise Exception()
|
334 |
return
|
335 |
except Exception:
|
342 |
'grant_type': 'refresh_token',
|
343 |
'refresh_token': self.refresh_token,
|
344 |
'client_id': self.client_id,
|
|
|
345 |
}
|
346 |
+
if self.client_secret:
|
347 |
+
args['client_secret'] = self.client_secret
|
348 |
|
349 |
try:
|
350 |
r = requests.post(self.auth.token_url, data=args)
|
356 |
self.logger.debug('OpenID token refreshed')
|
357 |
self.access_token = req['access_token']
|
358 |
self.refresh_token = req['refresh_token'] if 'refresh_token' in req else None
|
359 |
+
if self.access_token and self.update_func:
|
360 |
+
self.update_func(self.access_token, self.refresh_token)
|
361 |
return
|
362 |
|
363 |
raise Exception('No token available')
|
@@ -383,24 +383,27 @@ def initialize(self, oauth_client_id, oauth_client_secret, oauth_client_scope=No
|
|
383 |
if oauth_client_scope:
|
384 |
self.oauth_client_scope = oauth_client_scope.split()
|
385 |
else:
|
386 |
-
self.oauth_client_scope = ['
|
|
|
|
|
387 |
|
388 |
async def get_authenticated_user(
|
389 |
self, redirect_uri: str, code: str
|
390 |
) -> Dict[str, Any]:
|
391 |
http = self.get_auth_http_client()
|
392 |
-
body =
|
393 |
'redirect_uri': redirect_uri,
|
394 |
'code': code,
|
395 |
'client_id': self.oauth_client_id,
|
396 |
-
'client_secret': self.oauth_client_secret,
|
397 |
'grant_type': 'authorization_code',
|
398 |
-
}
|
|
|
|
|
399 |
response = await http.fetch(
|
400 |
self._OAUTH_ACCESS_TOKEN_URL,
|
401 |
method='POST',
|
402 |
headers={'Content-Type': 'application/x-www-form-urlencoded'},
|
403 |
-
body=body,
|
404 |
)
|
405 |
ret = tornado.escape.json_decode(response.body)
|
406 |
if not ret.get('id_token', ''):
|
@@ -447,14 +450,23 @@ async def get(self):
|
|
447 |
redirect_uri=self.get_login_url(),
|
448 |
code=self.get_argument('code'),
|
449 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
# Save the user with e.g. set_secure_cookie
|
451 |
self.set_secure_cookie('access_token', user['access_token'],
|
452 |
-
expires_days=float(
|
453 |
if 'refresh_token' in user:
|
454 |
self.set_secure_cookie('refresh_token', user['refresh_token'],
|
455 |
-
expires_days=float(
|
456 |
self.set_secure_cookie('identity', tornado.escape.json_encode(user['id_token']),
|
457 |
-
expires_days=float(
|
458 |
if data.get('redirect', None):
|
459 |
url = data['redirect']
|
460 |
if 'state' in data:
|
@@ -466,8 +478,11 @@ async def get(self):
|
|
466 |
raise tornado.web.HTTPError(400, reason='missing redirect')
|
467 |
else:
|
468 |
state = {}
|
469 |
-
|
470 |
-
|
|
|
|
|
|
|
471 |
elif not self.settings.get('debug', False):
|
472 |
raise tornado.web.HTTPError(400, 'missing redirect')
|
473 |
if self.get_argument('state', False):
|
383 |
if oauth_client_scope:
|
384 |
self.oauth_client_scope = oauth_client_scope.split()
|
385 |
else:
|
386 |
+
self.oauth_client_scope = ['profile', 'groups']
|
387 |
+
if oauth_client_secret:
|
388 |
+
self.oauth_client_scope.append('offline_access')
|
389 |
|
390 |
async def get_authenticated_user(
|
391 |
self, redirect_uri: str, code: str
|
392 |
) -> Dict[str, Any]:
|
393 |
http = self.get_auth_http_client()
|
394 |
+
body = {
|
395 |
'redirect_uri': redirect_uri,
|
396 |
'code': code,
|
397 |
'client_id': self.oauth_client_id,
|
|
|
398 |
'grant_type': 'authorization_code',
|
399 |
+
}
|
400 |
+
if self.oauth_client_secret:
|
401 |
+
body['client_secret'] = self.oauth_client_secret
|
402 |
response = await http.fetch(
|
403 |
self._OAUTH_ACCESS_TOKEN_URL,
|
404 |
method='POST',
|
405 |
headers={'Content-Type': 'application/x-www-form-urlencoded'},
|
406 |
+
body=urllib.parse.urlencode(body),
|
407 |
)
|
408 |
ret = tornado.escape.json_decode(response.body)
|
409 |
if not ret.get('id_token', ''):
|
450 |
redirect_uri=self.get_login_url(),
|
451 |
code=self.get_argument('code'),
|
452 |
)
|
453 |
+
|
454 |
+
# set expire times (can be 0 in user data, which is an invalid cookie)
|
455 |
+
access_expire = user.get('expires_in', 0)
|
456 |
+
if not access_expire:
|
457 |
+
access_expire = 1800
|
458 |
+
refresh_expire = user.get('refresh_expires_in', 0)
|
459 |
+
if not refresh_expire:
|
460 |
+
refresh_expire = 86400
|
461 |
+
|
462 |
# Save the user with e.g. set_secure_cookie
|
463 |
self.set_secure_cookie('access_token', user['access_token'],
|
464 |
+
expires_days=float(access_expire)/3600/24)
|
465 |
if 'refresh_token' in user:
|
466 |
self.set_secure_cookie('refresh_token', user['refresh_token'],
|
467 |
+
expires_days=float(refresh_expire)/3600/24)
|
468 |
self.set_secure_cookie('identity', tornado.escape.json_encode(user['id_token']),
|
469 |
+
expires_days=float(refresh_expire)/3600/24)
|
470 |
if data.get('redirect', None):
|
471 |
url = data['redirect']
|
472 |
if 'state' in data:
|
478 |
raise tornado.web.HTTPError(400, reason='missing redirect')
|
479 |
else:
|
480 |
state = {}
|
481 |
+
redirect = self.get_argument('next', None)
|
482 |
+
if not redirect:
|
483 |
+
redirect = self.get_argument('redirect', None)
|
484 |
+
if redirect:
|
485 |
+
state['redirect'] = redirect
|
486 |
elif not self.settings.get('debug', False):
|
487 |
raise tornado.web.HTTPError(400, 'missing redirect')
|
488 |
if self.get_argument('state', False):
|