Example #1
0
    def test_client_secret_jwt(self, services):
        _service_context = services['accesstoken'].service_context
        _service_context.token_endpoint = "https://example.com/token"
        _service_context.set(
            'provider_info', {
                'issuer': 'https://example.com/',
                'token_endpoint': "https://example.com/token"
            })

        csj = ClientSecretJWT()
        request = AccessTokenRequest()

        csj.construct(request,
                      service=services['accesstoken'],
                      algorithm="HS256",
                      authn_endpoint='userinfo')
        assert request["client_assertion_type"] == JWT_BEARER
        assert "client_assertion" in request
        cas = request["client_assertion"]

        _kj = KeyJar()
        _kj.add_symmetric(_service_context.get('client_id'),
                          _service_context.get('client_secret'),
                          usage=['sig'])
        jso = JWT(key_jar=_kj, sign_alg='HS256').unpack(cas)
        assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"])

        _rj = JWS(alg='HS256')
        info = _rj.verify_compact(
            cas,
            _kj.get_signing_key(issuer_id=_service_context.get('client_id')))

        assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"])
        assert info['aud'] == [_service_context.get('provider_info')['issuer']]
    def test_verify_client_jws_authn_method(self):
        client_keyjar = KeyJar()
        client_keyjar.import_jwks(KEYJAR.export_jwks(private=True),
                                  CONF["issuer"])
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
        # Audience is OP issuer ID
        aud = "{}token".format(CONF["issuer"])  # aud == Token endpoint
        _assertion = _jwt.pack({"aud": [aud]})

        request = {
            "client_assertion": _assertion,
            "client_assertion_type": JWT_BEARER
        }
        http_info = {"headers": {}}
        res = verify_client(
            self.endpoint_context,
            request,
            http_info=http_info,
            endpoint=self.server.server_get("endpoint", "token"),
        )
        assert res["method"] == "client_secret_jwt"
        assert res["client_id"] == "client_id"
Example #3
0
def test_with_jti():
    _kj = KeyJar()
    _kj.add_symmetric(ALICE, "hemligt ordsprak", usage=["sig"])

    alice = JWT(key_jar=_kj, iss=ALICE, sign_alg="HS256")
    alice.with_jti = True
    payload = {"sub": "sub2"}
    _jwt = alice.pack(payload=payload)

    bob = JWT(key_jar=_kj, iss=BOB, sign_alg="HS256")
    info = bob.unpack(_jwt)
    assert "jti" in info
Example #4
0
def test_msg_cls():
    _kj = KeyJar()
    _kj.add_symmetric(ALICE, "hemligt ordsprak", usage=["sig"])

    alice = JWT(key_jar=_kj, iss=ALICE, sign_alg="HS256")
    payload = {"sub": "sub2"}
    _jwt = alice.pack(payload=payload)

    bob = JWT(key_jar=_kj, iss=BOB, sign_alg="HS256")
    bob.msg_cls = DummyMsg
    info = bob.unpack(_jwt)
    assert isinstance(info, DummyMsg)
Example #5
0
def test_jwt_pack_unpack_sym():
    _kj = KeyJar()
    _kj.add_symmetric(ALICE, 'hemligt ordsprak', usage=['sig'])
    alice = JWT(key_jar=_kj, iss=ALICE, sign_alg="HS256")
    payload = {'sub': 'sub2'}
    _jwt = alice.pack(payload=payload)

    _kj = KeyJar()
    _kj.add_symmetric(ALICE, 'hemligt ordsprak', usage=['sig'])
    bob = JWT(key_jar=_kj, iss=BOB, sign_alg="HS256")
    info = bob.unpack(_jwt)
    assert info
    def test_jws_authn_method_wrong_key(self):
        client_keyjar = KeyJar()
        client_keyjar[CONF["issuer"]] = KEYJAR.issuer_keys[""]
        # Fake symmetric key
        client_keyjar.add_symmetric("", "client_secret:client_secret", ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
        _assertion = _jwt.pack({"aud": [CONF["issuer"]]})

        request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}

        with pytest.raises(NoSuitableSigningKeys):
            self.method.verify(request=request, key_type='private_key')
    def test_jws_authn_method_aud_userinfo_endpoint(self):
        client_keyjar = KeyJar()
        client_keyjar[CONF["issuer"]] = KEYJAR.issuer_keys[""]
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")

        # audience is the OP - not specifically the user info endpoint
        _assertion = _jwt.pack({"aud": [CONF["issuer"]]})

        request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}

        assert self.method.verify(request=request, endpoint="userinfo", key_type='client_secret')
    def test_jws_authn_method_aud_iss(self):
        client_keyjar = KeyJar()
        client_keyjar[CONF["issuer"]] = KEYJAR.issuer_keys[""]
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
        # Audience is OP issuer ID
        aud = CONF["issuer"]
        _assertion = _jwt.pack({"aud": [aud]})

        request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}

        assert self.method.verify(request=request, key_type='client_secret')
