Skip to content

Commit 42965f1

Browse files
committed
Merge branch 'events' into dev
2 parents 5996dbd + 2d66281 commit 42965f1

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

oauth2cli/oauth2.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,64 @@ class initialization.
287287
data.update(scope=scope)
288288
return self._obtain_token("client_credentials", data=data, **kwargs)
289289

290+
def __init__(self,
291+
client_id,
292+
on_obtaining_tokens=lambda event: None, # event is defined in _obtain_token(...)
293+
on_removing_rt=lambda token_item: None,
294+
on_updating_rt=lambda token_item, new_rt: None,
295+
**kwargs):
296+
super(Client, self).__init__(client_id, **kwargs)
297+
self.on_obtaining_tokens = on_obtaining_tokens
298+
self.on_removing_rt = on_removing_rt
299+
self.on_updating_rt = on_updating_rt
300+
301+
def _obtain_token(self, grant_type, params=None, data=None, *args, **kwargs):
302+
resp = super(Client, self)._obtain_token(
303+
grant_type, params, data, *args, **kwargs)
304+
if "error" not in resp:
305+
_resp = resp.copy()
306+
if grant_type == "refresh_token" and "refresh_token" in _resp:
307+
_resp.pop("refresh_token") # We'll handle this in its own method
308+
if "scope" in _resp:
309+
scope = _resp["scope"].split() # It is conceptually a set,
310+
# but we represent it as a list which can be persisted to JSON
311+
else:
312+
# TODO: Deal with absent scope in authorization grant
313+
scope = data.get("scope")
314+
self.on_obtaining_tokens({
315+
"client_id": self.client_id,
316+
"scope": scope,
317+
"token_endpoint": self.configuration["token_endpoint"],
318+
"grant_type": grant_type, # can be used to know an IdToken-less
319+
# response is for an app or for a user
320+
"response": _resp, "params": params, "data": data,
321+
})
322+
return resp
323+
324+
def obtain_token_with_refresh_token(self, token_item, scope=None,
325+
rt_getter=lambda token_item: token_item["refresh_token"],
326+
**kwargs):
327+
# type: (Union[str, dict], Union[str, list, set, tuple], Callable) -> dict
328+
"""This is an "overload" which accepts a refresh token item as a dict,
329+
therefore this method can relay refresh_token item to event listeners.
330+
331+
:param refresh_token_item: A refresh token item came from storage
332+
:param scope: If omitted, is treated as equal to the scope originally
333+
granted by the resource ownser,
334+
according to https://tools.ietf.org/html/rfc6749#section-6
335+
:param rt_getter: A callable used to extract the RT from token_item
336+
"""
337+
if isinstance(token_item, str):
338+
# Satisfy the L of SOLID, although we expect caller uses a dict
339+
return super(Client, self).obtain_token_with_refresh_token(
340+
token_item, scope=scope, **kwargs)
341+
if isinstance(token_item, dict):
342+
resp = super(Client, self).obtain_token_with_refresh_token(
343+
rt_getter(token_item), scope=scope, **kwargs)
344+
if resp.get('error') == 'invalid_grant':
345+
self.on_removing_rt(token_item) # Discard old RT
346+
if 'refresh_token' in resp:
347+
self.on_updating_rt(token_item, resp['refresh_token'])
348+
return resp
349+
raise ValueError("token_item should not be a type %s" % type(token_item))
350+

0 commit comments

Comments
 (0)