Exemple #1
0
    def parse_and_validate(
        cls, token: str, jwt_bundle: JwtBundle, audience: List[str]
    ) -> 'JwtSvid':
        """Parses and validates a JWT-SVID token and returns an instance of JwtSvid.

        The JWT-SVID signature is verified using the JWT bundle source.

        Args:
            token: A token as a string that is parsed and validated.
            jwt_bundle: An instance of JwtBundle that provides the JWT authorities to verify the signature.
            audience: A list of strings used to validate the 'aud' claim.

        Returns:
            An instance of JwtSvid with a SPIFFE ID parsed from the 'sub', audience from 'aud', and expiry
            from 'exp' claim.

        Raises:
            JwtSvidError:   When the token expired or the expiration claim is missing,
                            when the algorithm is not supported, when the header 'kid' is missing,
                            when the signature cannot be verified, or
                            when the 'aud' claim has an audience that is not in the audience list provided as parameter.
            ValueError:     When the token is blank or cannot be parsed.
            BundleNotFoundError:    If the bundle for the trust domain of the spiffe id from the 'sub'
                                    cannot be found the jwt_bundle_source.
            AuthorityNotFoundError: If the authority cannot be found in the bundle using the value from the 'kid' header.
            InvalidTokenError: In case token is malformed and fails to decode.
        """
        if not token:
            raise ValueError(INVALID_INPUT_ERROR.format('token cannot be empty'))

        if not jwt_bundle:
            raise ValueError(INVALID_INPUT_ERROR.format('jwt_bundle cannot be empty'))
        try:
            token_header = jwt.get_unverified_header(token)
            validator = JwtSvidValidator()
            validator.validate_header(token_header)
            signing_key = jwt_bundle.find_jwt_authority(token_header['kid'])
            claims = jwt.decode(
                token,
                algorithms=token_header['alg'],
                key=signing_key,
                audience=audience,
                options={
                    'verify_signature': True,
                    'verify_aud': True,
                    'verify_exp': True,
                },
            )
            # TODO:validate required claims
            spiffe_ID = SpiffeId.parse(claims['sub'])
            return JwtSvid(spiffe_ID, claims['aud'], claims['exp'], claims, token)
        except PyJWTError as err:
            raise InvalidTokenError(str(err))
Exemple #2
0
def test_invalid_input_validate_claims(test_input_claim, test_input_audience,
                                       expected):
    with pytest.raises(ArgumentError) as exception:
        JwtSvidValidator().validate_claims(test_input_claim,
                                           test_input_audience)

    assert str(exception.value) == expected
Exemple #3
0
def test_validate_claims_missing_required_claim(test_input_claim,
                                                test_input_audience, expected):
    with pytest.raises(MissingClaimError) as exception:
        JwtSvidValidator().validate_claims(test_input_claim,
                                           test_input_audience)

    assert str(exception.value) == expected
Exemple #4
0
    def parse_insecure(cls, token: str, expected_audience: List) -> 'JwtSvid':
        """Parses and validates a JWT-SVID token and returns an instance of a JwtSvid with a SPIFFE ID parsed from the 'sub', audience from 'aud',
        and expiry from 'exp' claim. The JWT-SVID signature is not verified.

        Args:
            token: A token as a string that is parsed and validated.
            audience: Audience as a list of strings used to validate the 'aud' claim.

        Returns:
            An instance of JwtSvid with a SPIFFE ID parsed from the 'sub', audience from 'aud', and expiry
            from 'exp' claim.

        Raises:
            ValueError: When the token is blank or cannot be parsed, or in case header is not specified or in case expected_audience is empty or
                if the SPIFFE ID in the 'sub' claim doesn't comply with the SPIFFE standard.
            InvalidAlgorithmError: In case specified 'alg' is not supported as specified by the SPIFFE standard.
            InvalidTypeError: If 'typ' is present in header but is not set to 'JWT' or 'JOSE'.
            InvalidClaimError: If a required claim ('exp', 'aud', 'sub') is not present in payload or expected_audience is not a subset of audience_claim.
            TokenExpiredError: If token is expired.
            InvalidTokenError: If token is malformed and fails to decode.
        """
        if not token:
            raise ValueError(INVALID_INPUT_ERROR.format('token cannot be empty'))
        try:
            token_header = jwt.get_unverified_header(token)
            validator = JwtSvidValidator()
            validator.validate_header(token_header)
            claims = jwt.decode(token, options={'verify_signature': False})
            validator.validate_claims(claims, expected_audience)
            spiffe_ID = SpiffeId.parse(claims['sub'])
            return JwtSvid(spiffe_ID, claims['aud'], claims['exp'], claims, token)
        except PyJWTError as err:
            raise InvalidTokenError(str(err))
