2424from oidcop .token .exception import UnknownToken
2525from oidcop .exception import UnAuthorizedClientScope , ToOld
2626from oidcop .session .token import AccessToken
27+ from oidcop .authn_event import create_authn_event
2728
2829logger = logging .getLogger (__name__ )
2930
@@ -207,7 +208,7 @@ def post_parse_request(
207208
208209
209210class RefreshTokenHelper (TokenEndpointHelper ):
210- def process_request (self , req : Union [Message , dict ], ** kwargs ):
211+ def process_request (self , req : Union [Message , dict ], ** kwargs ):
211212 _context = self .endpoint .server_get ("endpoint_context" )
212213 _mngr = _context .session_manager
213214
@@ -216,6 +217,8 @@ def process_request(self, req: Union[Message, dict], **kwargs):
216217
217218 token_value = req ["refresh_token" ]
218219 _session_info = _mngr .get_session_info_by_token (token_value , grant = True )
220+ grant = _session_info ["grant" ]
221+ audience = grant .authorization_request .get ("audience" , {})
219222 if _session_info ["client_id" ] != req ["client_id" ]:
220223 logger .debug ("{} owner of token" .format (_session_info ["client_id" ]))
221224 logger .warning ("{} using token it was not given" .format (req ["client_id" ]))
@@ -377,7 +380,7 @@ def __init__(self, endpoint, config=None):
377380 "urn:ietf:params:oauth:token-type:access_token" ,
378381 "urn:ietf:params:oauth:token-type:jwt" ,
379382 # "urn:ietf:params:oauth:token-type:id_token",
380- # "urn:ietf:params:oauth:token-type:refresh_token",
383+ "urn:ietf:params:oauth:token-type:refresh_token" ,
381384 ]
382385
383386 def post_parse_request (self , request , client_id = "" , ** kwargs ):
@@ -423,7 +426,7 @@ def post_parse_request(self, request, client_id="", **kwargs):
423426
424427 token = _mngr .find_token (_session_info ["session_id" ], request ["subject_token" ])
425428
426- if not isinstance (token , AccessToken ):
429+ if not isinstance (token , ( AccessToken , RefreshToken ) ):
427430 return self .error_cls (
428431 error = "invalid_request" , error_description = "Wrong token type"
429432 )
@@ -436,6 +439,14 @@ def post_parse_request(self, request, client_id="", **kwargs):
436439
437440 def check_for_errors (self , request ):
438441 _context = self .endpoint .server_get ("endpoint_context" )
442+
443+ #TODO: also check if the (valid) subject_token matches subject_token_type
444+ if request ["subject_token_type" ] not in self .token_types_allowed :
445+ return TokenErrorResponse (
446+ error = "invalid_request" ,
447+ error_description = "Unsupported subject token type" ,
448+ )
449+
439450 if "resource" in request :
440451 iss = urlparse (_context .issuer )
441452 if any (
@@ -446,9 +457,13 @@ def check_for_errors(self, request):
446457 )
447458
448459 if "audience" in request :
449- if any (
450- aud != _context .issuer for aud in request ["audience" ]
451- ):
460+ if request ["subject_token_type" ] == "urn:ietf:params:oauth:token-type:refresh_token" :
461+ return TokenErrorResponse (
462+ error = "invalid_target" , error_description = "Refresh token has single owner"
463+ )
464+ _token_usage_rules = _context .authz .usage_rules (request ["client_id" ])
465+ audience = _token_usage_rules ["refresh_token" ].get ("audience" , {})
466+ if (not len (set (request ["audience" ]).intersection (set (audience )))):
452467 return TokenErrorResponse (
453468 error = "invalid_target" , error_description = "Unknown audience"
454469 )
@@ -468,21 +483,18 @@ def check_for_errors(self, request):
468483 error = "invalid_request" , error_description = "Actor token not supported"
469484 )
470485
471- # TODO: also check if the (valid) subject_token matches subject_token_type
472- if request ["subject_token_type" ] not in self .token_types_allowed :
473- return TokenErrorResponse (
474- error = "invalid_request" ,
475- error_description = "Unsupported subject token type" ,
476- )
477-
478- def token_exchange_response (self , token ):
479- response_args = {
480- "issued_token_type" : "urn:ietf:params:oauth:token-type:access_token" ,
481- "token_type" : token .token_type ,
482- "access_token" : token .value ,
483- "scope" : token .scope ,
484- "expires_in" : token .usage_rules ["expires_in" ]
485- }
486+ def token_exchange_response (self , access_token , refresh_token = None ):
487+ response_args = {}
488+ response_args ["access_token" ] = access_token .value
489+ response_args ["scope" ] = access_token .scope
490+ if refresh_token is None :
491+ response_args ["issued_token_type" ] = "urn:ietf:params:oauth:token-type:access_token"
492+ response_args ["token_type" ] = access_token .token_type
493+ response_args ["expires_in" ] = access_token .usage_rules ["expires_in" ]
494+ else :
495+ response_args ["issued_token_type" ] = "urn:ietf:params:oauth:token-type:refresh_token"
496+ response_args ["refresh_token" ] = refresh_token .value
497+ response_args ["expires_in" ] = refresh_token .usage_rules ["expires_in" ]
486498 return TokenExchangeResponse (** response_args )
487499
488500 def process_request (self , request , ** kwargs ):
@@ -516,7 +528,7 @@ def process_request(self, request, **kwargs):
516528
517529 token = _mngr .find_token (_session_info ["session_id" ], request ["subject_token" ])
518530
519- if not isinstance (token , AccessToken ):
531+ if not isinstance (token , ( AccessToken , RefreshToken ) ):
520532 return self .error_cls (
521533 error = "invalid_request" , error_description = "Wrong token type"
522534 )
@@ -538,27 +550,96 @@ def process_request(self, request, **kwargs):
538550 error_description = "Unauthorized scope requested" ,
539551 )
540552
541- try :
542- new_token = self ._mint_token (
543- token_class = token .token_class ,
544- grant = grant ,
545- session_id = _session_info ["session_id" ],
553+ _requested_token_type = request .get ("requested_token_type" ,
554+ "urn:ietf:params:oauth:token-type:access_token" )
555+ if (
556+ _requested_token_type == "urn:ietf:params:oauth:token-type:access_token"
557+ or _requested_token_type == "urn:ietf:params:oauth:token-type:id_token"
558+ ):
559+ _token_class = _requested_token_type .split (":" )[- 1 ]
560+ _token_type = token .token_type
561+
562+ try :
563+ new_token = self ._mint_token (
564+ token_class = _token_class ,
565+ grant = grant ,
566+ session_id = _session_info ["session_id" ],
567+ client_id = request ["client_id" ],
568+ based_on = token ,
569+ scope = request .get ("scope" ),
570+ token_args = {
571+ "resources" :request .get ("resource" ),
572+ },
573+ token_type = _token_type
574+ )
575+ except MintingNotAllowed :
576+ logger .error (f"Minting not allowed for { _token_class } " )
577+ return self .error_cls (
578+ error = "invalid_grant" ,
579+ error_description = "Token Exchange not allowed with that token" ,
580+ )
581+
582+ return self .token_exchange_response (access_token = new_token )
583+
584+ elif _requested_token_type == "urn:ietf:params:oauth:token-type:refresh_token" :
585+ _token_class = "refresh_token"
586+ _token_type = None
587+ authn_event = create_authn_event (_session_info ["user_id" ])
588+ _token_usage_rules = _context .authz .usage_rules (request ["client_id" ])
589+ _exp_in = _token_usage_rules ["refresh_token" ].get ("expires_in" )
590+ if _exp_in and "valid_until" in authn_event :
591+ authn_event ["valid_until" ] = utc_time_sans_frac () + _exp_in
592+
593+ sid = _mngr .create_session (
594+ authn_event = authn_event ,
595+ auth_req = request ,
596+ user_id = _session_info ["user_id" ],
546597 client_id = request ["client_id" ],
547- based_on = token ,
548- scope = request .get ("scope" ),
549- token_args = {
550- "resources" :request .get ("resource" ),
551- },
552- token_type = token .token_type
553- )
554- except MintingNotAllowed :
555- logger .error ("Minting not allowed for 'access_token'" )
556- return self .error_cls (
557- error = "invalid_grant" ,
558- error_description = "Token Exchange not allowed with that token" ,
598+ token_usage_rules = _token_usage_rules ,
559599 )
560600
561- return self .token_exchange_response (token = new_token )
601+ try :
602+ new_token = self ._mint_token (
603+ token_class = "access_token" ,
604+ grant = _mngr .get_grant (sid ),
605+ session_id = sid ,
606+ client_id = request ["client_id" ],
607+ based_on = token ,
608+ scope = request .get ("scope" ),
609+ token_args = {
610+ "resources" :request .get ("resource" ),
611+ },
612+ token_type = _token_type
613+ )
614+ except MintingNotAllowed :
615+ logger .error ("Minting not allowed for 'access_token'" )
616+ return self .error_cls (
617+ error = "invalid_grant" ,
618+ error_description = "Token Exchange not allowed with that token" ,
619+ )
620+
621+ try :
622+ refresh_token = self ._mint_token (
623+ token_class = _token_class ,
624+ grant = _mngr .get_grant (sid ),
625+ session_id = sid ,
626+ client_id = request ["client_id" ],
627+ based_on = token ,
628+ scope = request .get ("scope" ),
629+ token_args = {
630+ "resources" :request .get ("resource" ),
631+ },
632+ token_type = _token_type
633+ )
634+ except MintingNotAllowed :
635+ logger .error ("Minting not allowed for 'refresh_token'" )
636+ return self .error_cls (
637+ error = "invalid_grant" ,
638+ error_description = "Token Exchange not allowed with that token" ,
639+ )
640+
641+ return self .token_exchange_response (access_token = new_token ,
642+ refresh_token = refresh_token )
562643
563644class Token (oauth2 .token .Token ):
564645 request_cls = Message
0 commit comments