Example #1
0
    def test_session_state_in_auth_req_for_session_support(self):
        provider = Provider(
            "foo",
            SessionDB(SERVER_INFO["issuer"]),
            CDB,
            AUTHN_BROKER,
            USERINFO,
            AUTHZ,
            verify_client,
            SYMKEY,
            urlmap=URLMAP,
            keyjar=KEYJAR,
            capabilities={"check_session_iframe": "https://op.example.com/check_session"},
        )

        req_args = {
            "scope": ["openid"],
            "redirect_uri": "http://localhost:8087/authz",
            "response_type": ["code"],
            "client_id": "a1b2c3",
        }
        areq = AuthorizationRequest(**req_args)
        resp = provider.authorization_endpoint(request=areq.to_urlencoded())
        aresp = self.cons.parse_response(AuthorizationResponse, resp.message, sformat="urlencoded")
        assert "session_state" in aresp
Example #2
0
class TestOICProvider(object):
    def setup_class(self):
        self.server = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), CDB,
                               AUTHN_BROKER, USERINFO,
                               AUTHZ, verify_client, SYMKEY, urlmap=URLMAP,
                               keyjar=KEYJAR)

        self.cons = Consumer({}, CONSUMER_CONFIG, CLIENT_CONFIG,
                               server_info=SERVER_INFO, )
        self.cons.behaviour = {"request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]}
        self.cons.debug = True
        self.cons.keyjar[""] = KC_RSA

    def test_server_init(self):
        assert self.server
        assert self.server.authn_broker == AUTHN_BROKER
        print self.server.urlmap
        assert self.server.urlmap["client_1"] == ["https://example.com/authz"]

    def test_server_authorization_endpoint(self):
        bib = {"scope": ["openid"],
               "state": "id-6da9ca0cc23959f5f33e8becd9b08cae",
               "redirect_uri": "http://*****:*****@example.com"]
        req["response_types"] = ["code"]

        print req.to_dict()

        resp = self.server.registration_endpoint(request=req.to_json())

        print resp.message
        regresp = RegistrationResponse().deserialize(resp.message, "json")
        print regresp.keys()
        assert _eq(regresp.keys(), ['redirect_uris', 'contacts', 'application_type',
                                    'client_name', 'registration_client_uri',
                                    'client_secret_expires_at',
                                    'registration_access_token',
                                    'client_id', 'client_secret',
                                    'client_id_issued_at', 'response_types'])

    def test_provider_key_setup(self):
        provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), None,
                            None, None, None, None, "")
        provider.baseurl = "http://www.example.com/"
        provider.key_setup("static", sig={"format": "jwk", "alg": "RSA"})

        keys = provider.keyjar.get_signing_key("RSA")
        assert len(keys) == 1
        assert provider.jwks_uri == "http://www.example.com/static/jwks"

    def _client_id(self, cdb):
        cid = None
        for k, item in cdb.items():
            if item in cdb.keys():
                cid = item
                break

        return cid

    def test_registered_redirect_uri_without_query_component(self):
        provider = Provider("FOO", {}, {}, None, None, None, None, "")
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=["http://example.org/cb"],
                                 response_types=["code"])

        registration_req = rr.to_json()

        provider.registration_endpoint(request=registration_req)

        correct = [
            "http://example.org/cb",
            "http://example.org/cb/foo",
        ]
        faulty = [
            "http://example.org/foo",
            "http://example.com/cb",
            "http://example.org/cb?got=you",
            "http://example.org/cb/foo?got=you"
        ]

        cid = self._client_id(provider.cdb)

        for ruri in faulty:
            areq = AuthorizationRequest(redirect_uri=ruri,
                                        client_id=cid,
                                        response_type="code",
                                        scope="openid")

            print areq
            try:
                provider._verify_redirect_uri(areq)
                assert False
            except RedirectURIError:
                pass

        for ruri in correct:
            areq = AuthorizationRequest(redirect_uri=ruri,
                                        client_id=cid,
                                        response_type="code", scope="openid")

            print areq
            try:
                provider._verify_redirect_uri(areq)
            except RedirectURIError, err:
                print err
                assert False
