1+ import hashlib
12import logging
3+ from typing import List
4+ from typing import Optional
25
6+ from cryptojwt .utils import as_bytes
37from oidcmsg import oidc
48from oidcmsg .oauth2 import ResponseMessage
59
@@ -37,19 +41,133 @@ def response_types_to_grant_types(response_types):
3741 return list (_res )
3842
3943
40- def add_request_uri (request_args = None , service = None , ** kwargs ):
41- _context = service .client_get ("service_context" )
42- if _context .requests_dir :
43- _pi = _context .provider_info
44- if _pi :
45- _req = _pi .get ('require_request_uri_registration' , False )
46- if _req is True :
47- request_args ['request_uris' ] = _context .generate_request_uris (_context .requests_dir )
44+ def create_callbacks (issuer : str ,
45+ hash_seed : str ,
46+ base_url : str ,
47+ code : Optional [bool ] = False ,
48+ implicit : Optional [bool ] = False ,
49+ form_post : Optional [bool ] = False ,
50+ request_uris : Optional [bool ] = False ,
51+ backchannel_logout_uri : Optional [bool ] = False ,
52+ frontchannel_logout_uri : Optional [bool ] = False ):
53+ """
54+ To mitigate some security issues the redirect_uris should be OP/AS
55+ specific. This method creates a set of redirect_uris unique to the
56+ OP/AS.
57+
58+ :param frontchannel_logout_uri: Whether a front-channel logout uri should be constructed
59+ :param backchannel_logout_uri: Whether a back-channel logout uri should be constructed
60+ :param request_uri: Whether a request_uri should be constructed
61+ :param issuer: Issuer ID
62+ :return: A set of redirect_uris
63+ """
64+ _hash = hashlib .sha256 ()
65+ _hash .update (hash_seed )
66+ _hash .update (as_bytes (issuer ))
67+ _hex = _hash .hexdigest ()
68+
69+ res = {'__hex' : _hex }
70+
71+ if code :
72+ res ['code' ] = f"{ base_url } /authz_cb/{ _hex } "
73+
74+ if implicit :
75+ res ['implicit' ] = f"{ base_url } /authz_im_cb/{ _hex } "
76+
77+ if form_post :
78+ res ['form_post' ] = f"{ base_url } /authz_fp_cb/{ _hex } "
79+
80+ if request_uris :
81+ res ["request_uris" ] = f"{ base_url } /req_uri/{ _hex } "
82+
83+ if backchannel_logout_uri or frontchannel_logout_uri :
84+ res ["post_logout_redirect_uris" ] = f"{ base_url } /session_logout/{ _hex } "
85+
86+ if backchannel_logout_uri :
87+ res ["backchannel_logout_uri" ] = f"{ base_url } /bc_logout/{ _hex } "
88+
89+ if frontchannel_logout_uri :
90+ res ["frontchannel_logout_uri" ] = f"{ base_url } /fc_logout/{ _hex } "
91+
92+ logger .debug (f"Created callback URIs: { res } " )
93+ return res
94+
95+
96+ def _cmp (a , b ):
97+ if b is None : # Don't care about the value as long as there is one
98+ return True
99+ elif isinstance (a , str ) and a == b :
100+ return True
101+ elif isinstance (a , list ) and b in a :
102+ return True
103+
104+ return a == b
48105
49- return request_args , {}
50106
107+ def _in_config_or_client_preferences (config , attr , val ):
108+ _val = config .get ("client_preferences" , {}).get (attr )
109+ if _cmp (_val , val ):
110+ return True
111+ _val = config .get (attr )
112+ return _cmp (_val , val )
51113
52- def add_post_logout_redirect_uris (request_args = None , service = None , ** kwargs ):
114+
115+ def add_callbacks (context , ignore : Optional [List [str ]] = None ):
116+ if ignore is None :
117+ ignore = []
118+ _iss = context .get ('issuer' )
119+
120+ _uris = {}
121+
122+ _pi = context .get ('provider_info' )
123+ _cp = context .config .get ("client_preferences" )
124+
125+ if "redirect_uris" not in ignore :
126+ # code and/or implicit
127+ if _in_config_or_client_preferences (context .config , "response_types" , "code" ):
128+ _uris ['code' ] = True
129+ for rt in ["id_token" , "id_token token" , "code id_token token" , "code idtoken" ,
130+ "code token" ]:
131+ if _in_config_or_client_preferences (context .config , "response_types" , rt ):
132+ _uris ["implicit" ] = True
133+ break
134+
135+ if "form_post" not in ignore :
136+ if _in_config_or_client_preferences (context .config , "form_post_usable" , True ):
137+ _uris ["form_post" ] = True
138+
139+ if "request_uris" not in ignore :
140+ if 'require_request_uri_registration' in _pi and _in_config_or_client_preferences (
141+ context .config , "request_uri_usable" , True ):
142+ _uris ['request_uris' ] = True
143+
144+ if "frontchannel_logout_uri" not in ignore :
145+ if 'frontchannel_logout_supported' in _pi and _in_config_or_client_preferences (
146+ context .config , "frontchannel_logout_usable" , True ):
147+ _uris ["frontchannel_logout_uri" ] = True
148+
149+ if "backchannel_logout_uri" not in ignore :
150+ if 'backchannel_logout_supported' in _pi and _in_config_or_client_preferences (
151+ context .config , "backchannel_logout_usable" , True ):
152+ _uris ["backchannel_logout_uri" ] = True
153+
154+ callbacks = create_callbacks (_iss ,
155+ hash_seed = context .get ('hash_seed' ),
156+ base_url = context .get ("base_url" ),
157+ ** _uris )
158+ context .hash2issuer [callbacks ['__hex' ]] = _iss
159+
160+ if "redirect_uris" not in ignore :
161+ _redirect_uris = [v for k , v in callbacks .items () if k in ["code" , "implicit" , "form_post" ]]
162+ callbacks ["redirect_uris" ] = _redirect_uris
163+ context .set ('callback' , callbacks )
164+
165+
166+ CALLBACK_URIS = ["post_logout_redirect_uris" , "backchannel_logout_uri" , "frontchannel_logout_uri" ,
167+ "request_uris" , 'redirect_uris' ]
168+
169+
170+ def add_callback_uris (request_args = None , service = None , ** kwargs ):
53171 """
54172
55173 :param request_args:
@@ -59,10 +177,17 @@ def add_post_logout_redirect_uris(request_args=None, service=None, **kwargs):
59177 :return:
60178 """
61179
62- if "post_logout_redirect_uris" not in request_args :
63- _uris = service .client_get ("service_context" ).register_args .get ("post_logout_redirect_uris" )
64- if _uris :
65- request_args ["post_logout_redirect_uris" ] = _uris
180+ _context = service .client_get ("service_context" )
181+ _ignore = [k for k in list (request_args .keys ()) if k in CALLBACK_URIS ]
182+ add_callbacks (_context , ignore = _ignore )
183+ for _key in CALLBACK_URIS :
184+ _req_val = request_args .get (_key )
185+ if not _req_val :
186+ _uri = _context .register_args .get (_key )
187+ if _uri is None :
188+ _uri = _context .callback .get (_key )
189+ if _uri :
190+ request_args [_key ] = _uri
66191
67192 return request_args , {}
68193
@@ -107,8 +232,8 @@ def __init__(self, client_get, client_authn_factory=None, conf=None):
107232 client_authn_factory = client_authn_factory ,
108233 conf = conf )
109234 self .pre_construct = [self .add_client_behaviour_preference ,
110- add_redirect_uris , add_request_uri ,
111- add_post_logout_redirect_uris ,
235+ # add_redirect_uris,
236+ add_callback_uris ,
112237 add_jwks_uri_or_jwks ]
113238 self .post_construct = [self .oidc_post_construct ]
114239
0 commit comments