Exemple #5
0
    def parse_and_validate(cls, token: str, jwt_bundle: JwtBundle,
                           audience: List[str]) -> 'JwtSvid':
        """Parses and validates a JWT-SVID token and returns an instance of JwtSvid.

        The JWT-SVID signature is verified using the JWT bundle source.

        Args:
            token: A token as a string that is parsed and validated.
            jwt_bundle: An instance of JwtBundle that provides the JWT authorities to verify the signature.
            audience: A list of strings used to validate the 'aud' claim.

        Returns:
            An instance of JwtSvid with a SPIFFE ID parsed from the 'sub', audience from 'aud', and expiry
            from 'exp' claim.

        Raises:
            JwtSvidError:   When the token expired or the expiration claim is missing,
                            when the algorithm is not supported, when the header 'kid' is missing,
                            when the signature cannot be verified, or
                            when the 'aud' claim has an audience that is not in the audience list provided as parameter.
            ArgumentError:     When the token is blank or cannot be parsed.
            BundleNotFoundError:    If the bundle for the trust domain of the spiffe id from the 'sub'
                                    cannot be found the jwt_bundle_source.
            AuthorityNotFoundError: If the authority cannot be found in the bundle using the value from the 'kid' header.
            InvalidTokenError: In case token is malformed and fails to decode.
        """
        if not token:
            raise ArgumentError(
                INVALID_INPUT_ERROR.format('token cannot be empty'))

        if not jwt_bundle:
            raise ArgumentError(
                INVALID_INPUT_ERROR.format('jwt_bundle cannot be empty'))
        try:
            header_params = jwt.get_unverified_header(token)
            validator = JwtSvidValidator()
            validator.validate_header(header_params)
            key_id = header_params.get('kid')
            signing_key = jwt_bundle.get_jwt_authority(key_id)
            if not signing_key:
                raise AuthorityNotFoundError(key_id)

            public_key_pem = signing_key.public_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PublicFormat.SubjectPublicKeyInfo,
            ).decode('UTF-8')

            claims = jwt.decode(
                token,
                algorithms=header_params.get('alg'),
                key=public_key_pem,
                audience=audience,
                options={
                    'verify_signature': True,
                    'verify_aud': True,
                    'verify_exp': True,
                },
            )

            spiffe_id = SpiffeId.parse(claims.get('sub', None))

            return JwtSvid(spiffe_id, claims['aud'], claims['exp'], claims,
                           token)
        except PyJWTError as err:
            raise InvalidTokenError(str(err))
        except ArgumentError as value_err:
            raise InvalidTokenError(str(value_err))
Exemple #6
0
def test_validate_header_valid_headers(test_input_header):
    JwtSvidValidator().validate_header(test_input_header)

    assert True
Exemple #7
0
def test_validate_header_invalid_type(test_input_header, expected):
    with pytest.raises(InvalidTypeError) as exception:
        JwtSvidValidator().validate_header(test_input_header)

    assert str(exception.value) == expected
Exemple #8
0
def test_validate_claims_valid_input(test_input_claim, test_input_audience):
    JwtSvidValidator().validate_claims(test_input_claim, test_input_audience)

    assert True
Exemple #9
0
def test_validate_claims_token_expired(test_input_claim, test_input_audience):
    with pytest.raises(TokenExpiredError) as exception:
        JwtSvidValidator().validate_claims(test_input_claim,
                                           test_input_audience)
    assert str(exception.value) == str(TokenExpiredError())