Example #3
0
class TestProvider(object):
    @pytest.fixture(autouse=True)
    def create_provider(self):
        self.provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]),
                                 CDB,
                                 AUTHN_BROKER, USERINFO,
                                 AUTHZ, verify_client, SYMKEY, urlmap=URLMAP,
                                 keyjar=KEYJAR)
        self.provider.baseurl = self.provider.name

        self.cons = Consumer({}, CONSUMER_CONFIG, CLIENT_CONFIG,
                             server_info=SERVER_INFO, )
        self.cons.behaviour = {
            "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]}
        self.cons.keyjar[""] = KC_RSA

    def test_authorization_endpoint(self):
        bib = {"scope": ["openid"],
               "state": "id-6da9ca0cc23959f5f33e8becd9b08cae",
               "redirect_uri": "http://*****:*****@example.com"]
        req["response_types"] = ["code"]

        resp = self.provider.registration_endpoint(request=req.to_json())

        regresp = RegistrationResponse().deserialize(resp.message, "json")
        assert _eq(regresp.keys(),
                   ['redirect_uris', 'contacts', 'application_type',
                    'client_name', 'registration_client_uri',
                    'client_secret_expires_at',
                    'registration_access_token',
                    'client_id', 'client_secret',
                    'client_id_issued_at', 'response_types'])

    def test_registration_endpoint_with_non_https_redirect_uri_implicit_flow(
            self):
        params = {"application_type": "web",
                  "redirect_uris": ["http://example.com/authz"],
                  "response_types": ["id_token", "token"]}
        req = RegistrationRequest(**params)
        resp = self.provider.registration_endpoint(request=req.to_json())

        assert resp.status == "400 Bad Request"
        error = json.loads(resp.message)
        assert error["error"] == "invalid_redirect_uri"

    def test_verify_redirect_uris_with_https_code_flow(self):
        params = {"application_type": "web",
                  "redirect_uris": ["http://example.com/authz"],
                  "response_types": ["code"]}
        request = RegistrationRequest(**params)
        verified_uris = self.provider._verify_redirect_uris(request)
        assert verified_uris == [("http://example.com/authz", None)]

    def test_verify_redirect_uris_with_non_https_redirect_uri_implicit_flow(self):
        params = {"application_type": "web",
                  "redirect_uris": ["http://example.com/authz"],
                  "response_types": ["id_token", "token"]}
        request = RegistrationRequest(**params)

        with pytest.raises(InvalidRedirectURIError) as exc_info:
            self.provider._verify_redirect_uris(request)

        assert str(exc_info.value) == "None https redirect_uri not allowed"

    @pytest.mark.network
    def test_registration_endpoint_openid4us(self):
        req = RegistrationRequest(
            **{'token_endpoint_auth_method': u'client_secret_post',
               'redirect_uris': [
                   u'https://connect.openid4.us:5443/phpRp/index.php/callback',
                   u'https://connect.openid4.us:5443/phpRp/authcheck.php/authcheckcb'],
               'jwks_uri': u'https://connect.openid4.us:5443/phpRp/rp/rp.jwk',
               'userinfo_encrypted_response_alg': u'RSA1_5',
               'contacts': [u'*****@*****.**'],
               'userinfo_encrypted_response_enc': u'A128CBC-HS256',
               'application_type': u'web',
               'client_name': u'ABRP-17',
               'grant_types': [u'authorization_code', u'implicit'],
               'post_logout_redirect_uris': [
                   u'https://connect.openid4.us:5443/phpRp/index.php/logoutcb'],
               'subject_type': u'public',
               'response_types': [u'code', u'token', u'id_token', u'code token',
                                  u'code id_token', u'id_token token',
                                  u'code id_token token'],
               'policy_uri': u'https://connect.openid4.us:5443/phpRp/index.php/policy',
               'logo_uri': u'https://connect.openid4.us:5443/phpRp/media/logo.png'})

        resp = self.provider.registration_endpoint(request=req.to_json())

        regresp = RegistrationResponse().deserialize(resp.message, "json")
        assert _eq(regresp.keys(), list(req.keys()) +
                   ['registration_client_uri',
                    'client_secret_expires_at',
                    'registration_access_token',
                    'client_id', 'client_secret',
                    'client_id_issued_at'])

    def test_provider_key_setup(self, tmpdir):
        path = tmpdir.strpath
        provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), None,
                            None, None, None, None, "")
        provider.baseurl = "http://www.example.com"
        provider.key_setup(path, path, sig={"format": "jwk", "alg": "RSA"})

        keys = provider.keyjar.get_signing_key("RSA")
        assert len(keys) == 1
        assert provider.jwks_uri == "http://www.example.com/{}/jwks".format(
            path)

    @pytest.mark.parametrize("uri", [
        "http://example.org/foo",
        "http://example.com/cb",
        "http://example.org/cb?got=you",
        "http://example.org/cb/foo?got=you"
    ])
    def test_verify_redirect_uri_faulty_without_query(self, uri):
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=["http://example.org/cb"],
                                 response_types=["code"])
        registration_req = rr.to_json()

        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(redirect_uri=uri,
                                    client_id=cid,
                                    response_type="code",
                                    scope="openid")

        with pytest.raises(RedirectURIError):
            self.provider._verify_redirect_uri(areq)

    @pytest.mark.parametrize("uri", [
        "http://example.org/cb",
        "http://example.org/cb/foo"
    ])
    def test_verify_redirect_uri_correct_without_query(self, uri):
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=["http://example.org/cb"],
                                 response_types=["code"])
        registration_req = rr.to_json()
        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(redirect_uri=uri,
                                    client_id=cid,
                                    response_type="code",
                                    scope="openid")

        self.provider._verify_redirect_uri(areq)

    @pytest.mark.parametrize("uri", [
        "http://example.org/cb",
        "http://example.org/cb/foo",
        "http://example.org/cb?got=you",
        "http://example.org/cb?foo=you"
        "http://example.org/cb?foo=bar&got=you",
        "http://example.org/cb?foo=you&foo=bar"
    ])
    def test_registered_redirect_uri_faulty_with_query_component(self, uri):
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=[
                                     "http://example.org/cb?foo=bar"],
                                 response_types=["code"])

        registration_req = rr.to_json()
        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(redirect_uri=uri,
                                    client_id=cid,
                                    scope="openid",
                                    response_type="code")

        with pytest.raises(RedirectURIError):
            self.provider._verify_redirect_uri(areq)

    def test_registered_redirect_uri_correct_with_query_component(self):
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=[
                                     "http://example.org/cb?foo=bar"],
                                 response_types=["code"])

        registration_req = rr.to_json()
        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(
            redirect_uri="http://example.org/cb?foo=bar",
            client_id=cid, scope="openid",
            response_type="code")

        self.provider._verify_redirect_uri(areq)

    def test_key_rollover(self):
        provider2 = Provider("FOOP", {}, {}, None, None, None, None, "")
        provider2.keyjar = KEYJAR
        # Number of KeyBundles
        assert len(provider2.keyjar.issuer_keys[""]) == 1
        kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]})
        provider2.do_key_rollover(json.loads(kb.jwks()), "b%d")
        assert len(provider2.keyjar.issuer_keys[""]) == 2
        kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]})
        provider2.do_key_rollover(json.loads(kb.jwks()), "b%d")
        assert len(provider2.keyjar.issuer_keys[""]) == 3
        provider2.remove_inactive_keys(-1)
        assert len(provider2.keyjar.issuer_keys[""]) == 2

    def test_endsession_endpoint(self):
        resp = self.provider.endsession_endpoint("")
        self._assert_cookies_expired(resp.headers)

        # End session not allowed if no cookie is sent (can't determine session)
        resp = self.provider.endsession_endpoint("", cookie="FAIL")
        assert resp.status == "400 Bad Request"

    def test_endsession_endpoint_with_id_token_hint(self):
        id_token = self._auth_with_id_token()
        assert self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify we got valid session

        id_token_hint = id_token.to_jwt(algorithm="none")
        resp = self.provider.endsession_endpoint(
            urlencode({"id_token_hint": id_token_hint}))
        assert not self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify session has been removed
        self._assert_cookies_expired(resp.headers)

    def test_endsession_endpoint_with_post_logout_redirect_uri(self):
        id_token = self._auth_with_id_token()
        assert self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify we got valid session

        post_logout_redirect_uri = \
            CDB[CLIENT_CONFIG["client_id"]]["post_logout_redirect_uris"][0][0]
        resp = self.provider.endsession_endpoint(urlencode(
            {"post_logout_redirect_uri": post_logout_redirect_uri}))
        assert isinstance(resp, Redirect)
        assert not self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify session has been removed
        self._assert_cookies_expired(resp.headers)

    def test_session_state_in_auth_req_for_session_support(self):
        provider = Provider("foo", SessionDB(SERVER_INFO["issuer"]), CDB,
                            AUTHN_BROKER, USERINFO,
                            AUTHZ, verify_client, SYMKEY, urlmap=URLMAP,
                            keyjar=KEYJAR, capabilities={
                "check_session_iframe": "https://op.example.com/check_session"})

        req_args = {"scope": ["openid"],
                    "redirect_uri": "http://localhost:8087/authz",
                    "response_type": ["code"],
                    "client_id": "a1b2c3"
                    }
        areq = AuthorizationRequest(**req_args)
        resp = provider.authorization_endpoint(
            request=areq.to_urlencoded())
        aresp = self.cons.parse_response(AuthorizationResponse, resp.message,
                                         sformat="urlencoded")
        assert "session_state" in aresp

    def _assert_cookies_expired(self, http_headers):
        cookies_string = ";".join(
            [c[1] for c in http_headers if c[0] == "Set-Cookie"])
        all_cookies = SimpleCookie()
        all_cookies.load(cookies_string)

        now = datetime.datetime.now()
        for c in [self.provider.cookie_name, self.provider.session_cookie_name]:
            dt = datetime.datetime.strptime(all_cookies[c]["expires"],
                                            "%a, %d-%b-%Y %H:%M:%S GMT")
            assert dt < now  # make sure the cookies have expired to be cleared

    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"]
