@@ -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 =
293+ lambda client_id = None , scope = None , token_endpoint = None ,
294+ response = None , params = None , data = None , ** kwargs :
295+ None ,
296+ on_removing_rt = lambda token_item : None ,
297+ on_updating_rt = lambda token_item , new_rt : None ,
298+ ** kwargs ):
299+ super (Client , self ).__init__ (client_id , ** kwargs )
300+ self .on_obtaining_tokens = on_obtaining_tokens
301+ self .on_removing_rt = on_removing_rt
302+ self .on_updating_rt = on_updating_rt
303+
304+ def _obtain_token (self , grant_type , params = None , data = None , * args , ** kwargs ):
305+ resp = super (Client , self )._obtain_token (
306+ grant_type , params , data , * args , ** kwargs )
307+ if "error" not in resp :
308+ _resp = resp .copy ()
309+ if grant_type == "refresh_token" and "refresh_token" in _resp :
310+ _resp .pop ("refresh_token" ) # We'll handle this in its own method
311+ if "scope" in _resp :
312+ scope = _resp ["scope" ].split () # It is conceptually a set,
313+ # but we represent it as a list which can be persisted to JSON
314+ else :
315+ # TODO: Deal with absent scope in authorization grant
316+ scope = data .get ("scope" )
317+ self .on_obtaining_tokens (
318+ client_id = self .client_id ,
319+ scope = scope ,
320+ token_endpoint = self .configuration ["token_endpoint" ],
321+ response = _resp , params = params , data = data )
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