Beispiel #1
0
 def load_key(header, payload):
     jwk_set = JsonWebKey.import_key_set(self.fetch_jwk_set())
     try:
         return jwk_set.find_by_kid(header.get('kid'))
     except ValueError:
         # re-try with new jwk set
         jwk_set = JsonWebKey.import_key_set(
             self.fetch_jwk_set(force=True))
         return jwk_set.find_by_kid(header.get('kid'))
Beispiel #2
0
    def test_import_key_set(self):
        jwks_public = read_file_path('jwks_public.json')
        key_set1 = JsonWebKey.import_key_set(jwks_public)
        key1 = key_set1.find_by_kid('abc')
        self.assertEqual(key1['e'], 'AQAB')

        key_set2 = JsonWebKey.import_key_set(jwks_public['keys'])
        key2 = key_set2.find_by_kid('abc')
        self.assertEqual(key2['e'], 'AQAB')

        key_set3 = JsonWebKey.import_key_set(json_dumps(jwks_public))
        key3 = key_set3.find_by_kid('abc')
        self.assertEqual(key3['e'], 'AQAB')

        self.assertRaises(ValueError, JsonWebKey.import_key_set, 'invalid')
Beispiel #3
0
 def load_key(header, _):
     alg = header.get("alg")
     if alg in ["HS256", "HS384", "HS512"]:
         # For HS256: client secret is used for id_token signing
         return self.client_secret
     elif alg in ["RS256", "RS384", "RS512"]:
         jwk_set = JsonWebKey.import_key_set(self.fetch_jwk_set())
         try:
             return jwk_set.find_by_kid(header.get("kid"))
         except ValueError:
             # re-try with new jwk set
             jwk_set = JsonWebKey.import_key_set(
                 self.fetch_jwk_set(force=True))
             return jwk_set.find_by_kid(header.get("kid"))
     else:
         raise RuntimeError(f"Unsupported id_token algorithm: '{alg}'")
Beispiel #4
0
 def fetch_keys(self):
     if not self.key_url:
         return
     response = requests.get(self.key_url)
     if response.ok:
         data = response.json()
         self._keyset = JsonWebKey.import_key_set(data)
Beispiel #5
0
    def verifyToken(self, accessToken=None, jwks=None):
        """Verify access token

        :param str accessToken: access token
        :param dict jwks: JWKs

        :return: dict
        """
        # Define an access token
        if not accessToken:
            accessToken = self.token["access_token"]
        # Renew a JWKs of an identity provider if needed
        if not jwks:
            result = self.updateJWKs()
            if not result["OK"]:
                return result
            jwks = self.jwks
        if not jwks:
            return S_ERROR("JWKs not found.")
        # Try to decode and verify an access token
        self.log.debug("Try to decode token %s with JWKs:\n" % accessToken, pprint.pformat(jwks))
        try:
            return S_OK(jwt.decode(accessToken, JsonWebKey.import_key_set(jwks)))
        except Exception as e:
            self.log.exception(e)
            return S_ERROR(repr(e))
Beispiel #6
0
    async def parse_id_token(self, token, nonce, claims_options=None):
        """Return an instance of UserInfo from token's ``id_token``."""
        claims_params = dict(
            nonce=nonce,
            client_id=self.client_id,
        )
        if 'access_token' in token:
            claims_params['access_token'] = token['access_token']
            claims_cls = CodeIDToken
        else:
            claims_cls = ImplicitIDToken

        metadata = await self.load_server_metadata()
        if claims_options is None and 'issuer' in metadata:
            claims_options = {'iss': {'values': [metadata['issuer']]}}

        alg_values = metadata.get('id_token_signing_alg_values_supported')
        if not alg_values:
            alg_values = ['RS256']

        jwt = JsonWebToken(alg_values)

        jwk_set = await self.fetch_jwk_set()
        try:
            claims = jwt.decode(
                token['id_token'],
                key=JsonWebKey.import_key_set(jwk_set),
                claims_cls=claims_cls,
                claims_options=claims_options,
                claims_params=claims_params,
            )
        except ValueError:
            jwk_set = await self.fetch_jwk_set(force=True)
            claims = jwt.decode(
                token['id_token'],
                key=JsonWebKey.import_key_set(jwk_set),
                claims_cls=claims_cls,
                claims_options=claims_options,
                claims_params=claims_params,
            )

        # https://github.com/lepture/authlib/issues/259
        if claims.get('nonce_supported') is False:
            claims.params['nonce'] = None
        claims.validate(leeway=120)
        return UserInfo(claims)