class TestOICConsumerLogout:
    @pytest.fixture(autouse=True)
    def setup_consumer(self, session_db_factory):
        client_config = {
            "client_id": CLIENT_ID,
            "client_authn_method": CLIENT_AUTHN_METHOD,
        }

        self.consumer = Consumer(DictSessionBackend(), CONFIG, client_config,
                                 SERVER_INFO)
        self.consumer.keyjar = CLIKEYS
        self.consumer.redirect_uris = ["https://example.com/authz"]
        self.consumer.client_secret = "hemlig"
        self.consumer.secret_type = "basic"
        self.consumer.issuer = ISSUER_ID

        self.provider = Provider(
            ISSUER_ID,
            session_db_factory(ISSUER_ID),
            CDB,
            AUTHN_BROKER,
            USERINFO,
            AUTHZ,
            verify_client,
            SYMKEY,
            urlmap=URLMAP,
            keyjar=SRVKEYS,
        )
        self.provider.baseurl = self.provider.name

    def test_logout_with_sub(self):
        # Simulate an authorization
        sid, request_location = self.consumer.begin("openid",
                                                    "code",
                                                    path="https://example.com")
        resp = self.provider.authorization_endpoint(request=request_location)
        part = self.consumer.parse_authz(resp.message)
        assert isinstance(part, tuple)
        aresp = part[0]
        assert aresp

        assert self.consumer.sdb[sid]["issuer"] == self.provider.baseurl

        # Simulate an accesstoken request
        areq = AccessTokenRequest(
            code=aresp["code"],
            client_id=CLIENT_ID,
            redirect_uri="http://example.com/authz",
            client_secret=self.consumer.client_secret,
            grant_type="authorization_code",
        )
        token_resp = self.provider.code_grant_type(areq)
        tresp = self.consumer.parse_response(AccessTokenResponse,
                                             token_resp.message,
                                             sformat="json")

        # Now, for the backchannel logout. This happens on the OP
        logout_info = {
            "sub": tresp["id_token"]["sub"],
            "events": {
                BACK_CHANNEL_LOGOUT_EVENT: {}
            },
        }
        alg = "RS256"
        _jws = JWT(
            self.provider.keyjar,
            iss=self.provider.baseurl,
            lifetime=86400,
            sign_alg=alg,
        )
        logout_token = _jws.pack(aud=CLIENT_ID, **logout_info)

        # The logout request that gets sent to the RP
        request = BackChannelLogoutRequest(logout_token=logout_token)

        # The RP evaluates the request. If everything is OK a session ID (== original state
        # value) is returned.
        _sid = self.consumer.backchannel_logout(request_args=request.to_dict())

        assert _sid == sid

        # Test other coding
        _sid = self.consumer.backchannel_logout(
            request=request.to_urlencoded())
        assert _sid == sid

    def test_not_for_me(self):
        _sub = "sub"

        logout_info = {"sub": _sub, "events": {BACK_CHANNEL_LOGOUT_EVENT: {}}}
        alg = "RS256"
        _jws = JWT(
            self.provider.keyjar,
            iss=self.provider.baseurl,
            lifetime=86400,
            sign_alg=alg,
        )
        logout_token = _jws.pack(aud="someone", **logout_info)

        # The logout request that gets sent to the RP
        request = BackChannelLogoutRequest(logout_token=logout_token)

        with pytest.raises(MessageException):
            self.consumer.backchannel_logout(request_args=request.to_dict())

    def test_logout_without_sub(self):
        # Simulate an authorization
        sid, request_location = self.consumer.begin("openid",
                                                    "code",
                                                    path="https://example.com")
        resp = self.provider.authorization_endpoint(request=request_location)
        part = self.consumer.parse_authz(resp.message)
        assert isinstance(part, tuple)
        aresp = part[0]
        assert aresp

        assert self.consumer.sdb[sid]["issuer"] == self.provider.baseurl

        # Simulate an accesstoken request
        areq = AccessTokenRequest(
            code=aresp["code"],
            client_id=CLIENT_ID,
            redirect_uri="http://example.com/authz",
            client_secret=self.consumer.client_secret,
            grant_type="authorization_code",
        )
        token_resp = self.provider.code_grant_type(areq)
        self.consumer.parse_response(AccessTokenResponse,
                                     token_resp.message,
                                     sformat="json")
        # Have to fake this until the provider changes are in place
        _smid = "session_management_id"
        self.consumer.sso_db.update(sid, "smid", _smid)

        # Now, for the backchannel logout. This happens on the OP
        logout_info = {"sid": _smid, "events": {BACK_CHANNEL_LOGOUT_EVENT: {}}}
        alg = "RS256"
        _jws = JWT(
            self.provider.keyjar,
            iss=self.provider.baseurl,
            lifetime=86400,
            sign_alg=alg,
        )
        logout_token = _jws.pack(aud=CLIENT_ID, **logout_info)

        # The logout request that gets sent to the RP
        request = BackChannelLogoutRequest(logout_token=logout_token)

        # The RP evaluates the request. If everything is OK a session ID (== original state
        # value) is returned.
        _sid = self.consumer.backchannel_logout(request_args=request.to_dict())

        assert _sid == [sid]

    def test_logout_with_none(self):
        # Now for the backchannel logout. This happens on the OP

        logout_info = LogoutToken(events={BACK_CHANNEL_LOGOUT_EVENT: {}})

        alg = "RS256"
        _jws = JWT(
            self.provider.keyjar,
            iss=self.provider.baseurl,
            lifetime=86400,
            sign_alg=alg,
        )
        logout_token = _jws.pack(aud=CLIENT_ID, **logout_info)

        # The logout request that gets sent to the RP
        request = BackChannelLogoutRequest(logout_token=logout_token)

        # The RP evaluates the request. If everything is OK a session ID (== original state
        # value) is returned.
        with pytest.raises(MessageException):
            self.consumer.backchannel_logout(request_args=request.to_dict())

    def test_sso_db_dict(self):
        client_config = {
            "client_id": CLIENT_ID,
            "client_authn_method": CLIENT_AUTHN_METHOD,
        }

        _consumer = Consumer({}, CONFIG, client_config, SERVER_INFO, sso_db={})
        _consumer.keyjar = CLIKEYS
        _consumer.redirect_uris = ["https://example.com/authz"]
        _consumer.client_secret = "hemlig"
        _consumer.secret_type = "basic"
        _consumer.issuer = ISSUER_ID

        # Simulate an authorization
        sid, request_location = _consumer.begin("openid",
                                                "code",
                                                path="https://example.com")
        resp = self.provider.authorization_endpoint(request=request_location)
        part = _consumer.parse_authz(resp.message)
        assert isinstance(part, tuple)
        aresp = part[0]
        assert aresp

        assert _consumer.sdb[sid]["issuer"] == self.provider.baseurl

        # Simulate an accesstoken request
        areq = AccessTokenRequest(
            code=aresp["code"],
            client_id=CLIENT_ID,
            redirect_uri="http://example.com/authz",
            client_secret=_consumer.client_secret,
            grant_type="authorization_code",
        )
        token_resp = self.provider.code_grant_type(areq)
        tresp = _consumer.parse_response(AccessTokenResponse,
                                         token_resp.message,
                                         sformat="json")

        # Now, for the backchannel logout. This happens on the OP
        logout_info = {
            "sub": tresp["id_token"]["sub"],
            "events": {
                BACK_CHANNEL_LOGOUT_EVENT: {}
            },
        }
        alg = "RS256"
        _jws = JWT(
            self.provider.keyjar,
            iss=self.provider.baseurl,
            lifetime=86400,
            sign_alg=alg,
        )
        logout_token = _jws.pack(aud=CLIENT_ID, **logout_info)

        # The logout request that gets sent to the RP
        request = BackChannelLogoutRequest(logout_token=logout_token)

        # The RP evaluates the request. If everything is OK a session ID (== original state
        # value) is returned.
        _sid = _consumer.backchannel_logout(request_args=request.to_dict())
        assert _sid == sid

    def test_attribute_error(self):
        self.consumer.sdb.update("sid", "foo", "bar")
        self.consumer.update("sid")

        with pytest.raises(AttributeError):
            getattr(self.consumer, "foo")
