Repository: jpadilla/pyjwt

Issue:
jwt.decode returns Signature has expired
I expect the decoded result of JWT, but it returns an error `Signature has expired`

## Reproduction Steps
```python
  import jwt

  try:
      payload: Dict = jwt.decode(token_obj[1], SECRET_KEY, algorithms=['HS256'], verify=True)
  except Exception as err:
      return JsonResponse(data={
          'message': err,
          'success': False,
      }, status=401)
```

## System Information

    $ python -m jwt.help

```json

{
  "cryptography": {
    "version": ""
  },
  "implementation": {
    "name": "CPython",
    "version": "3.6.12"
  },
  "platform": {
    "release": "4.19.121-linuxkit",
    "system": "Linux"
  },
  "pyjwt": {
    "version": "2.0.0"
  }
}
```
jwt.jws_api.PyJWS.decode_complete shouldn't accept kwargs argument
Here we are using flask_jwt_extended. There is a call in it that calls jwt.api_jwt.PyJWT.decode with a now disapeared parameter. The problem is that jwt.api_jwt.PyJWT.decode accepts any named parameters, and forwards them to jwt.jws_api.PyJWS.decode_complete. So a call that should fail is working but doing the wrong thing.

Moreover kwargs in jwt.jws_api.PyJWS.decode_complete isn't used at all. It looks suspicious.

## Expected Result

If an API caller calls a function with bad parameter, an exception is raised immediately. **kwargs shouldn't be used as garbage parameters collection.

## Actual Result

An exception is raised a few line later, masking the real problem of the wrong usage of the API

## Reproduction Steps

```python
import jwt
# does not raise even if prout has never been a valid parameter.
unverified_claims = jwt.decode(
        encoded_token, verify=False, prout="bar",
    )

```


Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f122fe7..1c1c451 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -10,6 +10,8 @@ This project adheres to `Semantic Versioning <https://semver.org/>`__.
 Changed
 ~~~~~~~
 
+- Remove arbitrary kwalgs. `#657 <https://github.com/jpadilla/pyjwt/pull/657>`__
+
 Fixed
 ~~~~~
 
diff --git a/jwt/api_jws.py b/jwt/api_jws.py
index 3a16294..8061c97 100644
--- a/jwt/api_jws.py
+++ b/jwt/api_jws.py
@@ -137,7 +137,6 @@ class PyJWS:
         key: str = "",
         algorithms: List[str] = None,
         options: Dict = None,
-        **kwargs,
     ) -> Dict[str, Any]:
         if options is None:
             options = {}
@@ -166,9 +165,8 @@ class PyJWS:
         key: str = "",
         algorithms: List[str] = None,
         options: Dict = None,
-        **kwargs,
     ) -> str:
-        decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
+        decoded = self.decode_complete(jwt, key, algorithms, options)
         return decoded["payload"]
 
     def get_unverified_header(self, jwt):
diff --git a/jwt/api_jwt.py b/jwt/api_jwt.py
index 48a9316..c5fbbc5 100644
--- a/jwt/api_jwt.py
+++ b/jwt/api_jwt.py
@@ -68,7 +68,9 @@ class PyJWT:
         key: str = "",
         algorithms: List[str] = None,
         options: Dict = None,
-        **kwargs,
+        audience: Optional[Union[str, List[str]]] = None,
+        issuer: Optional[str] = None,
+        leeway: Union[float, timedelta] = 0,
     ) -> Dict[str, Any]:
         if options is None:
             options = {"verify_signature": True}
@@ -92,7 +94,6 @@ class PyJWT:
             key=key,
             algorithms=algorithms,
             options=options,
-            **kwargs,
         )
 
         try:
@@ -103,7 +104,7 @@ class PyJWT:
             raise DecodeError("Invalid payload string: must be a json object")
 
         merged_options = {**self.options, **options}
-        self._validate_claims(payload, merged_options, **kwargs)
+        self._validate_claims(payload, merged_options, audience, issuer, leeway)
 
         decoded["payload"] = payload
         return decoded
@@ -114,18 +115,20 @@ class PyJWT:
         key: str = "",
         algorithms: List[str] = None,
         options: Dict = None,
-        **kwargs,
+        audience: Optional[Union[str, List[str]]] = None,
+        issuer: Optional[str] = None,
+        leeway: Union[float, timedelta] = 0,
     ) -> Dict[str, Any]:
-        decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
+        decoded = self.decode_complete(
+            jwt, key, algorithms, options, audience, issuer, leeway
+        )
         return decoded["payload"]
 
-    def _validate_claims(
-        self, payload, options, audience=None, issuer=None, leeway=0, **kwargs
-    ):
+    def _validate_claims(self, payload, options, audience, issuer, leeway):
         if isinstance(leeway, timedelta):
             leeway = leeway.total_seconds()
 
-        if not isinstance(audience, (bytes, str, type(None), Iterable)):
+        if not isinstance(audience, (str, type(None), Iterable)):
             raise TypeError("audience must be a string, iterable, or None")
 
         self._validate_required_claims(payload, options)


Test patch:
diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py
index a6230b3..3f274a3 100644
--- a/tests/test_api_jwt.py
+++ b/tests/test_api_jwt.py
@@ -106,6 +106,17 @@ class TestJWT:
         exception = context.value
         assert str(exception) == "Invalid payload string: must be a json object"
 
+    def test_decode_with_unknown_parameter_throws_exception(self, jwt):
+        secret = "secret"
+        example_jwt = (
+            b"eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9"
+            b".eyJoZWxsbyI6ICJ3b3JsZCJ9"
+            b".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8"
+        )
+
+        with pytest.raises(TypeError):
+            jwt.decode(example_jwt, key=secret, foo="bar", algorithms=["HS256"])
+
     def test_decode_with_invalid_audience_param_throws_exception(self, jwt):
         secret = "secret"
         example_jwt = (