def test_jws_authn_method_wrong_key():
    client_keyjar = KeyJar()
    client_keyjar[conf["issuer"]] = KEYJAR.issuer_keys[""]
    # Fake symmetric key
    client_keyjar.add_symmetric("", "client_secret:client_secret", ["sig"])

    _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
    _assertion = _jwt.pack({"aud": [conf["issuer"]]})

    request = {
        "client_assertion": _assertion,
        "client_assertion_type": JWT_BEARER
    }

    with pytest.raises(AuthnFailure):
        JWSAuthnMethod(endpoint_context).verify(request)
    def test_verify_client_jws_authn_method(self):
        client_keyjar = KeyJar()
        client_keyjar[CONF["issuer"]] = KEYJAR.issuer_keys[""]
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
        # Audience is OP issuer ID
        aud = CONF["issuer"] + "token"
        _assertion = _jwt.pack({"aud": [aud]})

        request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}

        res = verify_client(self.endpoint_context, request, endpoint="token")
        assert res["method"] == "client_secret_jwt"
        assert res["client_id"] == "client_id"
    def test_jws_authn_method_aud_not_me(self):
        client_keyjar = KeyJar()
        client_keyjar[CONF["issuer"]] = KEYJAR.issuer_keys[""]
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")

        # Other audiences not OK
        aud = "https://example.org"

        _assertion = _jwt.pack({"aud": [aud]})

        request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}

        with pytest.raises(NotForMe):
            self.method.verify(request=request, key_type='client_secret')
    def test_client_secret_jwt(self):
        client_keyjar = KeyJar()
        client_keyjar[CONF["issuer"]] = KEYJAR.issuer_keys[""]
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
        _jwt.with_jti = True
        _assertion = _jwt.pack({"aud": [CONF["issuer"]]})

        request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}

        assert self.method.is_usable(request=request)
        authn_info = self.method.verify(request)

        assert authn_info["client_id"] == client_id
        assert "jwt" in authn_info
Example #13
0
    def test_to_from_jwt(self):
        item = DummyMessage(req_str="Fair",
                            opt_str="game",
                            opt_int=9,
                            opt_str_list=["one", "two"],
                            req_str_list=["spike", "lee"],
                            opt_json='{"ford": "green"}')
        keyjar = KeyJar()
        keyjar.add_symmetric('', b"A1B2C3D4E5F6G7H8")
        jws = item.to_jwt(key=keyjar.get_signing_key('oct'), algorithm="HS256")

        jitem = DummyMessage().from_jwt(jws, keyjar)

        assert _eq(jitem.keys(), [
            'opt_str', 'req_str', 'opt_json', 'req_str_list', 'opt_str_list',
            'opt_int'
        ])
Example #14
0
def test_client_secret_jwt():
    client_keyjar = KeyJar()
    client_keyjar[conf['issuer']] = KEYJAR.issuer_keys['']
    # The only own key the client has a this point
    client_keyjar.add_symmetric('', client_secret, ['sig'])

    _jwt = JWT(client_keyjar, iss=client_id, sign_alg='HS256')
    _assertion = _jwt.pack({'aud': [conf['issuer']]})

    request = {
        'client_assertion': _assertion,
        'client_assertion_type': JWT_BEARER
    }

    authn_info = ClientSecretJWT(endpoint_context).verify(request)

    assert authn_info['client_id'] == client_id
    assert 'jwt' in authn_info