Example #5
0
class FakeOP:
    STATE = "12345678"

    def __init__(self):
        op_base_url = TestConfiguration.get_instance().rp_config.OP_URL
        self.provider = Provider(
            "https://op.tester.se/",
            SessionDB(op_base_url),
            CDB,
            AUTHN_BROKER,
            USERINFO,
            AUTHZ,
            verify_client,
            SYMKEY,
            urlmap=None,
            keyjar=KEYJAR
        )
        self.provider.baseurl = TestConfiguration.get_instance().rp_config.OP_URL
        self.op_base = TestConfiguration.get_instance().rp_config.OP_URL
        self.redirect_urls = TestConfiguration.get_instance().rp_config.CLIENTS[PROVIDER]["client_info"][
            "redirect_uris"]

    def setup_userinfo_endpoint(self):
        cons = Consumer({}, CONSUMER_CONFIG, {"client_id": CLIENT_ID},
                        server_info=SERVER_INFO, )
        cons.behaviour = {
            "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]}
        cons.keyjar[""] = KC_RSA

        cons.client_secret = "drickyoughurt"
        state, location = cons.begin("openid", "token",
                                     path=TestConfiguration.get_instance().rp_base)

        resp = self.provider.authorization_endpoint(
            request=urlparse(location).query)

        # redirect
        atr = AuthorizationResponse().deserialize(
            urlparse(resp.message).fragment, "urlencoded")

        uir = UserInfoRequest(access_token=atr["access_token"], schema="openid")
        resp = self.provider.userinfo_endpoint(request=uir.to_urlencoded())
        responses.add(
            responses.POST,
            self.op_base + "userinfo",
            body=resp.message,
            status=200,
            content_type='application/json')

    def setup_token_endpoint(self):
        authreq = AuthorizationRequest(state="state",
                                       redirect_uri=self.redirect_urls[0],
                                       client_id=CLIENT_ID,
                                       response_type="code",
                                       scope=["openid"])
        _sdb = self.provider.sdb
        sid = _sdb.token.key(user="******", areq=authreq)
        access_grant = _sdb.token(sid=sid)
        ae = AuthnEvent("user", "salt")
        _sdb[sid] = {
            "oauth_state": "authz",
            "authn_event": ae,
            "authzreq": authreq.to_json(),
            "client_id": CLIENT_ID,
            "code": access_grant,
            "code_used": False,
            "scope": ["openid"],
            "redirect_uri": self.redirect_urls[0],
        }
        _sdb.do_sub(sid, "client_salt")
        # Construct Access token request
        areq = AccessTokenRequest(code=access_grant, client_id=CLIENT_ID,
                                  redirect_uri=self.redirect_urls[0],
                                  client_secret="client_secret_1")
        txt = areq.to_urlencoded()
        resp = self.provider.token_endpoint(request=txt)
        responses.add(
            responses.POST,
            self.op_base + "token",
            body=resp.message,
            status=200,
            content_type='application/json')

    def setup_authentication_response(self, state=None):
        context = Context()
        context.path = 'openid/authz_cb'
        op_base = TestConfiguration.get_instance().rp_config.OP_URL
        if not state:
            state = rndstr()
        context.request = {
            'code': 'F+R4uWbN46U+Bq9moQPC4lEvRd2De4o=',
            'scope': 'openid profile email address phone',
            'state': state}
        context.state = self.generate_state(op_base)
        return context

    def generate_state(self, op_base):
        state = State()
        state_id = TestConfiguration.get_instance().rp_config.STATE_ID
        state_data = {
            StateKeys.OP: PROVIDER,
            StateKeys.NONCE: "9YraWpJAmVp4L3NJ",
            StateKeys.TOKEN_ENDPOINT: TestConfiguration.get_instance().rp_config.OP_URL + "token",
            StateKeys.CLIENT_ID: "client_1",
            StateKeys.CLIENT_SECRET: "2222222222",
            StateKeys.JWKS_URI:
                TestConfiguration.get_instance().rp_config.OP_URL + "static/jwks.json",
            StateKeys.USERINFO_ENDPOINT:
                TestConfiguration.get_instance().rp_config.OP_URL + "userinfo",
            StateKeys.STATE: FakeOP.STATE
        }
        state.add(state_id, state_data)
        return state

    def setup_client_registration_endpoint(self):
        client_info = TestConfiguration.get_instance().rp_config.CLIENTS[PROVIDER]["client_info"]
        request = RegistrationRequest().deserialize(json.dumps(client_info), "json")
        _cinfo = self.provider.do_client_registration(request, CLIENT_ID)
        args = dict([(k, v) for k, v in _cinfo.items()
                     if k in RegistrationResponse.c_param])
        args['client_id'] = CLIENT_ID
        self.provider.comb_uri(args)
        registration_response = RegistrationResponse(**args)
        responses.add(
            responses.POST,
            self.op_base + "registration",
            body=registration_response.to_json(),
            status=200,
            content_type='application/json')

    def setup_opienid_config_endpoint(self):
        self.provider.baseurl = self.op_base
        self.provider.jwks_uri = self.publish_jwks()
        provider_info = self.provider.create_providerinfo()
        responses.add(
            responses.GET,
            self.op_base + ".well-known/openid-configuration",
            body=provider_info.to_json(),
            status=200,
            content_type='application/json'
        )

    def setup_webfinger_endpoint(self):
        wf = WebFinger()
        resp = Response(wf.response(subject=self.op_base, base=self.op_base))
        responses.add(responses.GET,
                      self.op_base + ".well-known/webfinger",
                      body=resp.message,
                      status=200,
                      content_type='application/json')

    def publish_jwks(self):
        jwks_uri = self.op_base + "static/jwks.json"
        responses.add(
            responses.GET,
            jwks_uri,
            body=json.dumps(JWKS),
            status=200,
            content_type='application/json')
        return jwks_uri
Example #6
0
class TestProvider(object):
    @pytest.fixture(autouse=True)
    def create_provider(self):
        self.provider = Provider(SERVER_INFO["issuer"],
                                 SessionDB(SERVER_INFO["issuer"]),
                                 CDB,
                                 AUTHN_BROKER,
                                 USERINFO,
                                 AUTHZ,
                                 verify_client,
                                 SYMKEY,
                                 urlmap=URLMAP,
                                 keyjar=KEYJAR)
        self.provider.baseurl = self.provider.name

        self.cons = Consumer(
            {},
            CONSUMER_CONFIG,
            CLIENT_CONFIG,
            server_info=SERVER_INFO,
        )
        self.cons.behaviour = {
            "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]
        }
        self.cons.keyjar[""] = KC_RSA

    def test_authorization_endpoint(self):
        bib = {
            "scope": ["openid"],
            "state": "id-6da9ca0cc23959f5f33e8becd9b08cae",
            "redirect_uri": "http://*****:*****@example.com"]
        req["response_types"] = ["code"]

        resp = self.provider.registration_endpoint(request=req.to_json())

        regresp = RegistrationResponse().deserialize(resp.message, "json")
        assert _eq(regresp.keys(), [
            'redirect_uris', 'contacts', 'application_type', 'client_name',
            'registration_client_uri', 'client_secret_expires_at',
            'registration_access_token', 'client_id', 'client_secret',
            'client_id_issued_at', 'response_types'
        ])

    def test_registration_endpoint_with_non_https_redirect_uri_implicit_flow(
            self):
        params = {
            "application_type": "web",
            "redirect_uris": ["http://example.com/authz"],
            "response_types": ["id_token", "token"]
        }
        req = RegistrationRequest(**params)
        resp = self.provider.registration_endpoint(request=req.to_json())

        assert resp.status == "400 Bad Request"
        error = json.loads(resp.message)
        assert error["error"] == "invalid_redirect_uri"

    def test_verify_redirect_uris_with_https_code_flow(self):
        params = {
            "application_type": "web",
            "redirect_uris": ["http://example.com/authz"],
            "response_types": ["code"]
        }
        request = RegistrationRequest(**params)
        verified_uris = self.provider._verify_redirect_uris(request)
        assert verified_uris == [("http://example.com/authz", None)]

    def test_verify_redirect_uris_with_non_https_redirect_uri_implicit_flow(
            self):
        params = {
            "application_type": "web",
            "redirect_uris": ["http://example.com/authz"],
            "response_types": ["id_token", "token"]
        }
        request = RegistrationRequest(**params)

        with pytest.raises(InvalidRedirectURIError) as exc_info:
            self.provider._verify_redirect_uris(request)

        assert str(exc_info.value) == "None https redirect_uri not allowed"

    # @pytest.mark.network
    # def test_registration_endpoint_openid4us(self):
    #     req = RegistrationRequest(
    #             **{'token_endpoint_auth_method': u'client_secret_post',
    #                'redirect_uris': [
    #                    u'https://connect.openid4.us:5443/phpRp/index.php'
    #                    u'/callback',
    #                    u'https://connect.openid4.us:5443/phpRp/authcheck.php'
    #                    u'/authcheckcb'],
    #                'jwks_uri':
    #                    u'https://connect.openid4.us:5443/phpRp/rp/rp.jwk',
    #                'userinfo_encrypted_response_alg': u'RSA1_5',
    #                'contacts': [u'*****@*****.**'],
    #                'userinfo_encrypted_response_enc': u'A128CBC-HS256',
    #                'application_type': u'web',
    #                'client_name': u'ABRP-17',
    #                'grant_types': [u'authorization_code', u'implicit'],
    #                'post_logout_redirect_uris': [
    #                    u'https://connect.openid4.us:5443/phpRp/index.php'
    #                    u'/logoutcb'],
    #                'subject_type': u'public',
    #                'response_types': [u'code', u'token', u'id_token',
    #                                   u'code token',
    #                                   u'code id_token', u'id_token token',
    #                                   u'code id_token token'],
    #                'policy_uri':
    #                    u'https://connect.openid4.us:5443/phpRp/index.php'
    #                    u'/policy',
    #                'logo_uri':
    #                    u'https://connect.openid4.us:5443/phpRp/media/logo.png'})
    #
    #     resp = self.provider.registration_endpoint(request=req.to_json())
    #
    #     regresp = RegistrationResponse().deserialize(resp.message, "json")
    #     assert _eq(regresp.keys(), list(req.keys()) +
    #                ['registration_client_uri',
    #                 'client_secret_expires_at',
    #                 'registration_access_token',
    #                 'client_id', 'client_secret',
    #                 'client_id_issued_at'])

    def test_provider_key_setup(self, tmpdir):
        path = tmpdir.strpath
        provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]),
                            None, None, None, None, None, "")
        provider.baseurl = "http://www.example.com"
        provider.key_setup(path, path, sig={"format": "jwk", "alg": "RSA"})

        keys = provider.keyjar.get_signing_key("RSA")
        assert len(keys) == 1
        assert provider.jwks_uri == "http://www.example.com/{}/jwks".format(
            path)

    @pytest.mark.parametrize("uri", [
        "http://example.org/foo", "http://example.com/cb",
        "http://example.org/cb?got=you", "http://example.org/cb/foo?got=you"
    ])
    def test_verify_redirect_uri_faulty_without_query(self, uri):
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=["http://example.org/cb"],
                                 response_types=["code"])
        registration_req = rr.to_json()

        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(redirect_uri=uri,
                                    client_id=cid,
                                    response_type="code",
                                    scope="openid")

        with pytest.raises(RedirectURIError):
            self.provider._verify_redirect_uri(areq)

    @pytest.mark.parametrize("uri", [
        "http://example.org/cb",
    ])
    def test_verify_redirect_uri_correct_without_query(self, uri):
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=["http://example.org/cb"],
                                 response_types=["code"])
        registration_req = rr.to_json()
        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(redirect_uri=uri,
                                    client_id=cid,
                                    response_type="code",
                                    scope="openid")

        self.provider._verify_redirect_uri(areq)

    @pytest.mark.parametrize("uri", [
        "http://example.org/cb", "http://example.org/cb?got=you",
        "http://example.org/cb?foo=you"
        "http://example.org/cb?foo=bar&got=you",
        "http://example.org/cb?foo=you&foo=bar"
    ])
    def test_registered_redirect_uri_faulty_with_query_component(self, uri):
        rr = RegistrationRequest(
            operation="register",
            redirect_uris=["http://example.org/cb?foo=bar"],
            response_types=["code"])

        registration_req = rr.to_json()
        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(redirect_uri=uri,
                                    client_id=cid,
                                    scope="openid",
                                    response_type="code")

        with pytest.raises(RedirectURIError):
            self.provider._verify_redirect_uri(areq)

    def test_registered_redirect_uri_correct_with_query_component(self):
        rr = RegistrationRequest(
            operation="register",
            redirect_uris=["http://example.org/cb?foo=bar"],
            response_types=["code"])

        registration_req = rr.to_json()
        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)
        cid = regresp["client_id"]

        areq = AuthorizationRequest(
            redirect_uri="http://example.org/cb?foo=bar",
            client_id=cid,
            scope="openid",
            response_type="code")

        self.provider._verify_redirect_uri(areq)

    def test_read_registration(self):
        rr = RegistrationRequest(operation="register",
                                 redirect_uris=["http://example.org/new"],
                                 response_types=["code"])
        registration_req = rr.to_json()
        resp = self.provider.registration_endpoint(request=registration_req)
        regresp = RegistrationResponse().from_json(resp.message)

        authn = ' '.join(['Bearer', regresp['registration_access_token']])
        query = '='.join(['client_id', regresp['client_id']])
        resp = self.provider.read_registration(authn, query)

        assert json.loads(resp.message) == regresp.to_dict()

    def test_read_registration_wrong_authn(self):
        resp = self.provider.read_registration('wrong string', 'request')
        assert resp.status == '400 Bad Request'
        assert json.loads(resp.message) == {
            'error': 'invalid_request',
            'error_description': None
        }

    def test_key_rollover(self):
        provider2 = Provider("FOOP", {}, {}, None, None, None, None, "")
        provider2.keyjar = KEYJAR
        # Number of KeyBundles
        assert len(provider2.keyjar.issuer_keys[""]) == 1
        kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]})
        provider2.do_key_rollover(json.loads(kb.jwks()), "b%d")
        assert len(provider2.keyjar.issuer_keys[""]) == 2
        kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]})
        provider2.do_key_rollover(json.loads(kb.jwks()), "b%d")
        assert len(provider2.keyjar.issuer_keys[""]) == 3
        provider2.remove_inactive_keys(-1)
        assert len(provider2.keyjar.issuer_keys[""]) == 2

    def test_endsession_endpoint(self):
        resp = self.provider.endsession_endpoint("")
        self._assert_cookies_expired(resp.headers)

        # End session not allowed if no cookie is sent (can't determine session)
        resp = self.provider.endsession_endpoint("", cookie="FAIL")
        assert resp.status == "400 Bad Request"

    def test_endsession_endpoint_with_id_token_hint(self):
        id_token = self._auth_with_id_token()
        assert self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify we got valid session

        id_token_hint = id_token.to_jwt(algorithm="none")
        resp = self.provider.endsession_endpoint(
            urlencode({"id_token_hint": id_token_hint}))
        assert not self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify session has been removed
        self._assert_cookies_expired(resp.headers)

    def test_endsession_endpoint_with_post_logout_redirect_uri(self):
        id_token = self._auth_with_id_token()
        assert self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify we got valid session

        post_logout_redirect_uri = \
            CDB[CLIENT_CONFIG["client_id"]]["post_logout_redirect_uris"][0][0]
        resp = self.provider.endsession_endpoint(
            urlencode({"post_logout_redirect_uri": post_logout_redirect_uri}))
        assert isinstance(resp, SeeOther)
        assert not self.provider.sdb.get_sids_by_sub(
            id_token["sub"])  # verify session has been removed
        self._assert_cookies_expired(resp.headers)

    def test_session_state_in_auth_req_for_session_support(self):
        provider = Provider(SERVER_INFO["issuer"],
                            SessionDB(SERVER_INFO["issuer"]),
                            CDB,
                            AUTHN_BROKER,
                            USERINFO,
                            AUTHZ,
                            verify_client,
                            SYMKEY,
                            urlmap=URLMAP,
                            keyjar=KEYJAR)

        provider.capabilities.update(
            {"check_session_iframe": "https://op.example.com/check_session"})

        req_args = {
            "scope": ["openid"],
            "redirect_uri": "http://localhost:8087/authz",
            "response_type": ["code"],
            "client_id": "number5"
        }
        areq = AuthorizationRequest(**req_args)
        resp = provider.authorization_endpoint(request=areq.to_urlencoded())
        aresp = self.cons.parse_response(AuthorizationResponse,
                                         resp.message,
                                         sformat="urlencoded")
        assert "session_state" in aresp

    def _assert_cookies_expired(self, http_headers):
        cookies_string = ";".join(
            [c[1] for c in http_headers if c[0] == "Set-Cookie"])
        all_cookies = SimpleCookie()

        try:
            cookies_string = cookies_string.decode()
        except (AttributeError, UnicodeDecodeError):
            pass

        all_cookies.load(cookies_string)

        now = datetime.datetime.utcnow()  #
        for c in [
                self.provider.cookie_name, self.provider.session_cookie_name
        ]:
            dt = datetime.datetime.strptime(all_cookies[c]["expires"],
                                            "%a, %d-%b-%Y %H:%M:%S GMT")
            assert dt < now  # make sure the cookies have expired to be cleared

    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 test_id_token_RS512_sign(self):
        self.provider.capabilities['id_token_signing_alg_values_supported'] = [
            'RS512'
        ]
        self.provider.build_jwx_def()
        id_token = self._auth_with_id_token()
        assert id_token.jws_header['alg'] == "RS512"