Beispiel #7
0
    def readToken(self, token):
        """Decode self token

        :param str token: token to decode

        :return: S_OK(dict)/S_ERROR()
        """
        result = self.db.getKeySet()
        if not result["OK"]:
            return result
        try:
            return S_OK(jwt.decode(token, JsonWebKey.import_key_set(result["Value"].as_dict())))
        except Exception as e:
            sLog.exception(e)
            return S_ERROR(repr(e))
Beispiel #8
0
def load_config():
    load_dotenv()

    config = {
        **os.environ,
    }

    # symmetric key, therefore no need to update or anything
    # we use https anyways :)
    with open('static/keys.json', 'r') as keys:
        config["KEY_SET"] = JsonWebKey.import_key_set(json.loads(keys.read()))
        config["OAUTH2_JWT_KEY"] = config["KEY_SET"].keys[0]

    config["ADMINS"] = str(config["ADMINS"]).split(",")

    return config
Beispiel #9
0
    def validate_jwks(self):
        """Client's JSON Web Key Set [RFC7517] document value, which contains
        the client's public keys.  The value of this field MUST be a JSON
        object containing a valid JWK Set.  These keys can be used by
        higher-level protocols that use signing or encryption.  This
        parameter is intended to be used by clients that cannot use the
        "jwks_uri" parameter, such as native clients that cannot host
        public URLs.  The "jwks_uri" and "jwks" parameters MUST NOT both
        be present in the same request or response.
        """
        if 'jwks' in self:
            if 'jwks_uri' in self:
                #  The "jwks_uri" and "jwks" parameters MUST NOT both  be present
                raise InvalidClaimError('jwks')

            jwks = self['jwks']
            try:
                key_set = JsonWebKey.import_key_set(jwks)
                if not key_set:
                    raise InvalidClaimError('jwks')
            except ValueError:
                raise InvalidClaimError('jwks')
Beispiel #10
0
def test_keys():
    """Try to store/get/remove keys"""
    # JWS
    jws = JsonWebSignature(algorithms=["RS256"])
    code_payload = {
        "user_id": "user",
        "scope": "scope",
        "client_id": "client",
        "redirect_uri": "redirect_uri",
        "code_challenge": "code_challenge",
    }

    # Token metadata
    header = {"alg": "RS256"}
    payload = {
        "sub": "user",
        "iss": "issuer",
        "scope": "scope",
        "setup": "setup",
        "group": "my_group"
    }

    # Remove all keys
    result = db.removeKeys()
    assert result["OK"], result["Message"]

    # Check active keys
    result = db.getActiveKeys()
    assert result["OK"], result["Message"]
    assert result["Value"] == []

    # Create new one
    result = db.getPrivateKey()
    assert result["OK"], result["Message"]

    private_key = result["Value"]
    assert isinstance(private_key, RSAKey)

    # Sign token
    header["kid"] = private_key.thumbprint()

    # Find key by KID
    result = db.getPrivateKey(header["kid"])
    assert result["OK"], result["Message"]
    # as_dict has no arguments for authlib < 1.0.0
    # for authlib >= 1.0.0:
    assert result["Value"].as_dict(True) == private_key.as_dict(True)

    # Sign token
    token = jwt.encode(header, payload, private_key)
    # Sign auth code
    code = jws.serialize_compact(header, json_b64encode(code_payload),
                                 private_key)

    # Get public key set
    result = db.getKeySet()
    keyset = result["Value"]
    assert result["OK"], result["Message"]
    # as_dict has no arguments for authlib < 1.0.0
    # for authlib >= 1.0.0:
    assert bool([
        key for key in keyset.as_dict(True)["keys"]
        if key["kid"] == header["kid"]
    ])

    # Read token
    _payload = jwt.decode(token, JsonWebKey.import_key_set(keyset.as_dict()))
    assert _payload == payload
    # Read auth code
    data = jws.deserialize_compact(code, keyset.keys[0])
    _code_payload = json_loads(urlsafe_b64decode(data["payload"]))
    assert _code_payload == code_payload
Beispiel #11
0
async def get_jwks(url: str) -> KeySet:
    async with httpx.AsyncClient() as client:
        resp = await client.get(url)
        resp.raise_for_status()
        return JsonWebKey.import_key_set(resp.json())
Beispiel #12
0
 def load_key(header, payload):
     jwk_set = JsonWebKey.import_key_set(provider.fetch_jwk_set(force=True))
     return jwk_set.find_by_kid(header.get("kid"))
Beispiel #13
0
 def get_validate_key(self):
     with open(get_file_path('jwks_public.json'), 'r') as f:
         return JsonWebKey.import_key_set(json.load(f))
Beispiel #14
0
 def jwks(self):
     return JsonWebKey.import_key_set(self.jwks_data())