def test_end_session_endpoint_with_wrong_post_logout_redirect_uri(self): self._code_auth("1234567") self._code_auth2("abcdefg") id_token = self._auth_with_id_token("1234567") _sdb = self.session_endpoint.endpoint_context.sdb _sid = self._get_sid() cookie = self._create_cookie("diana", _sid, "1234567", "client_1") post_logout_redirect_uri = "https://demo.example.com/log_out" msg = Message(id_token=id_token) verify_id_token(msg, keyjar=self.session_endpoint.endpoint_context.keyjar) with pytest.raises(RedirectURIError): self.session_endpoint.process_request( { "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde", "id_token_hint": id_token, verified_claim_name("id_token_hint"): msg[ verified_claim_name("id_token") ], }, cookie=cookie, )
def test_end_session_endpoint_with_wrong_post_logout_redirect_uri(self): _resp = self._code_auth("1234567") self._code_auth2("abcdefg") resp_args, _session_id = self._auth_with_id_token("1234567") id_token = resp_args["id_token"] cookie = self._create_cookie(_session_id) http_info = {"cookie": [cookie]} post_logout_redirect_uri = "https://demo.example.com/log_out" msg = Message(id_token=id_token) verify_id_token( msg, keyjar=self.session_endpoint.server_get("endpoint_context").keyjar) with pytest.raises(RedirectURIError): self.session_endpoint.process_request( { "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde", "id_token_hint": id_token, verified_claim_name("id_token_hint"): msg[verified_claim_name("id_token")], }, http_info=http_info, )
def test_end_session_endpoint_with_cookie_wrong_user(self): # Need cookie and ID Token to figure this out id_token = self._auth_with_id_token("1234567") cookie = self._create_cookie("diggins", "_sid_", "1234567", "client_1") msg = Message(id_token=id_token) verify_id_token(msg, keyjar=self.session_endpoint.endpoint_context.keyjar) msg2 = Message(id_token_hint=id_token) msg2[verified_claim_name("id_token_hint")] = msg[ verified_claim_name("id_token") ] with pytest.raises(ValueError): self.session_endpoint.process_request(msg2, cookie=cookie)
def verify(self, request, **kwargs): _jwt = JWT(self.endpoint_context.keyjar) try: ca_jwt = _jwt.unpack(request["client_assertion"]) except (Invalid, MissingKey, BadSignature) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") try: logger.debug("authntoken: %s" % sanitize(ca_jwt.to_dict())) except AttributeError: logger.debug("authntoken: %s" % sanitize(ca_jwt)) request[verified_claim_name("client_assertion")] = ca_jwt try: client_id = kwargs["client_id"] except KeyError: client_id = ca_jwt["iss"] # I should be among the audience # could be either my issuer id or the token endpoint if self.endpoint_context.issuer in ca_jwt["aud"]: pass elif self.endpoint_context.endpoint["token"].full_path in ca_jwt[ "aud"]: pass else: raise NotForMe("Not for me!") return {"client_id": client_id, "jwt": ca_jwt}
def test_id_token_nonce_match(self): self.service.store_nonce2state('nonce', 'state') resp = AccessTokenResponse() resp[verified_claim_name('id_token')] = {'nonce': 'nonce'} self.service.store_nonce2state('nonce2', 'state2') with pytest.raises(ParameterError): self.service.update_service_context(resp, key='state2')
def max_age(request): try: return request[verified_claim_name("request")]["max_age"] except KeyError: try: return request["max_age"] except KeyError: return 0
def backchannel_logout(client, request='', request_args=None): """ :param request: URL encoded logout request :return: """ if request: req = BackChannelLogoutRequest().from_urlencoded(as_unicode(request)) else: req = BackChannelLogoutRequest(**request_args) kwargs = { 'aud': client.service_context.get('client_id'), 'iss': client.service_context.get('issuer'), 'keyjar': client.service_context.keyjar, 'allowed_sign_alg': client.service_context.get('registration_response').get( "id_token_signed_response_alg", "RS256") } try: req.verify(**kwargs) except (MessageException, ValueError, NotForMe) as err: raise MessageException('Bogus logout request: {}'.format(err)) # Find the subject through 'sid' or 'sub' try: sub = req[verified_claim_name('logout_token')]['sub'] except KeyError: try: sid = req[verified_claim_name('logout_token')]['sid'] except KeyError: raise MessageException('Neither "sid" nor "sub"') else: _state = client.session_interface.get_state_by_sid(sid) else: _state = client.session_interface.get_state_by_sub(sub) return _state
def _do_request_uri(self, request, client_id, endpoint_context, **kwargs): _request_uri = request.get("request_uri") if _request_uri: # Do I do pushed authorization requests ? if "pushed_authorization" in endpoint_context.endpoint: # Is it a UUID urn if _request_uri.startswith("urn:uuid:"): _req = endpoint_context.par_db.get(_request_uri) if _req: del endpoint_context.par_db[_request_uri] # One time # usage return _req else: raise ValueError("Got a request_uri I can not resolve") # Do I support request_uri ? _supported = endpoint_context.provider_info.get( "request_uri_parameter_supported", True) _registered = endpoint_context.cdb[client_id].get("request_uris") # Not registered should be handled else where if _registered: # Before matching remove a possible fragment _p = _request_uri.split("#") if _p[0] not in _registered: raise ValueError("A request_uri outside the registered") # Fetch the request _resp = endpoint_context.httpc.get(_request_uri, **endpoint_context.httpc_params) if _resp.status_code == 200: args = {"keyjar": endpoint_context.keyjar} request = AuthorizationRequest().from_jwt(_resp.text, **args) self.allowed_request_algorithms( client_id, endpoint_context, request.jws_header.get("alg", "RS256"), "sign", ) if request.jwe_header is not None: self.allowed_request_algorithms( client_id, endpoint_context, request.jws_header.get("alg"), "enc_alg", ) self.allowed_request_algorithms( client_id, endpoint_context, request.jws_header.get("enc"), "enc_enc", ) request[verified_claim_name("request")] = request else: raise ServiceError("Got a %s response", _resp.status) return request
def test_end_session_endpoint_with_cookie_id_token_and_unknown_sid(self): # Need cookie and ID Token to figure this out resp_args, _session_id = self._auth_with_id_token("1234567") id_token = resp_args["id_token"] _uid, _cid, _gid = self.session_manager.decrypt_session_id(_session_id) cookie = self._create_cookie( self.session_manager.session_key(_uid, "client_66", _gid)) http_info = {"cookie": [cookie]} msg = Message(id_token=id_token) verify_id_token( msg, keyjar=self.session_endpoint.server_get("endpoint_context").keyjar) msg2 = Message(id_token_hint=id_token) msg2[verified_claim_name("id_token_hint")] = msg[verified_claim_name( "id_token")] with pytest.raises(ValueError): self.session_endpoint.process_request(msg2, http_info=http_info)
def pick_auth(endpoint_context, areq, all=False): """ Pick authentication method :param areq: AuthorizationRequest instance :return: A dictionary with the authentication method and its authn class ref """ acrs = [] try: if len(endpoint_context.authn_broker) == 1: return endpoint_context.authn_broker.default() if "acr_values" in areq: if not isinstance(areq["acr_values"], list): areq["acr_values"] = [areq["acr_values"]] acrs = areq["acr_values"] else: # same as any try: acrs = areq["claims"]["id_token"]["acr"]["values"] except KeyError: try: _ith = areq[verified_claim_name("id_token_hint")] except KeyError: try: _hint = areq["login_hint"] except KeyError: pass else: if endpoint_context.login_hint2acrs: acrs = endpoint_context.login_hint2acrs(_hint) else: try: acrs = [_ith["acr"]] except KeyError: pass if not acrs: return endpoint_context.authn_broker.default() for acr in acrs: res = endpoint_context.authn_broker.pick(acr) logger.debug("Picked AuthN broker for ACR %s: %s" % (str(acr), str(res))) if res: if all: return res else: # Return the first guess by pick. return res[0] except KeyError as exc: logger.debug("An error occurred while picking the authN broker: %s" % str(exc)) return None
def verify(self, request, key_type, **kwargs): _context = self.server_get("endpoint_context") _jwt = JWT(_context.keyjar, msg_cls=JsonWebToken) try: ca_jwt = _jwt.unpack(request["client_assertion"]) except (Invalid, MissingKey, BadSignature) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") _sign_alg = ca_jwt.jws_header.get("alg") if _sign_alg and _sign_alg.startswith("HS"): if key_type == "private_key": raise AttributeError("Wrong key type") keys = _context.keyjar.get("sig", "oct", ca_jwt["iss"], ca_jwt.jws_header.get("kid")) _secret = _context.cdb[ca_jwt["iss"]].get("client_secret") if _secret and keys[0].key != as_bytes(_secret): raise AttributeError( "Oct key used for signing not client_secret") else: if key_type == "client_secret": raise AttributeError("Wrong key type") authtoken = sanitize(ca_jwt.to_dict()) logger.debug("authntoken: {}".format(authtoken)) _endpoint = kwargs.get("endpoint") if _endpoint is None or not _endpoint: if _context.issuer in ca_jwt["aud"]: pass else: raise NotForMe("Not for me!") else: if set(ca_jwt["aud"]).intersection( _endpoint.allowed_target_uris()): pass else: raise NotForMe("Not for me!") # If there is a jti use it to make sure one-time usage is true _jti = ca_jwt.get("jti") if _jti: _key = "{}:{}".format(ca_jwt["iss"], _jti) if _key in _context.jti_db: raise MultipleUsage("Have seen this token once before") else: _context.jti_db[_key] = utc_time_sans_frac() request[verified_claim_name("client_assertion")] = ca_jwt client_id = kwargs.get("client_id") or ca_jwt["iss"] return {"client_id": client_id, "jwt": ca_jwt}
def pick_auth(endpoint_context, areq, pick_all=False): """ Pick authentication method :param areq: AuthorizationRequest instance :return: A dictionary with the authentication method and its authn class ref """ acrs = [] if len(endpoint_context.authn_broker) == 1: return endpoint_context.authn_broker.default() if "acr_values" in areq: if not isinstance(areq["acr_values"], list): areq["acr_values"] = [areq["acr_values"]] acrs = areq["acr_values"] else: try: acrs = areq["claims"]["id_token"]["acr"]["values"] except KeyError: _ith = verified_claim_name("id_token_hint") if areq.get(_ith): _ith = areq[verified_claim_name("id_token_hint")] if _ith.get("acr"): acrs = [_ith["acr"]] else: if areq.get("login_hint") and endpoint_context.login_hint2acrs: acrs = endpoint_context.login_hint2acrs(areq["login_hint"]) if not acrs: return endpoint_context.authn_broker.default() for acr in acrs: res = endpoint_context.authn_broker.pick(acr) logger.debug(f"Picked AuthN broker for ACR {str(acr)}: {str(res)}") if res: return res if pick_all else res[0] return None
def test_example(self): _symkey = KC_SYM_S.get(alg2keytype("HS256")) esreq = EndSessionRequest(id_token_hint=IDTOKEN.to_jwt( key=_symkey, algorithm="HS256", lifetime=300), redirect_url="http://example.org/jqauthz", state="state0") request = EndSessionRequest().from_urlencoded(esreq.to_urlencoded()) keyjar = KeyJar() for _key in _symkey: keyjar.add_symmetric('', _key.key) keyjar.add_symmetric(ISS, _key.key) keyjar.add_symmetric(CLIENT_ID, _key.key) request.verify(keyjar=keyjar) assert isinstance(request, EndSessionRequest) assert set(request.keys()) == { verified_claim_name('id_token_hint'), 'id_token_hint', 'redirect_url', 'state' } assert request["state"] == "state0" assert request[verified_claim_name("id_token_hint")]["aud"] == [ "client_1" ]
def test_id_token_acr(self): _req = AUTH_REQ_DICT.copy() _req["claims"] = { "id_token": {"acr": {"value": "http://www.swamid.se/policy/assurance/al1"}} } _req["response_type"] = "code id_token token" _req["nonce"] = "rnd_nonce" _pr_resp = self.endpoint.parse_request(_req) _resp = self.endpoint.process_request(_pr_resp) res = verify_id_token( _resp["response_args"], keyjar=self.endpoint.endpoint_context.keyjar ) assert res res = _resp["response_args"][verified_claim_name("id_token")] assert res["acr"] == "http://www.swamid.se/policy/assurance/al1"
def post_parse_response(self, response, **kwargs): response = authorization.Authorization.post_parse_response( self, response, **kwargs) _idt = response.get(verified_claim_name('id_token')) if _idt: # If there is a verified ID Token then we have to do nonce # verification. _request = self.get_request_from_response(response) _req_nonce = _request.get('nonce') if _req_nonce: _id_token_nonce = _idt.get('nonce') if not _id_token_nonce: raise MissingRequiredAttribute('nonce') elif _req_nonce != _id_token_nonce: raise ValueError('Invalid nonce') return response
def extend_request_args(self, args, item_cls, item_type, key, parameters, orig=False): """ Add a set of parameters and their value to a set of request arguments. :param args: A dictionary :param item_cls: The :py:class:`oidcmsg.message.Message` subclass that describes the item :param item_type: The type of item, this is one of the parameter names in the :py:class:`oidcservice.state_interface.State` class. :param key: The key to the information in the database :param parameters: A list of parameters who's values this method will return. :param orig: Where the value of a claim is a signed JWT return that. :return: A dictionary with keys from the list of parameters and values being the values of those parameters in the item. If the parameter does not a appear in the item it will not appear in the returned dictionary. """ try: item = self.get_item(item_cls, item_type, key) except KeyError: pass else: for parameter in parameters: if orig: try: args[parameter] = item[parameter] except KeyError: pass else: try: args[parameter] = item[verified_claim_name(parameter)] except KeyError: try: args[parameter] = item[parameter] except KeyError: pass return args
def multiple_extend_request_args(self, args, key, parameters, item_types, orig=False): """ Go through a set of items (by their type) and add the attribute-value that match the list of parameters to the arguments If the same parameter occurs in 2 different items then the value in the later one will be the one used. :param args: Initial set of arguments :param key: Key to the State information in the state database :param parameters: A list of parameters that we're looking for :param item_types: A list of item_type specifying which items we are interested in. :param orig: Where the value of a claim is a signed JWT return that. :return: A possibly augmented set of arguments. """ _state = self.get_state(key) for typ in item_types: try: _item = Message(**_state[typ]) except KeyError: continue for parameter in parameters: if orig: try: args[parameter] = _item[parameter] except KeyError: pass else: try: args[parameter] = _item[verified_claim_name(parameter)] except KeyError: try: args[parameter] = _item[parameter] except KeyError: pass return args
def parse_request(self, request, http_info=None, **kwargs): """ :param request: :param auth: :param kwargs: :return: """ if not request: request = {} # Verify that the client is allowed to do this try: auth_info = self.client_authentication(request, http_info, **kwargs) except UnknownOrNoAuthnMethod: pass else: if not auth_info: pass elif isinstance(auth_info, ResponseMessage): return auth_info else: request["client_id"] = auth_info["client_id"] request["access_token"] = auth_info["token"] if isinstance(request, dict): _context = self.server_get("endpoint_context") request = self.request_cls(**request) if not request.verify(keyjar=_context.keyjar, sigalg=""): raise InvalidRequest("Request didn't verify") # id_token_signing_alg_values_supported try: _ith = request[verified_claim_name("id_token_hint")] except KeyError: pass else: if ( _ith.jws_header["alg"] not in _context.provider_info["id_token_signing_alg_values_supported"] ): raise JWSException("Unsupported signing algorithm") return request
def update_service_context(self, resp, key='', **kwargs): try: _idt = resp[verified_claim_name('id_token')] except KeyError: pass else: try: if self.get_state_by_nonce(_idt['nonce']) != key: raise ParameterError('Someone has messed with "nonce"') except KeyError: raise ValueError('Invalid nonce value') self.store_sub2state(_idt['sub'], key) if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int( resp['expires_in']) self.store_item(resp, 'token_response', key)
def update_service_context(self, resp, key='', **kwargs): try: _idt = resp[verified_claim_name('id_token')] except KeyError: pass else: # If there is a verified ID Token then we have to do nonce # verification try: if self.get_state_by_nonce(_idt['nonce']) != key: raise ParameterError('Someone has messed with "nonce"') except KeyError: raise ValueError('Missing nonce value') self.store_sub2state(_idt['sub'], key) if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int(resp['expires_in']) self.store_item(resp.to_json(), 'auth_response', key)
def verify(self, request, **kwargs): _jwt = JWT(self.endpoint_context.keyjar) try: ca_jwt = _jwt.unpack(request["client_assertion"]) except (Invalid, MissingKey, BadSignature) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") authtoken = sanitize(ca_jwt) if hasattr(ca_jwt, "to_dict") and callable(ca_jwt, "to_dict"): authtoken = sanitize(ca_jwt.to_dict()) logger.debug("authntoken: {}".format(authtoken)) _endpoint = kwargs.get("endpoint") if _endpoint is None or not _endpoint: if self.endpoint_context.issuer in ca_jwt["aud"]: pass else: raise NotForMe("Not for me!") else: if set(ca_jwt["aud"]).intersection( self.endpoint_context.endpoint[_endpoint].allowed_target_uris()): pass else: raise NotForMe("Not for me!") # If there is a jti use it to make sure one-time usage is true _jti = ca_jwt.get('jti') if _jti: _key = "{}:{}".format(ca_jwt['iss'], _jti) if _key in self.endpoint_context.jti_db: raise MultipleUsage("Have seen this token once before") else: self.endpoint_context.jti_db.set(_key, utc_time_sans_frac()) request[verified_claim_name("client_assertion")] = ca_jwt client_id = kwargs.get("client_id") or ca_jwt["iss"] return {"client_id": client_id, "jwt": ca_jwt}
def verify(self, request=None, **kwargs): _context = self.server_get("endpoint_context") _jwt = JWT(_context.keyjar, msg_cls=JsonWebToken) try: _jwt = _jwt.unpack(request["request"]) except (Invalid, MissingKey, BadSignature) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") # If there is a jti use it to make sure one-time usage is true _jti = _jwt.get("jti") if _jti: _key = "{}:{}".format(_jwt["iss"], _jti) if _key in _context.jti_db: raise MultipleUsage("Have seen this token once before") else: _context.jti_db[_key] = utc_time_sans_frac() request[verified_claim_name("client_assertion")] = _jwt client_id = kwargs.get("client_id") or _jwt["iss"] return {"client_id": client_id, "jwt": _jwt}
def parse_request(self, request, auth=None, **kwargs): """ :param request: :param auth: :param kwargs: :return: """ if not request: request = {} # Verify that the client is allowed to do this try: auth_info = self.client_authentication(request, auth, **kwargs) except UnknownOrNoAuthnMethod: pass else: if isinstance(auth_info, ResponseMessage): return auth_info else: request['client_id'] = auth_info['client_id'] request['access_token'] = auth_info['token'] if isinstance(request, dict): request = self.request_cls(**request) if not request.verify(keyjar=self.endpoint_context.keyjar, sigalg=''): raise InvalidRequest("Didn't verify") # id_token_signing_alg_values_supported _ith = request[verified_claim_name("id_token_hint")] if _ith.jws_header['alg'] not in \ self.endpoint_context.provider_info[ 'id_token_signing_alg_values_supported']: raise JWSException('Unsupported signing algorithm') return request
def max_age(request): verified_request = verified_claim_name("request") return request.get(verified_request, {}).get("max_age") or request.get("max_age", 0)
def process_request(self, request=None, cookie=None, **kwargs): """ Perform user logout :param request: :param cookie: :param kwargs: :return: """ _sdb = self.endpoint_context.sdb try: part = self.endpoint_context.cookie_dealer.get_cookie_value( cookie, cookie_name='oidc_op') except IndexError: raise InvalidRequest('Cookie error') if part: # value is a base64 encoded JSON document _cookie_info = json.loads(as_unicode(b64d(as_bytes(part[0])))) _sid = _cookie_info['sid'] else: _sid = '' if 'id_token_hint' in request: _ith_sid = _sdb.sso_db.get_sids_by_sub( request[verified_claim_name("id_token_hint")]['sub'])[0] if _ith_sid != _sid: # someone's messing with me raise ValueError('Wrong ID Token hint') session = _sdb[_sid] client_id = session['authn_req']['client_id'] _cinfo = self.endpoint_context.cdb[client_id] # verify that the post_logout_redirect_uri if present are among the ones # registered try: _url_q = splitquery(request['post_logout_redirect_uri']) except KeyError: pass else: if not _url_q in _cinfo['post_logout_redirect_uris']: raise ValueError('Unregistered post_logout_redirect_uri') # Kill the session _sdb.revoke_session(sid=_sid) if 'post_logout_redirect_uri' in request: _ruri = request["post_logout_redirect_uri"] if 'state' in request: _ruri = '{}?{}'.format(_ruri, urlencode({'state': request['state']})) else: # To my own logout-done page try: _ruri = self.endpoint_context.conf['post_logout_page'] except KeyError: _ruri = self.endpoint_context.issuer return {'response_args': _ruri}
def proposed_user(self, request): try: return request[verified_claim_name('it_token_hint')]['sub'] except KeyError: return ''
def proposed_user(request): try: return request[verified_claim_name("it_token_hint")]["sub"] except KeyError: return ""
def proposed_user(request): cn = verified_claim_name("it_token_hint") if request.get(cn): return request[cn].get("sub", "") return ""
def _do_request_uri(self, request, client_id, endpoint_context, **kwargs): _request_uri = request.get("request_uri") if _request_uri: # Do I do pushed authorization requests ? _endp = self.server_get("endpoint", "pushed_authorization") if _endp: # Is it a UUID urn if _request_uri.startswith("urn:uuid:"): _req = endpoint_context.par_db.get(_request_uri) if _req: # One time usage del endpoint_context.par_db[_request_uri] return _req else: raise ValueError("Got a request_uri I can not resolve") # Do I support request_uri ? if endpoint_context.provider_info.get( "request_uri_parameter_supported", True) is False: raise ServiceError( "Someone is using request_uri which I'm not supporting") _registered = endpoint_context.cdb[client_id].get("request_uris") # Not registered should be handled else where if _registered: # Before matching remove a possible fragment _p = _request_uri.split("#") # ignore registered fragments for now. if _p[0] not in [base for base, qp in _registered]: raise ValueError("A request_uri outside the registered") # Fetch the request _resp = endpoint_context.httpc.get(_request_uri, **endpoint_context.httpc_params) if _resp.status_code == 200: args = {"keyjar": endpoint_context.keyjar, "issuer": client_id} _ver_request = self.request_cls().from_jwt(_resp.text, **args) self.allowed_request_algorithms( client_id, endpoint_context, _ver_request.jws_header.get("alg", "RS256"), "sign", ) if _ver_request.jwe_header is not None: self.allowed_request_algorithms( client_id, endpoint_context, _ver_request.jws_header.get("alg"), "enc_alg", ) self.allowed_request_algorithms( client_id, endpoint_context, _ver_request.jws_header.get("enc"), "enc_enc", ) # The protected info overwrites the non-protected for k, v in _ver_request.items(): request[k] = v request[verified_claim_name("request")] = _ver_request else: raise ServiceError("Got a %s response", _resp.status) return request
def process_request(self, request=None, cookie=None, **kwargs): """ Perform user logout :param request: :param cookie: :param kwargs: :return: """ _cntx = self.endpoint_context _sdb = _cntx.sdb if "post_logout_redirect_uri" in request: if "id_token_hint" not in request: raise InvalidRequest( "If post_logout_redirect_uri then id_token_hint is a MUST") _cookie_name = self.endpoint_context.cookie_name["session"] try: part = self.endpoint_context.cookie_dealer.get_cookie_value( cookie, cookie_name=_cookie_name) except IndexError: raise InvalidRequest("Cookie error") except KeyError: part = None if part: # value is a base64 encoded JSON document _cookie_info = json.loads(as_unicode(b64d(as_bytes(part[0])))) logger.debug("Cookie info: {}".format(_cookie_info)) _sid = _cookie_info["sid"] else: logger.debug("No relevant cookie") _sid = "" _cookie_info = {} if "id_token_hint" in request: logger.debug("ID token hint: {}".format( request[verified_claim_name("id_token_hint")])) auds = request[verified_claim_name("id_token_hint")]["aud"] _ith_sid = "" _sids = _sdb.sso_db.get_sids_by_sub( request[verified_claim_name("id_token_hint")]["sub"]) if _sids is None: raise ValueError("Unknown subject identifier") for _isid in _sids: if _sdb[_isid]["authn_req"]["client_id"] in auds: _ith_sid = _isid break if not _ith_sid: raise ValueError("Unknown subject") if _sid: if _ith_sid != _sid: # someone's messing with me raise ValueError("Wrong ID Token hint") else: _sid = _ith_sid else: auds = [] try: session = _sdb[_sid] except KeyError: raise ValueError("Can't find any corresponding session") client_id = session["authn_req"]["client_id"] # Does this match what's in the cookie ? if _cookie_info: if client_id != _cookie_info["client_id"]: logger.warning( "Client ID in authz request and in cookie does not match") raise ValueError("Wrong Client") if auds: if client_id not in auds: raise ValueError("Incorrect ID Token hint") _cinfo = _cntx.cdb[client_id] # verify that the post_logout_redirect_uri if present are among the ones # registered try: _uri = request["post_logout_redirect_uri"] except KeyError: if _cntx.issuer.endswith("/"): _uri = "{}{}".format(_cntx.issuer, self.kwargs["post_logout_uri_path"]) else: _uri = "{}/{}".format(_cntx.issuer, self.kwargs["post_logout_uri_path"]) plur = False else: plur = True verify_uri(_cntx, request, "post_logout_redirect_uri", client_id=client_id) payload = { "sid": _sid, "client_id": client_id, "user": session["authn_event"]["uid"], } # redirect user to OP logout verification page if plur and "state" in request: _uri = "{}?{}".format(_uri, urlencode({"state": request["state"]})) payload["state"] = request["state"] payload["redirect_uri"] = _uri logger.debug("JWS payload: {}".format(payload)) # From me to me _jws = JWT( _cntx.keyjar, iss=_cntx.issuer, lifetime=86400, sign_alg=self.kwargs["signing_alg"], ) sjwt = _jws.pack(payload=payload, recv=_cntx.issuer) location = "{}?{}".format(self.kwargs["logout_verify_url"], urlencode({"sjwt": sjwt})) return {"redirect_location": location}