def test_client_secret_jwt():
    client_keyjar = KeyJar()
    client_keyjar[conf["issuer"]] = KEYJAR.issuer_keys[""]
    # The only own key the client has a this point
    client_keyjar.add_symmetric("", client_secret, ["sig"])

    _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
    _assertion = _jwt.pack({"aud": [conf["issuer"]]})

    request = {
        "client_assertion": _assertion,
        "client_assertion_type": JWT_BEARER
    }

    authn_info = ClientSecretJWT(endpoint_context).verify(request)

    assert authn_info["client_id"] == client_id
    assert "jwt" in authn_info
def test_jws_authn_method_aud_token_endpoint():
    client_keyjar = KeyJar()
    client_keyjar[conf["issuer"]] = KEYJAR.issuer_keys[""]
    # The only own key the client has a this point
    client_keyjar.add_symmetric("", client_secret, ["sig"])

    _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")

    # audience is OP token endpoint - that's OK
    aud = "{}token".format(conf["issuer"])
    _assertion = _jwt.pack({"aud": [aud]})

    request = {
        "client_assertion": _assertion,
        "client_assertion_type": JWT_BEARER
    }

    assert JWSAuthnMethod(endpoint_context).verify(request)
Example #17
0
    def test_to_jwe_from_jwt(self):
        msg = DummyMessage(req_str="Fair",
                           opt_str="game",
                           opt_int=9,
                           opt_str_list=["one", "two"],
                           req_str_list=["spike", "lee"],
                           opt_json='{"ford": "green"}')
        keys = [SYMKey(key="A1B2C3D4E5F6G7H8")]
        jwe = msg.to_jwe(keys, alg="A128KW", enc="A128CBC-HS256")

        keyjar = KeyJar()
        keyjar.add_symmetric('', 'A1B2C3D4E5F6G7H8')
        jitem = DummyMessage().from_jwt(jwe, keyjar)

        assert _eq(jitem.keys(), [
            'opt_str', 'req_str', 'opt_json', 'req_str_list', 'opt_str_list',
            'opt_int'
        ])
    def test_jws_authn_method_aud_userinfo_endpoint(self):
        client_keyjar = KeyJar()
        client_keyjar.import_jwks(KEYJAR.export_jwks(private=True),
                                  CONF["issuer"])
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")

        # audience is the OP - not specifically the user info endpoint
        _assertion = _jwt.pack({"aud": [CONF["issuer"]]})

        request = {
            "client_assertion": _assertion,
            "client_assertion_type": JWT_BEARER
        }

        assert self.method.verify(
            request=request,
            endpoint=self.method.server_get("endpoint", "userinfo"),
            key_type="client_secret",
        )
    def test_jws_authn_method_aud_token_endpoint(self):
        client_keyjar = KeyJar()
        client_keyjar.import_jwks(KEYJAR.export_jwks(private=True),
                                  CONF["issuer"])
        # The only own key the client has a this point
        client_keyjar.add_symmetric("", client_secret, ["sig"])

        _jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")

        # audience is OP token endpoint - that's OK
        aud = "{}token".format(CONF["issuer"])
        _assertion = _jwt.pack({"aud": [aud]})

        request = {
            "client_assertion": _assertion,
            "client_assertion_type": JWT_BEARER
        }

        assert self.method.verify(
            request=request,
            endpoint=self.method.server_get("endpoint", "token"),
            key_type="client_secret",
        )
    def test_client_secret_jwt(self, entity):
        _service_context = entity.client_get("service_context")
        _service_context.token_endpoint = "https://example.com/token"

        _service_context.provider_info = {
            'issuer': 'https://example.com/',
            'token_endpoint': "https://example.com/token"
        }

        _service_context.registration_response = {
            'token_endpoint_auth_signing_alg': "HS256"
        }

        csj = ClientSecretJWT()
        request = AccessTokenRequest()

        csj.construct(request,
                      service=entity.client_get("service", 'accesstoken'),
                      authn_endpoint='token_endpoint')
        assert request["client_assertion_type"] == JWT_BEARER
        assert "client_assertion" in request
        cas = request["client_assertion"]

        _kj = KeyJar()
        _kj.add_symmetric(_service_context.client_id,
                          _service_context.client_secret, ['sig'])
        jso = JWT(key_jar=_kj, sign_alg='HS256').unpack(cas)
        assert _eq(jso.keys(), ["aud", "iss", "sub", "exp", "iat", 'jti'])

        _rj = JWS(alg='HS256')
        info = _rj.verify_compact(
            cas, _kj.get_signing_key(issuer_id=_service_context.client_id))

        assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"])
        assert info['aud'] == [
            _service_context.provider_info['token_endpoint']
        ]
    def test_example(self):
        _symkey = KC_SYM_S.get(alg2keytype("HS256"))
        esreq = EndSessionRequest(id_token_hint=IDTOKEN.to_jwt(
            key=_symkey, algorithm="HS256", lifetime=300),
                                  redirect_url="http://example.org/jqauthz",
                                  state="state0")

        request = EndSessionRequest().from_urlencoded(esreq.to_urlencoded())
        keyjar = KeyJar()
        for _key in _symkey:
            keyjar.add_symmetric('', _key.key)
            keyjar.add_symmetric(ISS, _key.key)
            keyjar.add_symmetric(CLIENT_ID, _key.key)
        request.verify(keyjar=keyjar)
        assert isinstance(request, EndSessionRequest)
        assert set(request.keys()) == {
            verified_claim_name('id_token_hint'), 'id_token_hint',
            'redirect_url', 'state'
        }
        assert request["state"] == "state0"
        assert request[verified_claim_name("id_token_hint")]["aud"] == [
            "client_1"
        ]
 def test_add_symmetric(self):
     kj = KeyJar()
     kj.add_symmetric('', 'abcdefghijklmnop', ['sig'])
     assert list(kj.owners()) == ['']
     assert len(kj.get_signing_key('oct', '')) == 1
