class TestProvider(object): CDB = { "number5": { "password": "******", "client_secret": "drickyoughurt", "redirect_uris": [("http://localhost:8087/authz", None)], "post_logout_redirect_uris": [("https://example.com/post_logout", None)], "client_salt": "salted", "response_types": [ "code", "token", "code id_token", "none", "code token", "id_token", ], }, "a1b2c3": { "redirect_uris": [("http://localhost:8088/authz", None)], "client_salt": "salted", "client_secret": "very_secret", "response_types": ["code", "token", "code id_token"], }, "client0": { "redirect_uris": [("http://www.example.org/authz", None)], "client_secret": "very_secret", "post_logout_redirect_uris": [("https://www.example.org/post_logout", None)], "client_salt": "salted", "response_types": ["code", "token", "code id_token"], }, CLIENT_ID: { "client_secret": CLIENT_SECRET, "redirect_uris": [("http://localhost:8087/authz", None)], "client_salt": "salted", "token_endpoint_auth_method": "client_secret_post", "response_types": ["code", "token", "code id_token"], }, } # type: Dict[str, Dict[str, Any]] @pytest.fixture(autouse=True) def create_provider(self, session_db_factory): self.provider = Provider( SERVER_INFO["issuer"], session_db_factory(SERVER_INFO["issuer"]), self.CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR, ) self.provider.baseurl = self.provider.name self.provider.logout_verify_url = "https://127.0.0.1/logout_verify.html" self.cons = Consumer( DictSessionBackend(), CONSUMER_CONFIG.copy(), CLIENT_CONFIG, server_info=SERVER_INFO, ) self.cons.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.cons.keyjar[""] = KC_RSA self.cons.keyjar.import_jwks(self.provider.keyjar.export_jwks(), self.cons.issuer) self.cons2 = Consumer({}, CONSUMER_CONFIG.copy(), CLIENT_CONFIG_2, server_info=SERVER_INFO) self.cons2.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.cons2.keyjar[""] = KC_RSA def _code_auth(self): state, location = self.cons.begin("openid", "code", path="http://localhost:8087") return self.provider.authorization_endpoint( request=location.split("?")[1]) def _code_auth2(self): state, location = self.cons2.begin("openid", "code", path="http://www.example.org") return self.provider.authorization_endpoint( request=location.split("?")[1]) def _auth_with_id_token(self): state, location = self.cons.begin("openid", "id_token", path="http://localhost:8087") resp = self.provider.authorization_endpoint( request=location.split("?")[1]) aresp = self.cons.parse_response(AuthorizationResponse, resp.message, sformat="urlencoded") return aresp["id_token"] def _create_cookie(self, user, client_id, c_type="sso"): cd = CookieDealer(self.provider) set_cookie = cd.create_cookie("{}][{}".format(user, client_id), c_type, self.provider.sso_cookie_name) cookies_string = set_cookie[1] all_cookies = SimpleCookie() # type: SimpleCookie try: cookies_string = cookies_string.decode() except (AttributeError, UnicodeDecodeError): pass all_cookies.load(cookies_string) return all_cookies def test_missing_post_logout_redirect_uri(self): esr = EndSessionRequest(state="foo") assert self.provider.verify_post_logout_redirect_uri(esr, CLIENT_ID) is None def test_wrong_post_logout_redirect_uri(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://example.com/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") assert self.provider.verify_post_logout_redirect_uri(esr, CLIENT_ID) is None def test_no_post_logout_redirect_uri(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://example.com/plru", "https://example.com/plru2", ] esr = EndSessionRequest(state="foo") assert self.provider.verify_post_logout_redirect_uri(esr, CLIENT_ID) is None def test_let_user_verify_logout(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") res = self.provider.let_user_verify_logout("user", esr, None, None) assert isinstance(res, Response) assert res.headers == [("Content-type", "text/html")] assert res.status_code == 200 def test_let_user_verify_logout_with_cookie(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") res = self.provider.let_user_verify_logout("user", esr, [("Set-Cookie", "kaka")], None) assert isinstance(res, Response) assert set(res.headers) == { ("Content-type", "text/html"), ("Set-Cookie", "kaka"), } assert res.status_code == 200 def test_let_user_verify_logout_with_redirect(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") res = self.provider.let_user_verify_logout( "user", esr, None, "https://example.com/redirect") assert isinstance(res, Response) assert set(res.headers) == {("Content-type", "text/html")} assert res.status_code == 200 # make sure the redirect was propagated txt = '<input type="hidden" name="{}" value="{}"/>'.format( "post_logout_redirect_uri", "https://localhost:8087/plru") assert txt in res.message def test_let_user_verify_logout_with_id_token_hint(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru", id_token_hint="J.W.S", ) res = self.provider.let_user_verify_logout("user", esr, None, None) assert isinstance(res, Response) assert set(res.headers) == {("Content-type", "text/html")} assert res.status_code == 200 # make sure the id_token_hint was propagated txt = '<input type="hidden" name="{}" value="{}"/>'.format( "id_token_hint", "J.W.S") assert txt in res.message def test_end_session_endpoint_with_cookie(self): self.provider.events = DummyEventStore() self._code_auth() cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_with_wrong_cookie(self): # Need cookie and ID Token to figure this out id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) # verify we got valid session id_token_hint = id_token.to_jwt(algorithm="none") cookie = self._create_cookie("diggins", "number5") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" assert _err["error_description"] == "Wrong user" 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() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") cookie = self._create_cookie("diggins", "number5") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" assert _err["error_description"] == "Wrong user" def test_end_session_endpoint_with_cookie_wrong_client(self): # Need cookie and ID Token to figure this out id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") # Wrong client_id cookie = self._create_cookie("username", "a1b2c3") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" def test_end_session_endpoint_with_cookie_dual_login(self): self._code_auth() self._code_auth2() cookie = self._create_cookie("username", "client0") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "client0" assert jwt_info[ "redirect_uri"] == "https://www.example.org/post_logout" def test_end_session_endpoint_with_cookie_dual_login_wrong_client(self): self._code_auth() self._code_auth2() # The cookie states that a user has a session at a client and this # statement is false. cookie = self._create_cookie("username", "a1b2c3") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" def test_end_session_endpoint_with_id_token_hint_only(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") resp = self.provider.end_session_endpoint( urlencode({"id_token_hint": id_token_hint})) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_with_id_token_hint_and_cookie(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_with_post_logout_redirect_uri(self): self._code_auth() cookie = self._create_cookie("username", "number5") post_logout_redirect_uri = self.CDB[str( CLIENT_CONFIG["client_id"])]["post_logout_redirect_uris"][0][0] resp = self.provider.end_session_endpoint( urlencode({ "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde" }), cookie=cookie, ) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_without_post_logout_redirect_uri(self): # default post logout page registered self.provider.post_logout_page = "https://foo.example.com/def_post" # No post_logout_redirect_uris registered _plru = self.provider.cdb["number5"]["post_logout_redirect_uris"] del self.provider.cdb["number5"]["post_logout_redirect_uris"] self._code_auth() cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://foo.example.com/def_post" # restore self.provider.cdb["number5"]["post_logout_redirect_uris"] = _plru def test_end_session_endpoint_without_post_logout_redirect_uri_no_default( self): # No post_logout_redirect_uris registered _plru = self.provider.cdb["number5"]["post_logout_redirect_uris"] del self.provider.cdb["number5"]["post_logout_redirect_uris"] self._code_auth() cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "server_error" # restore self.provider.cdb["number5"]["post_logout_redirect_uris"] = _plru def test_end_session_endpoint_bogus_sjwt(self): self._code_auth() cookie = self._create_cookie("username", "number5") post_logout_redirect_uri = self.CDB[str( CLIENT_CONFIG["client_id"])]["post_logout_redirect_uris"][0][0] resp = self.provider.end_session_endpoint( urlencode({ "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde" }), cookie=cookie, ) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) _sjwt = qs["sjwt"][0] _sjwt = ".".join(_sjwt.split(".")[:2]) + "." # Not signed with pytest.raises(ValueError): self.provider.unpack_signed_jwt(_sjwt) def test_end_session_endpoint_with_wrong_post_logout_redirect_uri(self): self._code_auth() cookie = self._create_cookie("username", "number5") post_logout_redirect_uri = "https://www.example.com/logout" resp = self.provider.end_session_endpoint( urlencode({ "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde" }), cookie=cookie, ) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" def test_end_session_endpoint_with_registered_post_logout_redirect_uri_with_query_part( self, ): self._code_auth() cookie = self._create_cookie("username", "number5") self.provider.cdb["number5"]["post_logout_redirect_uris"] = [ ("https://www.example.com/logout", { "foo": ["bar"] }) ] # No post_logout_redirect_uri in request resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) _qp = parse_qs(resp.message.split("?")[1]) _jwt = self.provider.unpack_signed_jwt(_qp["sjwt"][0]) assert _jwt["redirect_uri"] == "https://www.example.com/logout?foo=bar" def test_back_channel_logout_no_uri(self): self._code_auth() res = self.provider.do_back_channel_logout( self.provider.cdb[CLIENT_ID], "username", "sid") assert res is None def test_back_channel_logout(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["backchannel_logout_uri"] = "https://example.com/bc_logout" _cdb["client_id"] = CLIENT_ID res = self.provider.do_back_channel_logout(_cdb, "username", "_sid_") assert isinstance(res, tuple) assert res[0] == "https://example.com/bc_logout" _jwt = self.provider.unpack_signed_jwt(res[1]) assert _jwt assert _jwt["iss"] == SERVER_INFO["issuer"] assert _jwt["aud"] == [CLIENT_ID] assert _jwt["sub"] == "username" assert _jwt["sid"] == "_sid_" def test_front_channel_logout(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["frontchannel_logout_uri"] = "https://example.com/fc_logout" _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") assert res == '<iframe src="https://example.com/fc_logout">' def test_front_channel_logout_session_required(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["frontchannel_logout_uri"] = "https://example.com/fc_logout" _cdb["frontchannel_logout_session_required"] = True _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") m = re.match(r'<iframe src="([^"]+)">', str(res)) assert m _q = parse_qs(str(m.group(1)).split("?")[1]) assert set(_q.keys()) == {"iss", "sid"} def test_front_channel_logout_session_required_uri_query(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb[ "frontchannel_logout_uri"] = "https://example.com/fc_logout?foo=bar" _cdb["frontchannel_logout_session_required"] = True _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") m = re.match(r'<iframe src="([^"]+)">', str(res)) assert m _q = parse_qs(str(m.group(1)).split("?")[1]) assert set(_q.keys()) == {"foo", "iss", "sid"} def test_front_channel_logout_missing_url(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") assert res is None def test_logout_from_client_bc(self): self._code_auth() self.provider.cdb[CLIENT_ID][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb[CLIENT_ID]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_one_client(_sid, CLIENT_ID) assert set(res.keys()) == {"back_channel", "front_channel"} assert res["back_channel"] != {} assert res["front_channel"] == {} assert set(res["back_channel"].keys()) == {CLIENT_ID} _spec = res["back_channel"][CLIENT_ID] assert _spec[0] == "https://example.com/bc_logout" _jwt = self.provider.unpack_signed_jwt(_spec[1]) assert _jwt assert _jwt["iss"] == SERVER_INFO["issuer"] assert _jwt["aud"] == [CLIENT_ID] assert _jwt["sid"] == _sid def test_logout_from_client_fc(self): self._code_auth() del self.provider.cdb[CLIENT_ID]["backchannel_logout_uri"] self.provider.cdb[CLIENT_ID][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb[CLIENT_ID]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_one_client(_sid, CLIENT_ID) assert set(res.keys()) == {"front_channel", "back_channel"} assert res["back_channel"] == {} assert set(res["front_channel"].keys()) == {CLIENT_ID} _spec = res["front_channel"][CLIENT_ID] assert _spec == '<iframe src="https://example.com/fc_logout">' def test_logout_from_client(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_all_clients(sid=_sid) assert res assert set(res.keys()) == {"back_channel", "front_channel"} assert set(res["front_channel"].keys()) == {"number5"} _spec = res["front_channel"]["number5"] assert _spec == '<iframe src="https://example.com/fc_logout">' assert set(res["back_channel"].keys()) == {"client0"} _spec = res["back_channel"]["client0"] assert _spec[0] == "https://example.com/bc_logout" _jwt = self.provider.unpack_signed_jwt(_spec[1]) assert _jwt assert _jwt["iss"] == SERVER_INFO["issuer"] assert _jwt["aud"] == ["client0"] def test_logout_spec_all(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] logout_spec_all = self.provider.logout_info_for_all_clients(sid=_sid) assert set(logout_spec_all.keys()) == {"back_channel", "front_channel"} assert set(logout_spec_all["back_channel"].keys()) == {"client0"} assert set(logout_spec_all["front_channel"].keys()) == {"number5"} def test_do_verified_logout_all(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=200) res = self.provider.do_verified_logout(_sid, CLIENT_ID, alla=True) assert set(res.keys()) == {"iframe", "cookie"} def test_do_verified_logout_just_the_one(self): self.provider.events = DummyEventStore() self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # There is no back channel logout, hence there should be no HTTP POST exception = requests.ConnectionError() with responses.RequestsMock( assert_all_requests_are_fired=False) as rsps: rsps.add(responses.POST, "https://example.com/bc_logout", body=exception) res = self.provider.do_verified_logout(_sid, CLIENT_ID, alla=False) assert set(res.keys()) == {"iframe", "cookie"} def test_do_verified_logout_the_other(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # This only does back channel logout with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=200) res = self.provider.do_verified_logout(_sid, "client0", alla=False) assert set(res.keys()) == {"cookie"} def test_do_verified_logout_the_other_back_channel_failed(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # Does back channel logout and it will fail with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=400) res = self.provider.do_verified_logout(_sid, "client0", alla=False) assert list(res.keys()) == [] def test_end_session_endpoint_no_post_logout_redirect_uri(self): self._code_auth() cookie = self._create_cookie("username", "number5") self.provider.cdb["number5"]["post_logout_redirect_uris"] = [ ("https://example.com/plru", ""), ("https://example.com/plru2", ""), ] res = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(res, Response) assert res.status_code == 400 def test_logout_info_for_all_clients_no_params(self): with pytest.raises(ParameterError): self.provider.logout_info_for_all_clients() def test_do_back_channel_logout_no_backchannel(self): self._code_auth() # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] _sub = self.provider.sdb[_sid]["sub"] # if "backchannel_logout_uri" in self.provider.cdb["number5"]: del self.provider.cdb["number5"]["backchannel_logout_uri"] res = self.provider.do_back_channel_logout( self.provider.cdb["number5"], _sub, _sid) assert res is None def test_id_token_hint_multiple_aud(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) # verify we got valid session self.provider.cdb["number5"]["post_logout_redirect_uris"] = [ ("https://example.com/plru", "") ] # add another aud and an azp. id_token["azp"] = id_token["aud"][0] id_token["aud"].append("foobar") id_token_hint = id_token.to_jwt(algorithm="none") resp = self.provider.end_session_endpoint( urlencode({"id_token_hint": id_token_hint})) assert isinstance(resp, SeeOther) def test_id_token_hint_aud_does_not_match_client_id(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) # verify we got valid session # add another aud and an azp. id_token_hint = id_token.to_jwt(algorithm="none") # Mess with the session DB _sid = list(self.provider.sdb._db.storage.keys())[0] self.provider.sdb[_sid]["client_id"] = "something else" resp = self.provider.end_session_endpoint( urlencode({"id_token_hint": id_token_hint})) assert isinstance(resp, Response) assert resp.status_code == 400 def test_no_back_or_front_channel_logout(self): self._code_auth() # Mess with client DB for c in ["backchannel_logout_uri", "frontchannel_logout_uri"]: if c in self.provider.cdb["number5"]: del self.provider.cdb["number5"][c] resp = self.provider.do_verified_logout(sid=list( self.provider.sdb._db.storage.keys())[0], client_id="number5") assert resp == {} def test_back_channel_logout_fails(self): self._code_auth() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # There is no back channel logout, hence there should be no HTTP POST with responses.RequestsMock(): res = self.provider.do_verified_logout(_sid, "client0", alla=False) assert res == {} def test_logout_info_for_one_client_no_logout_info(self): self._code_auth() # Mess with client DB for c in ["backchannel_logout_uri", "frontchannel_logout_uri"]: if c in self.provider.cdb["number5"]: del self.provider.cdb["number5"][c] # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] resp = self.provider.logout_info_for_one_client(_sid, "number5") assert resp == {"back_channel": {}, "front_channel": {}} def test_unknown_client(self): self._code_auth() cookie = self._create_cookie("username", "unknown") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) assert resp.status_code == 400 def test_no_cookie_no_id_token_hint(self): self._code_auth() resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"})) assert isinstance(resp, Response) assert resp.status_code == 400 def test_back_channel_logout_failed_front_channel_logout_exists(self): self._code_auth() # client0 self.provider.cdb["number5"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = "number5" # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # Does back channel logout and it will fail with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=400) res = self.provider.do_verified_logout(_sid, "client0", alla=True) assert set(res.keys()) == {"cookie", "iframe"} def test_logout_from_clients_one_without_logout_info(self): self._code_auth() self._code_auth2() # Mess with client DB # neither back channel nor front channel for c in ["backchannel_logout_uri", "frontchannel_logout_uri"]: if c in self.provider.cdb["client0"]: del self.provider.cdb["client0"][c] self.provider.cdb["client0"]["client_id"] = "client0" # both back channel and front channel self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = "number5" # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_all_clients(sid=_sid) assert set(res.keys()) == {"back_channel", "front_channel"} assert set(res["back_channel"].keys()) == {"number5"} assert set(res["front_channel"].keys()) == {"number5"}