diff --git a/CHANGELOG.md b/CHANGELOG.md index 5793d708..68eb2acf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Audience parameter now supports iterables [#205][205] +- An invalid signature now raises an `InvalidSignatureError` instead of `DecodeError` [#315][315] + ### Fixed ### Added @@ -204,4 +206,5 @@ rarely used. Users affected by this should upgrade to 3.3+. [271]: https://github.com/jpadilla/pyjwt/pull/271 [277]: https://github.com/jpadilla/pyjwt/pull/277 [281]: https://github.com/jpadilla/pyjwt/pull/281 +[315]: https://github.com/jpadilla/pyjwt/pull/315 [7c1e61d]: https://github.com/jpadilla/pyjwt/commit/7c1e61dde27bafe16e7d1bb6e35199e778962742 diff --git a/docs/api.rst b/docs/api.rst index 4bb4d3b4..08b69915 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -19,6 +19,11 @@ Exceptions Raised when a token cannot be decoded because it failed validation +.. class:: InvalidSignatureError + + Raised when a token's signature doesn't match the one provided as part of + the token. + .. class:: ExpiredSignatureError Raised when a token's ``exp`` claim indicates that it has expired diff --git a/jwt/api_jws.py b/jwt/api_jws.py index ad3ff6ae..b796fa76 100644 --- a/jwt/api_jws.py +++ b/jwt/api_jws.py @@ -7,7 +7,10 @@ Algorithm, get_default_algorithms, has_crypto, requires_cryptography # NOQA ) from .compat import binary_type, string_types, text_type -from .exceptions import DecodeError, InvalidAlgorithmError, InvalidTokenError +from .exceptions import ( + DecodeError, InvalidAlgorithmError, InvalidSignatureError, + InvalidTokenError +) from .utils import base64url_decode, base64url_encode, force_bytes, merge_dict @@ -203,7 +206,7 @@ def _verify_signature(self, payload, signing_input, header, signature, key = alg_obj.prepare_key(key) if not alg_obj.verify(signing_input, key, signature): - raise DecodeError('Signature verification failed') + raise InvalidSignatureError('Signature verification failed') except KeyError: raise InvalidAlgorithmError('Algorithm not supported') diff --git a/jwt/exceptions.py b/jwt/exceptions.py index 31177a0a..b396bf1f 100644 --- a/jwt/exceptions.py +++ b/jwt/exceptions.py @@ -6,6 +6,10 @@ class DecodeError(InvalidTokenError): pass +class InvalidSignatureError(DecodeError): + pass + + class ExpiredSignatureError(InvalidTokenError): pass diff --git a/tests/test_api_jws.py b/tests/test_api_jws.py index 60671a29..4bf8d398 100644 --- a/tests/test_api_jws.py +++ b/tests/test_api_jws.py @@ -5,7 +5,8 @@ from jwt.algorithms import Algorithm from jwt.api_jws import PyJWS from jwt.exceptions import ( - DecodeError, InvalidAlgorithmError, InvalidTokenError + DecodeError, InvalidAlgorithmError, InvalidSignatureError, + InvalidTokenError ) from jwt.utils import base64url_decode, force_bytes, force_unicode @@ -178,8 +179,14 @@ def test_bad_secret(self, jws, payload): bad_secret = 'bar' jws_message = jws.encode(payload, right_secret) - with pytest.raises(DecodeError): + with pytest.raises(DecodeError) as excinfo: + # Backward compat for ticket #315 + jws.decode(jws_message, bad_secret) + assert 'Signature verification failed' == str(excinfo.value) + + with pytest.raises(InvalidSignatureError) as excinfo: jws.decode(jws_message, bad_secret) + assert 'Signature verification failed' == str(excinfo.value) def test_decodes_valid_jws(self, jws, payload): example_secret = 'secret'