Skip to content
This repository was archived by the owner on Jun 1, 2023. It is now read-only.

Commit 7bce59c

Browse files
committed
Extended add_callbacks to deal with more callback uris.
1 parent bdaffb6 commit 7bce59c

4 files changed

Lines changed: 59 additions & 30 deletions

File tree

src/oidcrp/rp_handler.py

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,30 @@ def do_provider_info(self, client=None, state='', behaviour_args=None):
237237
except KeyError:
238238
return _context.get('issuer')
239239

240+
def add_callbacks(self, context):
241+
_iss = context.get('issuer')
242+
# Create the necessary callback URLs
243+
# as a side effect self.hash2issuer is set
244+
_extra_uris = {
245+
"request_uri": False,
246+
"backchannel_logout_uri": False,
247+
"frontchannel_logout_uri": False
248+
}
249+
_pi = context.get('provider_info')
250+
_cp = context.config.get("client_preferences")
251+
if 'require_request_uri_registration' in _pi and "request_uri_usable" in _cp:
252+
_extra_uris['request_uri'] = True
253+
if 'frontchannel_logout_supported' in _pi and "frontchannel_logout_usable" in _cp:
254+
_extra_uris["frontchannel_logout_uri"] = True
255+
if 'backchannel_logout_supported' in _pi and "backchannel_logout_usable" in _cp:
256+
_extra_uris["backchannel_logout_uri"] = True
257+
258+
callbacks = self.create_callbacks(_iss, **_extra_uris)
259+
260+
context.set('redirect_uris', [
261+
v for k, v in callbacks.items() if not k.startswith('__')])
262+
context.set('callback', callbacks)
263+
240264
def do_client_registration(self, client=None,
241265
iss_id: Optional[str] = '',
242266
state: Optional[str] = '',
@@ -266,16 +290,7 @@ def do_client_registration(self, client=None,
266290

267291
if not _context.client_id: # means I have to do dynamic client registration
268292
if not _context.get('redirect_uris'):
269-
# Create the necessary callback URLs
270-
# as a side effect self.hash2issuer is set
271-
if 'require_request_uri_registration' in _context.get('provider_info'):
272-
callbacks = self.create_callbacks(_iss, request_uri=True)
273-
else:
274-
callbacks = self.create_callbacks(_iss)
275-
276-
_context.set('redirect_uris', [
277-
v for k, v in callbacks.items() if not k.startswith('__')])
278-
_context.set('callback', callbacks)
293+
self.add_callbacks(_context)
279294

280295
if behaviour_args:
281296
_params = RegistrationRequest().parameters()
@@ -285,19 +300,6 @@ def do_client_registration(self, client=None,
285300

286301
load_registration_response(client, request_args=request_args)
287302

288-
def add_callbacks(self, service_context):
289-
_iss = service_context.get('issuer')
290-
291-
if 'require_request_uri_registration' in service_context.get('provider_info'):
292-
_callbacks = self.create_callbacks(_iss, request_uri=True)
293-
else:
294-
_callbacks = self.create_callbacks(_iss)
295-
296-
service_context.set('redirect_uris', [
297-
v for k, v in _callbacks.items() if not k.startswith('__')])
298-
service_context.set('callback', _callbacks)
299-
return _callbacks
300-
301303
def do_webfinger(self, user):
302304
"""
303305
Does OpenID Provider Issuer discovery using webfinger.
@@ -356,12 +358,16 @@ def client_setup(self, iss_id='', user='', behaviour_args=None):
356358
self.issuer2rp[issuer] = client
357359
return client
358360

359-
def create_callbacks(self, issuer, request_uri=False):
361+
def create_callbacks(self, issuer, request_uri=False, backchannel_logout_uri=False,
362+
frontchannel_logout_uri=False):
360363
"""
361364
To mitigate some security issues the redirect_uris should be OP/AS
362365
specific. This method creates a set of redirect_uris unique to the
363366
OP/AS.
364367
368+
:param frontchannel_logout_uri: Whether a front-channel logout uri should be constructed
369+
:param backchannel_logout_uri: Whether a back-channel logout uri should be constructed
370+
:param request_uri: Whether a request_uri should be constructed
365371
:param issuer: Issuer ID
366372
:return: A set of redirect_uris
367373
"""
@@ -379,6 +385,15 @@ def create_callbacks(self, issuer, request_uri=False):
379385
if request_uri:
380386
res["request_uri"] = f"{self.base_url}/req_uri/{_hex}"
381387

388+
if backchannel_logout_uri or frontchannel_logout_uri:
389+
res["post_logout_redirect_uris"] = f"{self.base_url}/session_logout/{_hex}"
390+
391+
if backchannel_logout_uri:
392+
res["backchannel_logout_uri"] = f"{self.base_url}/bc_logout/{_hex}"
393+
if frontchannel_logout_uri:
394+
res["frontchannel_logout_uri"] = f"{self.base_url}/fc_logout/{_hex}"
395+
396+
logger.debug(f"Created callback URIs: {res}")
382397
return res
383398

384399
def _get_response_type(self, context, req_args: Optional[dict] = None):
@@ -678,7 +693,7 @@ def finalize_auth(self, client, issuer: str, response: dict,
678693

679694
def get_access_and_id_token(self, authorization_response=None,
680695
state: Optional[str] = '',
681-
client: Optional[object] =None,
696+
client: Optional[object] = None,
682697
behaviour_args: Optional[dict] = None):
683698
"""
684699
There are a number of services where access tokens and ID tokens can
@@ -830,7 +845,8 @@ def finalize(self, issuer, response, behaviour_args: Optional[dict] = None):
830845
'userinfo': inforesp,
831846
'state': authorization_response['state'],
832847
'token': token['access_token'],
833-
'id_token': _id_token
848+
'id_token': _id_token,
849+
'session_state': authorization_response.get('session_state', '')
834850
}
835851

836852
def has_active_authentication(self, state):
@@ -898,7 +914,9 @@ def get_valid_access_token(self, state):
898914
else:
899915
raise OidcServiceError('No valid access token')
900916

901-
def logout(self, state, client=None, post_logout_redirect_uri=''):
917+
def logout(self, state: str,
918+
client: Optional[Client] = None,
919+
post_logout_redirect_uri: Optional[str] = '') -> dict:
902920
"""
903921
Does a RP initiated logout from an OP. After logout the user will be
904922
redirect by the OP to a URL of choice (post_logout_redirect_uri).
@@ -929,6 +947,17 @@ def logout(self, state, client=None, post_logout_redirect_uri=''):
929947

930948
return resp
931949

950+
def close(self, state: str,
951+
issuer: Optional[str] = '',
952+
post_logout_redirect_uri: Optional[str] = '') -> dict:
953+
if issuer:
954+
client = self.issuer2rp[issuer]
955+
else:
956+
client = self.get_client_from_session_key(state)
957+
958+
return self.logout(state=state, client=client,
959+
post_logout_redirect_uri=post_logout_redirect_uri)
960+
932961
def clear_session(self, state):
933962
client = self.get_client_from_session_key(state)
934963
client.client_get("service_context").state.remove_state(state)

src/oidcrp/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def get_headers(self,
344344
return _headers
345345

346346
def get_request_parameters(self, request_args=None, method="",
347-
request_body_type="", authn_method='', **kwargs):
347+
request_body_type="", authn_method='', **kwargs) -> dict:
348348
"""
349349
Builds the request message and constructs the HTTP headers.
350350

tests/pub_client.jwks

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"keys": [{"kty": "RSA", "use": "sig", "kid": "SUswNi1MRFlDT0Y2YjU1Z1RfQlo2S3dEa3FTTkV3LThFcnhDTHF5elk2VQ", "e": "AQAB", "n": "0UkUx2ewKyc-XJ1o0ToyGjws_JybAMZj2oYjsPyyvQ_T5dhZ2VmRRRkhsaVJ2xE_GGc7mSG0IjmGFyXp5y0w4mJBcsAEE5-8eBTvQdYIryjW74r3jt6Fi4Hlm1yFMTie3apv8mw79BUj-jT0kh3_m-FiKKUvLsq45DcLtTJ4cx7Ize37dl1sFSpQcoYMk7eiUEM8fiNboiVwvBYNAWVMkUM-LnVUPm3UjvKp0LihYEkZFWOxmuQmj2x25SFUkjus38ERrRqJQBZduxdBHFrWtWg8yOA53BkMU0FFg_r0H3ctl-5GaKw-BWlogU4qXnsq85xy0EoenRk7FPV8g_ulJw"}, {"kty": "EC", "use": "sig", "kid": "NC1pdGRQN002bWM3bk1xX2R0SktscElqbFdtN29ITDV2WVd2b0hOYzREVQ", "crv": "P-256", "x": "kK7Qp1woSerI7rUOAwW_4sU6ZmwV3wwXKX3VU-v2fMI", "y": "iPWd_Pjq6EjxYy08KNFZ3PxhEwgWHgAQTTknlKMKJA0"}]}
1+
{"keys": [{"kty": "RSA", "use": "sig", "kid": "SUswNi1MRFlDT0Y2YjU1Z1RfQlo2S3dEa3FTTkV3LThFcnhDTHF5elk2VQ", "n": "0UkUx2ewKyc-XJ1o0ToyGjws_JybAMZj2oYjsPyyvQ_T5dhZ2VmRRRkhsaVJ2xE_GGc7mSG0IjmGFyXp5y0w4mJBcsAEE5-8eBTvQdYIryjW74r3jt6Fi4Hlm1yFMTie3apv8mw79BUj-jT0kh3_m-FiKKUvLsq45DcLtTJ4cx7Ize37dl1sFSpQcoYMk7eiUEM8fiNboiVwvBYNAWVMkUM-LnVUPm3UjvKp0LihYEkZFWOxmuQmj2x25SFUkjus38ERrRqJQBZduxdBHFrWtWg8yOA53BkMU0FFg_r0H3ctl-5GaKw-BWlogU4qXnsq85xy0EoenRk7FPV8g_ulJw", "e": "AQAB"}, {"kty": "EC", "use": "sig", "kid": "NC1pdGRQN002bWM3bk1xX2R0SktscElqbFdtN29ITDV2WVd2b0hOYzREVQ", "crv": "P-256", "x": "kK7Qp1woSerI7rUOAwW_4sU6ZmwV3wwXKX3VU-v2fMI", "y": "iPWd_Pjq6EjxYy08KNFZ3PxhEwgWHgAQTTknlKMKJA0"}]}

tests/test_20_rp_handler_oidc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ def test_finalize(self):
898898
# assume code flow
899899
resp = self.rph.finalize(_session['iss'], auth_response.to_dict())
900900

901-
assert set(resp.keys()) == {'userinfo', 'state', 'token', 'id_token'}
901+
assert set(resp.keys()) == {'userinfo', 'state', 'token', 'id_token', 'session_state'}
902902

903903
def test_dynamic_setup(self):
904904
user_id = 'acct:foobar@example.com'

0 commit comments

Comments
 (0)