Example #7
0
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()  # type: ignore

        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()  # type: ignore

        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")

        # only cookies
        assert set(resp.keys()) == {"cookie"}

    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"}
Example #8
0
class FakeOP:
    STATE = "12345678"

    def __init__(self):
        op_base_url = TestConfiguration.get_instance().rp_config.OP_URL
        self.provider = Provider("https://op.tester.se/",
                                 SessionDB(op_base_url),
                                 CDB,
                                 AUTHN_BROKER,
                                 USERINFO,
                                 AUTHZ,
                                 verify_client,
                                 SYMKEY,
                                 urlmap=None,
                                 keyjar=KEYJAR)
        self.provider.baseurl = TestConfiguration.get_instance(
        ).rp_config.OP_URL
        self.op_base = TestConfiguration.get_instance().rp_config.OP_URL
        self.redirect_urls = TestConfiguration.get_instance(
        ).rp_config.CLIENTS[PROVIDER]["client_info"]["redirect_uris"]

    def setup_userinfo_endpoint(self):
        cons = Consumer(
            {},
            CONSUMER_CONFIG,
            {"client_id": CLIENT_ID},
            server_info=SERVER_INFO,
        )
        cons.behaviour = {
            "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]
        }
        cons.keyjar[""] = KC_RSA

        cons.client_secret = "drickyoughurt"
        state, location = cons.begin(
            "openid", "token", path=TestConfiguration.get_instance().rp_base)

        resp = self.provider.authorization_endpoint(
            request=urlparse(location).query)

        # redirect
        atr = AuthorizationResponse().deserialize(
            urlparse(resp.message).fragment, "urlencoded")

        uir = UserInfoRequest(access_token=atr["access_token"],
                              schema="openid")
        resp = self.provider.userinfo_endpoint(request=uir.to_urlencoded())
        responses.add(responses.POST,
                      self.op_base + "userinfo",
                      body=resp.message,
                      status=200,
                      content_type='application/json')

    def setup_token_endpoint(self):
        authreq = AuthorizationRequest(state="state",
                                       redirect_uri=self.redirect_urls[0],
                                       client_id=CLIENT_ID,
                                       response_type="code",
                                       scope=["openid"])
        _sdb = self.provider.sdb
        sid = _sdb.token.key(user="******", areq=authreq)
        access_grant = _sdb.token(sid=sid)
        ae = AuthnEvent("user", "salt")
        _sdb[sid] = {
            "oauth_state": "authz",
            "authn_event": ae,
            "authzreq": authreq.to_json(),
            "client_id": CLIENT_ID,
            "code": access_grant,
            "code_used": False,
            "scope": ["openid"],
            "redirect_uri": self.redirect_urls[0],
        }
        _sdb.do_sub(sid, "client_salt")
        # Construct Access token request
        areq = AccessTokenRequest(code=access_grant,
                                  client_id=CLIENT_ID,
                                  redirect_uri=self.redirect_urls[0],
                                  client_secret="client_secret_1")
        txt = areq.to_urlencoded()
        resp = self.provider.token_endpoint(request=txt)
        responses.add(responses.POST,
                      self.op_base + "token",
                      body=resp.message,
                      status=200,
                      content_type='application/json')

    def setup_authentication_response(self, state=None):
        context = Context()
        context.path = 'openid/authz_cb'
        op_base = TestConfiguration.get_instance().rp_config.OP_URL
        if not state:
            state = rndstr()
        context.request = {
            'code': 'F+R4uWbN46U+Bq9moQPC4lEvRd2De4o=',
            'scope': 'openid profile email address phone',
            'state': state
        }
        context.state = self.generate_state(op_base)
        return context

    def generate_state(self, op_base):
        state = State()
        state_id = TestConfiguration.get_instance().rp_config.STATE_ID
        state_data = {
            StateKeys.OP:
            PROVIDER,
            StateKeys.NONCE:
            "9YraWpJAmVp4L3NJ",
            StateKeys.TOKEN_ENDPOINT:
            TestConfiguration.get_instance().rp_config.OP_URL + "token",
            StateKeys.CLIENT_ID:
            "client_1",
            StateKeys.CLIENT_SECRET:
            "2222222222",
            StateKeys.JWKS_URI:
            TestConfiguration.get_instance().rp_config.OP_URL +
            "static/jwks.json",
            StateKeys.USERINFO_ENDPOINT:
            TestConfiguration.get_instance().rp_config.OP_URL + "userinfo",
            StateKeys.STATE:
            FakeOP.STATE
        }
        state.add(state_id, state_data)
        return state

    def setup_client_registration_endpoint(self):
        client_info = TestConfiguration.get_instance(
        ).rp_config.CLIENTS[PROVIDER]["client_info"]
        request = RegistrationRequest().deserialize(json.dumps(client_info),
                                                    "json")
        _cinfo = self.provider.do_client_registration(request, CLIENT_ID)
        args = dict([(k, v) for k, v in _cinfo.items()
                     if k in RegistrationResponse.c_param])
        args['client_id'] = CLIENT_ID
        self.provider.comb_uri(args)
        registration_response = RegistrationResponse(**args)
        responses.add(responses.POST,
                      self.op_base + "registration",
                      body=registration_response.to_json(),
                      status=200,
                      content_type='application/json')

    def setup_opienid_config_endpoint(self):
        self.provider.baseurl = self.op_base
        self.provider.jwks_uri = self.publish_jwks()
        provider_info = self.provider.create_providerinfo()
        responses.add(responses.GET,
                      self.op_base + ".well-known/openid-configuration",
                      body=provider_info.to_json(),
                      status=200,
                      content_type='application/json')

    def setup_webfinger_endpoint(self):
        wf = WebFinger()
        resp = Response(wf.response(subject=self.op_base, base=self.op_base))
        responses.add(responses.GET,
                      self.op_base + ".well-known/webfinger",
                      body=resp.message,
                      status=200,
                      content_type='application/json')

    def publish_jwks(self):
        jwks_uri = self.op_base + "static/jwks.json"
        responses.add(responses.GET,
                      jwks_uri,
                      body=json.dumps(JWKS),
                      status=200,
                      content_type='application/json')
        return jwks_uri