Repository: jpadilla/pyjwt

Issue:
jwt.decode verify_signature=False and verify_exp=True doesn't work properly
When trying to decode an expired token to see if it needs a refresh, the expected exception doesn't raise.

## Expected Result

I expect the following code to  raise an `ExpiredSignatureError`

## Actual Result

However it doesn't raise.

## Reproduction Steps

```python
import jwt

audience = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
token = ( 
       "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiJ9.eyJpc3MiOiAicHJvbWlzaW5nLWxpZmUtMjE3NTEzQGFw"
        "cHNwb3QuZ3NlcnZpY2VhY2NvdW50LmNvbSIsICJzdWIiOiAicHJvbWlzaW5nLWxpZmUtMjE3NTEzQGFwcHNwb"
        "3QuZ3NlcnZpY2VhY2NvdW50LmNvbSIsICJhdWQiOiAiaHR0cHM6Ly9pZGVudGl0eXRvb2xraXQuZ29vZ2xlYX"
        "Bpcy5jb20vZ29vZ2xlLmlkZW50aXR5LmlkZW50aXR5dG9vbGtpdC52MS5JZGVudGl0eVRvb2xraXQiLCAidWl"
        "kIjogInVzZXJfMTFrUWQxbzRpYUxpUTc3WHNzNmtraTJvQU9JV282WDhpZSIsICJpYXQiOiAxNjAyNjYwOTkw"
        "LCAiZXhwIjogMTYwMjY2NDU5MCwgImNsYWltcyI6IHsibWVyY2hhbnRfaWQiOiAibWVyY2hhbnRfMTFrUWNkd"
        "DcycDR2NkRVUElVNjIwd3VnRVd1ODQySjNRYSJ9fQ.dOsXrwH23rJmGunQz7_o3M-AEHlUkLt_orjSn9GINkk"
        "r6y-3odlguPBW-9Yegn9cteRn3Q04CYxQYqjvFne8CKy8wdh9yj2vCTDkxLhGg5qzso2r93R-2g16Cbkm0-Wl"
        "MJV8cvjIl0IURy1Q2YLNmZEJlwm5PDbforncXtum9LXtBNXqE2vMKaZtwVIRXoyWVorz8BPKT9XV31yeaTfD9"
        "kC9-4TP2XoXa82G-NxVQou9EACfiQwF2HpEm-K02R0mfEp67WgAmmNuqoqlZqvxzbw95J0-vgk89sOxXpAW3JG"
        "qYKWuW_OnrnnNsOZKrX4pD2Rl5RTll_z-qx0tvnNmKw"
)

try:
    jwt.decode(
        token,
        algorithms=["RS256"],
        audience=audience,
        options={"verify_signature": False, "verify_exp": True},
    )
except jwt.ExpiredSignatureError:
    print("Test pass")
else:
    print("Test failed")
```

## System Information

    $ python -m jwt.help

```json
{
  "cryptography": {
    "version": "3.3.1"
  },
  "implementation": {
    "name": "CPython",
    "version": "3.8.6"
  },
  "platform": {
    "release": "5.8.0-33-generic",
    "system": "Linux"
  },
  "pyjwt": {
    "version": "2.0.0"
  }
}
```

Discussion:
Natim: @jpadilla what do you think?

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1fb0890..4a5b5ee 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -6,7 +6,7 @@ repos:
         args: ["--target-version=py36"]
 
   - repo: https://github.com/asottile/blacken-docs
-    rev: v1.9.1
+    rev: v1.9.2
     hooks:
       - id: blacken-docs
         args: ["--target-version=py36"]
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4712e2e..77467d6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -10,6 +10,8 @@ This project adheres to `Semantic Versioning <https://semver.org/>`__.
 Changed
 ~~~~~~~
 
+- Allow claims validation without making JWT signature validation mandatory. `#608 <https://github.com/jpadilla/pyjwt/pull/608>`__
+
 Fixed
 ~~~~~
 
diff --git a/jwt/api_jwt.py b/jwt/api_jwt.py
index 80b33bf..70a5e53 100644
--- a/jwt/api_jwt.py
+++ b/jwt/api_jwt.py
@@ -75,6 +75,13 @@ class PyJWT:
         else:
             options.setdefault("verify_signature", True)
 
+        if not options["verify_signature"]:
+            options.setdefault("verify_exp", False)
+            options.setdefault("verify_nbf", False)
+            options.setdefault("verify_iat", False)
+            options.setdefault("verify_aud", False)
+            options.setdefault("verify_iss", False)
+
         if options["verify_signature"] and not algorithms:
             raise DecodeError(
                 'It is required that you pass in a value for the "algorithms" argument when calling decode().'
@@ -95,9 +102,8 @@ class PyJWT:
         if not isinstance(payload, dict):
             raise DecodeError("Invalid payload string: must be a json object")
 
-        if options["verify_signature"]:
-            merged_options = {**self.options, **options}
-            self._validate_claims(payload, merged_options, **kwargs)
+        merged_options = {**self.options, **options}
+        self._validate_claims(payload, merged_options, **kwargs)
 
         decoded["payload"] = payload
         return decoded


Test patch:
diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py
index f161763..05cb714 100644
--- a/tests/test_api_jwt.py
+++ b/tests/test_api_jwt.py
@@ -579,6 +579,22 @@ class TestJWT:
                 options={"verify_exp": True},
             )
 
+    def test_decode_with_verify_exp_option_and_signature_off(self, jwt, payload):
+        payload["exp"] = utc_timestamp() - 1
+        secret = "secret"
+        jwt_message = jwt.encode(payload, secret)
+
+        jwt.decode(
+            jwt_message,
+            options={"verify_signature": False},
+        )
+
+        with pytest.raises(ExpiredSignatureError):
+            jwt.decode(
+                jwt_message,
+                options={"verify_signature": False, "verify_exp": True},
+            )
+
     def test_decode_with_optional_algorithms(self, jwt, payload):
         secret = "secret"
         jwt_message = jwt.encode(payload, secret)