Example #23
0
 def test_add_symmetric(self):
     kj = KeyJar()
     kj.add_symmetric("", "abcdefghijklmnop", ["sig"])
     assert list(kj.owners()) == [""]
     assert len(kj.get_signing_key("oct", "")) == 1
Example #24
0
class ServiceContext:
    """
    This class keeps information that a client needs to be able to talk
    to a server. Some of this information comes from configuration and some
    from dynamic provider info discovery or client registration.
    But information is also picked up during the conversation with a server.
    """
    def __init__(self, keyjar=None, config=None, **kwargs):
        self.keyjar = keyjar or KeyJar()
        self.provider_info = {}
        self.registration_response = {}
        self.kid = {"sig": {}, "enc": {}}

        if config is None:
            config = {}
        self.config = config

        # Below so my IDE won't complain
        self.base_url = ''
        self.requests_dir = ''
        self.register_args = {}
        self.allow = {}
        self.behaviour = {}
        self.client_preferences = {}
        self.client_id = ''
        self._c_secret = ''
        self.issuer = ''
        self.redirect_uris = []
        self.callback = None
        self.args = {}
        self.add_on = {}
        self.httpc_params = {}

        try:
            self.clock_skew = config['clock_skew']
        except KeyError:
            self.clock_skew = 15

        for key, val in kwargs.items():
            setattr(self, key, val)

        for attr in [
                'client_id', 'issuer', 'base_url', 'requests_dir', 'allow',
                'client_preferences', 'behaviour', 'provider_info',
                'redirect_uris', 'callback'
        ]:
            try:
                setattr(self, attr, config[attr])
            except KeyError:
                pass

        for attr in RegistrationRequest.c_param:
            try:
                self.register_args[attr] = config[attr]
            except KeyError:
                pass

        if 'client_secret' in config:
            self.set_client_secret(config['client_secret'])

        if self.requests_dir:
            # make sure the path exists. If not, then make it.
            if not os.path.isdir(self.requests_dir):
                os.makedirs(self.requests_dir)

        try:
            self.import_keys(config['keys'])
        except KeyError:
            pass

        if 'keydefs' in config:
            self.keyjar = build_keyjar(config['keydefs'], keyjar=self.keyjar)

    def get_client_secret(self):
        """Return the client secret."""
        return self._c_secret

    def set_client_secret(self, val):
        """Set client secret."""
        if not val:
            self._c_secret = ""
        else:
            self._c_secret = val
            # client uses it for signing
            # Server might also use it for signing which means the
            # client uses it for verifying server signatures
            if self.keyjar is None:
                self.keyjar = KeyJar()
            self.keyjar.add_symmetric("", str(val))

    # since client secret is used as a symmetric key in some instances
    # some special handling is needed for the client_secret attribute
    client_secret = property(get_client_secret, set_client_secret)

    def __setitem__(self, key, value):
        setattr(self, key, value)

    def filename_from_webname(self, webname):
        """
        A 1<->1 map is maintained between a URL pointing to a file and
        the name of the file in the file system.

        As an example if the base_url is 'https://example.com' and a jwks_uri
        is 'https://example.com/jwks_uri.json' then the filename of the
        corresponding file on the local filesystem would be 'jwks_uri'.
        Relative to the directory from which the RP instance is run.

        :param webname: The published URL
        :return: local filename
        """
        if not webname.startswith(self.base_url):
            raise ValueError("Webname doesn't match base_url")

        _name = webname[len(self.base_url):]
        if _name.startswith('/'):
            return _name[1:]

        return _name

    def generate_request_uris(self, path):
        """
        Need to generate a redirect_uri path that is unique for a OP/RP combo
        This is to counter the mix-up attack.

        :param path: Leading path
        :return: A list of one unique URL
        """
        _hash = hashlib.sha256()
        try:
            _hash.update(as_bytes(self.provider_info['issuer']))
        except KeyError:
            _hash.update(as_bytes(self.issuer))
        _hash.update(as_bytes(self.base_url))
        if not path.startswith('/'):
            return ['{}/{}/{}'.format(self.base_url, path, _hash.hexdigest())]

        return ['{}{}/{}'.format(self.base_url, path, _hash.hexdigest())]

    def import_keys(self, keyspec):
        """
        The client needs it's own set of keys. It can either dynamically
        create them or load them from local storage.
        This method can also fetch other entities keys provided the
        URL points to a JWKS.

        :param keyspec:
        """
        for where, spec in keyspec.items():
            if where == 'file':
                for typ, files in spec.items():
                    if typ == 'rsa':
                        for fil in files:
                            _key = RSAKey(
                                key=import_private_rsa_key_from_file(fil),
                                use='sig')
                            _bundle = KeyBundle()
                            _bundle.append(_key)
                            self.keyjar.add_kb('', _bundle)
            elif where == 'url':
                for iss, url in spec.items():
                    _bundle = KeyBundle(source=url)
                    self.keyjar.add_kb(iss, _bundle)

    def get_sign_alg(self, typ):
        """

        :param typ: ['id_token', 'userinfo', 'request_object']
        :return:
        """

        try:
            return self.behaviour[CLI_REG_MAP[typ]['sign']]
        except KeyError:
            try:
                return self.provider_info[PROVIDER_INFO_MAP[typ]['sign']]
            except KeyError:
                pass

        return None

    def get_enc_alg_enc(self, typ):
        """

        :param typ:
        :return:
        """

        res = {}
        for attr in ['enc', 'alg']:
            try:
                _alg = self.behaviour[CLI_REG_MAP[typ][attr]]
            except KeyError:
                try:
                    _alg = self.provider_info[PROVIDER_INFO_MAP[typ][attr]]
                except KeyError:
                    _alg = None

            res[attr] = _alg

        return res