Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-PyJWT for openSUSE:Factory 
checked in at 2021-05-20 19:23:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-PyJWT (Old)
 and      /work/SRC/openSUSE:Factory/.python-PyJWT.new.2988 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-PyJWT"

Thu May 20 19:23:28 2021 rev:22 rq:894171 version:2.1.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-PyJWT/python-PyJWT.changes        
2021-02-04 20:23:57.710815999 +0100
+++ /work/SRC/openSUSE:Factory/.python-PyJWT.new.2988/python-PyJWT.changes      
2021-05-20 19:23:53.862232039 +0200
@@ -1,0 +2,16 @@
+Tue May 18 22:19:50 UTC 2021 - Dirk M??ller <dmuel...@suse.com>
+
+- update to 2.1.0:
+  - Allow claims validation without making JWT signature validation mandatory. 
`
+  - Remove padding from JWK test data. `
+  - Make `kty` mandatory in JWK to be compliant with RFC7517. `
+  - Allow JWK without `alg` to be compliant with RFC7517. `
+  - Allow to verify with private key on ECAlgorithm, as well as on 
Ed25519Algorithm. `
+  - Add caching by default to PyJWKClient `
+  - Add missing exceptions.InvalidKeyError to jwt module __init__ imports `
+  - Add support for ES256K algorithm `
+  - Add `from_jwk()` to Ed25519Algorithm `
+  - Add `to_jwk()` to Ed25519Algorithm `
+  - Export `PyJWK` and `PyJWKSet`
+
+-------------------------------------------------------------------

Old:
----
  PyJWT-2.0.1.tar.gz

New:
----
  PyJWT-2.1.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-PyJWT.spec ++++++
--- /var/tmp/diff_new_pack.RzRDVB/_old  2021-05-20 19:23:54.254230380 +0200
+++ /var/tmp/diff_new_pack.RzRDVB/_new  2021-05-20 19:23:54.258230364 +0200
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %global skip_python2 1
 Name:           python-PyJWT
-Version:        2.0.1
+Version:        2.1.0
 Release:        0
 Summary:        JSON Web Token implementation in Python
 License:        MIT
@@ -34,7 +34,7 @@
 Requires:       python-cryptography >= 3.3.1
 Requires:       python-setuptools
 Requires(post): update-alternatives
-Requires(postun): update-alternatives
+Requires(postun):update-alternatives
 BuildArch:      noarch
 %python_subpackages
 

++++++ PyJWT-2.0.1.tar.gz -> PyJWT-2.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/.pre-commit-config.yaml 
new/PyJWT-2.1.0/.pre-commit-config.yaml
--- old/PyJWT-2.0.1/.pre-commit-config.yaml     2021-01-06 03:19:08.000000000 
+0100
+++ new/PyJWT-2.1.0/.pre-commit-config.yaml     2021-04-28 13:47:41.000000000 
+0200
@@ -1,24 +1,24 @@
 repos:
   - repo: https://github.com/psf/black
-    rev: 20.8b1
+    rev: 21.4b0
     hooks:
       - id: black
         args: ["--target-version=py36"]
 
   - repo: https://github.com/asottile/blacken-docs
-    rev: v1.9.1
+    rev: v1.10.0
     hooks:
       - id: blacken-docs
         args: ["--target-version=py36"]
 
-  - repo: https://gitlab.com/pycqa/flake8
-    rev: 3.8.4
+  - repo: https://github.com/PyCQA/flake8
+    rev: 3.9.1
     hooks:
       - id: flake8
         language_version: python3.8
 
   - repo: https://github.com/PyCQA/isort
-    rev: 5.7.0
+    rev: 5.8.0
     hooks:
       - id: isort
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/CHANGELOG.rst 
new/PyJWT-2.1.0/CHANGELOG.rst
--- old/PyJWT-2.0.1/CHANGELOG.rst       2021-01-17 18:56:23.000000000 +0100
+++ new/PyJWT-2.1.0/CHANGELOG.rst       2021-04-28 13:59:20.000000000 +0200
@@ -4,7 +4,7 @@
 All notable changes to this project will be documented in this file.
 This project adheres to `Semantic Versioning <https://semver.org/>`__.
 
-`Unreleased <https://github.com/jpadilla/pyjwt/compare/2.0.0...HEAD>`__
+`Unreleased <https://github.com/jpadilla/pyjwt/compare/2.1.0...HEAD>`__
 -----------------------------------------------------------------------
 
 Changed
@@ -16,6 +16,32 @@
 Added
 ~~~~~
 
+`v2.1.0 <https://github.com/jpadilla/pyjwt/compare/2.0.1...2.1.0>`__
+--------------------------------------------------------------------
+
+Changed
+~~~~~~~
+
+- Allow claims validation without making JWT signature validation mandatory. 
`#608 <https://github.com/jpadilla/pyjwt/pull/608>`__
+
+Fixed
+~~~~~
+
+- Remove padding from JWK test data. `#628 
<https://github.com/jpadilla/pyjwt/pull/628>`__
+- Make `kty` mandatory in JWK to be compliant with RFC7517. `#624 
<https://github.com/jpadilla/pyjwt/pull/624>`__
+- Allow JWK without `alg` to be compliant with RFC7517. `#624 
<https://github.com/jpadilla/pyjwt/pull/624>`__
+- Allow to verify with private key on ECAlgorithm, as well as on 
Ed25519Algorithm. `#645 <https://github.com/jpadilla/pyjwt/pull/645>`__
+
+Added
+~~~~~
+
+- Add caching by default to PyJWKClient `#611 
<https://github.com/jpadilla/pyjwt/pull/611>`__
+- Add missing exceptions.InvalidKeyError to jwt module __init__ imports `#620 
<https://github.com/jpadilla/pyjwt/pull/620>`__
+- Add support for ES256K algorithm `#629 
<https://github.com/jpadilla/pyjwt/pull/629>`__
+- Add `from_jwk()` to Ed25519Algorithm `#621 
<https://github.com/jpadilla/pyjwt/pull/621>`__
+- Add `to_jwk()` to Ed25519Algorithm `#643 
<https://github.com/jpadilla/pyjwt/pull/643>`__
+- Export `PyJWK` and `PyJWKSet` `#652 
<https://github.com/jpadilla/pyjwt/pull/652>`__
+
 `v2.0.1 <https://github.com/jpadilla/pyjwt/compare/2.0.0...2.0.1>`__
 --------------------------------------------------------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/PKG-INFO new/PyJWT-2.1.0/PKG-INFO
--- old/PyJWT-2.0.1/PKG-INFO    2021-01-17 18:56:59.065547200 +0100
+++ new/PyJWT-2.1.0/PKG-INFO    2021-04-28 14:01:11.608931300 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: PyJWT
-Version: 2.0.1
+Version: 2.1.0
 Summary: JSON Web Token implementation in Python
 Home-page: https://github.com/jpadilla/pyjwt
 Author: Jose Padilla
@@ -86,6 +86,6 @@
 Requires-Python: >=3.6
 Description-Content-Type: text/x-rst
 Provides-Extra: crypto
-Provides-Extra: tests
 Provides-Extra: dev
+Provides-Extra: tests
 Provides-Extra: docs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/PyJWT.egg-info/PKG-INFO 
new/PyJWT-2.1.0/PyJWT.egg-info/PKG-INFO
--- old/PyJWT-2.0.1/PyJWT.egg-info/PKG-INFO     2021-01-17 18:56:58.000000000 
+0100
+++ new/PyJWT-2.1.0/PyJWT.egg-info/PKG-INFO     2021-04-28 14:01:11.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: PyJWT
-Version: 2.0.1
+Version: 2.1.0
 Summary: JSON Web Token implementation in Python
 Home-page: https://github.com/jpadilla/pyjwt
 Author: Jose Padilla
@@ -86,6 +86,6 @@
 Requires-Python: >=3.6
 Description-Content-Type: text/x-rst
 Provides-Extra: crypto
-Provides-Extra: tests
 Provides-Extra: dev
+Provides-Extra: tests
 Provides-Extra: docs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/PyJWT.egg-info/SOURCES.txt 
new/PyJWT-2.1.0/PyJWT.egg-info/SOURCES.txt
--- old/PyJWT-2.0.1/PyJWT.egg-info/SOURCES.txt  2021-01-17 18:56:58.000000000 
+0100
+++ new/PyJWT-2.1.0/PyJWT.egg-info/SOURCES.txt  2021-04-28 14:01:11.000000000 
+0200
@@ -5,7 +5,6 @@
 LICENSE
 MANIFEST.in
 README.rst
-pyproject.toml
 setup.cfg
 setup.py
 tox.ini
@@ -50,10 +49,14 @@
 tests/keys/jwk_ec_key_P-256.json
 tests/keys/jwk_ec_key_P-384.json
 tests/keys/jwk_ec_key_P-521.json
+tests/keys/jwk_ec_key_secp256k1.json
 tests/keys/jwk_ec_pub_P-256.json
 tests/keys/jwk_ec_pub_P-384.json
 tests/keys/jwk_ec_pub_P-521.json
+tests/keys/jwk_ec_pub_secp256k1.json
 tests/keys/jwk_hmac.json
+tests/keys/jwk_okp_key_Ed25519.json
+tests/keys/jwk_okp_pub_Ed25519.json
 tests/keys/jwk_rsa_key.json
 tests/keys/jwk_rsa_pub.json
 tests/keys/testkey2_rsa.pub.pem
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/docs/algorithms.rst 
new/PyJWT-2.1.0/docs/algorithms.rst
--- old/PyJWT-2.0.1/docs/algorithms.rst 2020-12-20 01:01:37.000000000 +0100
+++ new/PyJWT-2.1.0/docs/algorithms.rst 2021-04-28 13:43:46.000000000 +0200
@@ -8,6 +8,7 @@
 * HS384 - HMAC using SHA-384 hash algorithm
 * HS512 - HMAC using SHA-512 hash algorithm
 * ES256 - ECDSA signature algorithm using SHA-256 hash algorithm
+* ES256K - ECDSA signature algorithm with secp256k1 curve using SHA-256 hash 
algorithm
 * ES384 - ECDSA signature algorithm using SHA-384 hash algorithm
 * ES512 - ECDSA signature algorithm using SHA-512 hash algorithm
 * RS256 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-256 hash algorithm
@@ -16,7 +17,7 @@
 * PS256 - RSASSA-PSS signature using SHA-256 and MGF1 padding with SHA-256
 * PS384 - RSASSA-PSS signature using SHA-384 and MGF1 padding with SHA-384
 * PS512 - RSASSA-PSS signature using SHA-512 and MGF1 padding with SHA-512
-* EdDSA - Ed25519255 signature using SHA-512. Provides 128-bit security
+* EdDSA - Ed25519 signature using SHA-512. Provides 128-bit security
 
 Asymmetric (Public-key) Algorithms
 ----------------------------------
@@ -56,3 +57,15 @@
 
 In the above case, if the JWT has any value for its alg header other than
 HS512 or HS256, the claim will be rejected with an ``InvalidAlgorithmError``.
+
+.. warning::
+
+   Do **not** compute the ``algorithms`` parameter based on the
+   ``alg`` from the token itself, or on any other data that an
+   attacker may be able to influence, as that might expose you to
+   various vulnerabilities (see `RFC 8725 ??2.1
+   <https://www.rfc-editor.org/rfc/rfc8725.html#section-2.1>`_). Instead,
+   either hard-code a fixed value for ``algorithms``, or configure it
+   in the same place you configure the ``key``. Make sure not to mix
+   symmetric and asymmetric algorithms that interpret the ``key`` in
+   different ways (e.g. HS\* and RS\*).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/docs/api.rst new/PyJWT-2.1.0/docs/api.rst
--- old/PyJWT-2.0.1/docs/api.rst        2020-12-21 17:55:46.000000000 +0100
+++ new/PyJWT-2.1.0/docs/api.rst        2021-04-28 13:43:48.000000000 +0200
@@ -28,19 +28,33 @@
 
     :param list algorithms: allowed algorithms, e.g. ``["ES256"]``
 
-        .. note:: It is highly recommended to specify the expected 
``algorithms``.
+        .. warning::
 
-        .. note:: It is insecure to mix symmetric and asymmetric algorithms 
because they require different kinds of keys.
+           Do **not** compute the ``algorithms`` parameter based on
+           the ``alg`` from the token itself, or on any other data
+           that an attacker may be able to influence, as that might
+           expose you to various vulnerabilities (see `RFC 8725 ??2.1
+           <https://www.rfc-editor.org/rfc/rfc8725.html#section-2.1>`_). 
Instead,
+           either hard-code a fixed value for ``algorithms``, or
+           configure it in the same place you configure the
+           ``key``. Make sure not to mix symmetric and asymmetric
+           algorithms that interpret the ``key`` in different ways
+           (e.g. HS\* and RS\*).
 
     :param dict options: extended decoding and validation options
 
-        * ``require_exp=False`` check that ``exp`` (expiration) claim is 
present
-        * ``require_iat=False`` check that ``iat`` (issued at) claim is present
-        * ``require_nbf=False`` check that ``nbf`` (not before) claim is 
present
-        * ``verify_aud=False`` check that ``aud`` (audience) claim matches 
``audience``
-        * ``verify_iat=False`` check that ``iat`` (issued at) claim value is 
an integer
-        * ``verify_exp=False`` check that ``exp`` (expiration) claim value is 
OK
-        * ``verify_iss=False`` check that ``iss`` (issuer) claim matches 
``issuer``
+        * ``require=[]`` list of claims that must be present. E.g. 
``require=["exp", "iat", "nbf"]``.
+            Only verifies that the claims exists. Does NOT verify that the 
claims are valid.
+        * ``verify_aud=True`` but will be ignored if ``verify_signature`` is 
``False``.
+            Check that ``aud`` (audience) claim matches ``audience``
+        * ``verify_iat=True`` but will be ignored if ``verify_signature`` is 
``False``.
+            Check that ``iat`` (issued at) claim value is an integer
+        * ``verify_exp=True`` but will be ignored if ``verify_signature`` is 
``False``.
+            Check that ``exp`` (expiration) claim value is OK
+        * ``verify_iss=True`` but will be ignored if ``verify_signature`` is 
``False``.
+            Check that ``iss`` (issuer) claim matches ``issuer``
+        * ``verify_nbf=True`` but will be ignored if ``verify_signature`` is 
``False``.
+            Check that ``nbf`` (not before) is in the past
         * ``verify_signature=True`` verify the JWT cryptographic signature
 
     :param Iterable audience: optional, the value for ``verify_aud`` check
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/docs/usage.rst 
new/PyJWT-2.1.0/docs/usage.rst
--- old/PyJWT-2.0.1/docs/usage.rst      2020-12-22 14:41:00.000000000 +0100
+++ new/PyJWT-2.1.0/docs/usage.rst      2021-04-28 13:23:40.000000000 +0200
@@ -262,7 +262,7 @@
 Requiring Presence of Claims
 ----------------------------
 
-If you wish to require one or more claims to be present in the claimset, you 
can set the ``require`` paramenter to include these claims.
+If you wish to require one or more claims to be present in the claimset, you 
can set the ``require`` parameter to include these claims.
 
 .. code-block:: pycon
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/jwt/__init__.py 
new/PyJWT-2.1.0/jwt/__init__.py
--- old/PyJWT-2.0.1/jwt/__init__.py     2021-01-17 18:56:23.000000000 +0100
+++ new/PyJWT-2.1.0/jwt/__init__.py     2021-04-28 13:59:20.000000000 +0200
@@ -1,3 +1,4 @@
+from .api_jwk import PyJWK, PyJWKSet
 from .api_jws import (
     PyJWS,
     get_unverified_header,
@@ -13,6 +14,7 @@
     InvalidAudienceError,
     InvalidIssuedAtError,
     InvalidIssuerError,
+    InvalidKeyError,
     InvalidSignatureError,
     InvalidTokenError,
     MissingRequiredClaimError,
@@ -23,7 +25,7 @@
 )
 from .jwks_client import PyJWKClient
 
-__version__ = "2.0.1"
+__version__ = "2.1.0"
 
 __title__ = "PyJWT"
 __description__ = "JSON Web Token implementation in Python"
@@ -42,6 +44,8 @@
     "PyJWS",
     "PyJWT",
     "PyJWKClient",
+    "PyJWK",
+    "PyJWKSet",
     "decode",
     "encode",
     "get_unverified_header",
@@ -55,6 +59,7 @@
     "InvalidAudienceError",
     "InvalidIssuedAtError",
     "InvalidIssuerError",
+    "InvalidKeyError",
     "InvalidSignatureError",
     "InvalidTokenError",
     "MissingRequiredClaimError",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/jwt/algorithms.py 
new/PyJWT-2.1.0/jwt/algorithms.py
--- old/PyJWT-2.0.1/jwt/algorithms.py   2021-01-12 14:01:47.000000000 +0100
+++ new/PyJWT-2.1.0/jwt/algorithms.py   2021-04-28 13:43:48.000000000 +0200
@@ -37,6 +37,10 @@
         rsa_recover_prime_factors,
     )
     from cryptography.hazmat.primitives.serialization import (
+        Encoding,
+        NoEncryption,
+        PrivateFormat,
+        PublicFormat,
         load_pem_private_key,
         load_pem_public_key,
         load_ssh_public_key,
@@ -51,6 +55,7 @@
     "RS384",
     "RS512",
     "ES256",
+    "ES256K",
     "ES384",
     "ES521",
     "ES512",
@@ -79,6 +84,7 @@
                 "RS384": RSAAlgorithm(RSAAlgorithm.SHA384),
                 "RS512": RSAAlgorithm(RSAAlgorithm.SHA512),
                 "ES256": ECAlgorithm(ECAlgorithm.SHA256),
+                "ES256K": ECAlgorithm(ECAlgorithm.SHA256),
                 "ES384": ECAlgorithm(ECAlgorithm.SHA384),
                 "ES521": ECAlgorithm(ECAlgorithm.SHA512),
                 "ES512": ECAlgorithm(
@@ -425,6 +431,8 @@
                 return False
 
             try:
+                if isinstance(key, EllipticCurvePrivateKey):
+                    key = key.public_key()
                 key.verify(der_sig, msg, ec.ECDSA(self.hash_alg()))
                 return True
             except InvalidSignature:
@@ -467,6 +475,13 @@
                     curve_obj = ec.SECP521R1()
                 else:
                     raise InvalidKeyError("Coords should be 66 bytes for curve 
P-521")
+            elif curve == "secp256k1":
+                if len(x) == len(y) == 32:
+                    curve_obj = ec.SECP256K1()
+                else:
+                    raise InvalidKeyError(
+                        "Coords should be 32 bytes for curve secp256k1"
+                    )
             else:
                 raise InvalidKeyError(f"Invalid curve: {curve}")
 
@@ -577,3 +592,73 @@
                 return True  # If no exception was raised, the signature is 
valid.
             except cryptography.exceptions.InvalidSignature:
                 return False
+
+        @staticmethod
+        def to_jwk(key):
+            if isinstance(key, Ed25519PublicKey):
+                x = key.public_bytes(
+                    encoding=Encoding.Raw,
+                    format=PublicFormat.Raw,
+                )
+
+                return json.dumps(
+                    {
+                        "x": base64url_encode(force_bytes(x)).decode(),
+                        "kty": "OKP",
+                        "crv": "Ed25519",
+                    }
+                )
+
+            if isinstance(key, Ed25519PrivateKey):
+                d = key.private_bytes(
+                    encoding=Encoding.Raw,
+                    format=PrivateFormat.Raw,
+                    encryption_algorithm=NoEncryption(),
+                )
+
+                x = key.public_key().public_bytes(
+                    encoding=Encoding.Raw,
+                    format=PublicFormat.Raw,
+                )
+
+                return json.dumps(
+                    {
+                        "x": base64url_encode(force_bytes(x)).decode(),
+                        "d": base64url_encode(force_bytes(d)).decode(),
+                        "kty": "OKP",
+                        "crv": "Ed25519",
+                    }
+                )
+
+            raise InvalidKeyError("Not a public or private key")
+
+        @staticmethod
+        def from_jwk(jwk):
+            try:
+                if isinstance(jwk, str):
+                    obj = json.loads(jwk)
+                elif isinstance(jwk, dict):
+                    obj = jwk
+                else:
+                    raise ValueError
+            except ValueError:
+                raise InvalidKeyError("Key is not valid JSON")
+
+            if obj.get("kty") != "OKP":
+                raise InvalidKeyError("Not an Octet Key Pair")
+
+            curve = obj.get("crv")
+            if curve != "Ed25519":
+                raise InvalidKeyError(f"Invalid curve: {curve}")
+
+            if "x" not in obj:
+                raise InvalidKeyError('OKP should have "x" parameter')
+            x = base64url_decode(obj.get("x"))
+
+            try:
+                if "d" not in obj:
+                    return Ed25519PublicKey.from_public_bytes(x)
+                d = base64url_decode(obj.get("d"))
+                return Ed25519PrivateKey.from_private_bytes(d)
+            except ValueError as err:
+                raise InvalidKeyError("Invalid key parameter") from err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/jwt/api_jwk.py 
new/PyJWT-2.1.0/jwt/api_jwk.py
--- old/PyJWT-2.0.1/jwt/api_jwk.py      2020-12-21 17:55:46.000000000 +0100
+++ new/PyJWT-2.1.0/jwt/api_jwk.py      2021-04-28 13:23:40.000000000 +0200
@@ -1,7 +1,7 @@
 import json
 
 from .algorithms import get_default_algorithms
-from .exceptions import PyJWKError, PyJWKSetError
+from .exceptions import InvalidKeyError, PyJWKError, PyJWKSetError
 
 
 class PyJWK:
@@ -9,11 +9,40 @@
         self._algorithms = get_default_algorithms()
         self._jwk_data = jwk_data
 
+        kty = self._jwk_data.get("kty", None)
+        if not kty:
+            raise InvalidKeyError("kty is not found: %s" % self._jwk_data)
+
         if not algorithm and isinstance(self._jwk_data, dict):
             algorithm = self._jwk_data.get("alg", None)
 
         if not algorithm:
-            raise PyJWKError("Unable to find a algorithm for key: %s" % 
self._jwk_data)
+            # Determine alg with kty (and crv).
+            crv = self._jwk_data.get("crv", None)
+            if kty == "EC":
+                if crv == "P-256" or not crv:
+                    algorithm = "ES256"
+                elif crv == "P-384":
+                    algorithm = "ES384"
+                elif crv == "P-521":
+                    algorithm = "ES512"
+                elif crv == "secp256k1":
+                    algorithm = "ES256K"
+                else:
+                    raise InvalidKeyError("Unsupported crv: %s" % crv)
+            elif kty == "RSA":
+                algorithm = "RS256"
+            elif kty == "oct":
+                algorithm = "HS256"
+            elif kty == "OKP":
+                if not crv:
+                    raise InvalidKeyError("crv is not found: %s" % 
self._jwk_data)
+                if crv == "Ed25519":
+                    algorithm = "EdDSA"
+                else:
+                    raise InvalidKeyError("Unsupported crv: %s" % crv)
+            else:
+                raise InvalidKeyError("Unsupported kty: %s" % kty)
 
         self.Algorithm = self._algorithms.get(algorithm)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/jwt/api_jwt.py 
new/PyJWT-2.1.0/jwt/api_jwt.py
--- old/PyJWT-2.0.1/jwt/api_jwt.py      2020-12-21 17:55:46.000000000 +0100
+++ new/PyJWT-2.1.0/jwt/api_jwt.py      2021-04-28 13:23:40.000000000 +0200
@@ -75,6 +75,13 @@
         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 @@
         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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/jwt/jwks_client.py 
new/PyJWT-2.1.0/jwt/jwks_client.py
--- old/PyJWT-2.0.1/jwt/jwks_client.py  2020-12-22 14:41:00.000000000 +0100
+++ new/PyJWT-2.1.0/jwt/jwks_client.py  2021-04-28 13:23:40.000000000 +0200
@@ -1,5 +1,6 @@
 import json
 import urllib.request
+from functools import lru_cache
 from typing import Any, List
 
 from .api_jwk import PyJWK, PyJWKSet
@@ -8,8 +9,12 @@
 
 
 class PyJWKClient:
-    def __init__(self, uri: str):
+    def __init__(self, uri: str, cache_keys: bool = True, max_cached_keys: int 
= 16):
         self.uri = uri
+        if cache_keys:
+            # Cache signing keys
+            # Ignore mypy (https://github.com/python/mypy/issues/2427)
+            self.get_signing_key = 
lru_cache(maxsize=max_cached_keys)(self.get_signing_key)  # type: ignore
 
     def fetch_data(self) -> Any:
         with urllib.request.urlopen(self.uri) as response:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/pyproject.toml 
new/PyJWT-2.1.0/pyproject.toml
--- old/PyJWT-2.0.1/pyproject.toml      2020-12-21 17:55:46.000000000 +0100
+++ new/PyJWT-2.1.0/pyproject.toml      1970-01-01 01:00:00.000000000 +0100
@@ -1,21 +0,0 @@
-[build-system]
-requires = ["setuptools", "wheel"]
-build-backend = "setuptools.build_meta"
-
-
-[tool.coverage.run]
-parallel = true
-branch = true
-source = ["jwt"]
-
-[tool.coverage.paths]
-source = ["jwt", ".tox/*/site-packages"]
-
-[tool.coverage.report]
-show_missing = true
-
-
-[tool.isort]
-profile = "black"
-atomic = true
-combine_as_imports = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/keys/jwk_ec_key_P-256.json 
new/PyJWT-2.1.0/tests/keys/jwk_ec_key_P-256.json
--- old/PyJWT-2.0.1/tests/keys/jwk_ec_key_P-256.json    2020-08-24 
18:22:55.000000000 +0200
+++ new/PyJWT-2.1.0/tests/keys/jwk_ec_key_P-256.json    2021-04-28 
13:23:40.000000000 +0200
@@ -2,7 +2,7 @@
   "kty": "EC",
   "kid": "bilbo.baggins.256@hobbiton.example",
   "crv": "P-256",
-  "x": "PTTjIY84aLtaZCxLTrG_d8I0G6YKCV7lg8M4xkKfwQ4=",
-  "y": "ank6KA34vv24HZLXlChVs85NEGlpg2sbqNmR_BcgyJU=",
-  "d": "9GJquUJf57a9sev-u8-PoYlIezIPqI_vGpIaiu4zyZk="
+  "x": "PTTjIY84aLtaZCxLTrG_d8I0G6YKCV7lg8M4xkKfwQ4",
+  "y": "ank6KA34vv24HZLXlChVs85NEGlpg2sbqNmR_BcgyJU",
+  "d": "9GJquUJf57a9sev-u8-PoYlIezIPqI_vGpIaiu4zyZk"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/keys/jwk_ec_key_secp256k1.json 
new/PyJWT-2.1.0/tests/keys/jwk_ec_key_secp256k1.json
--- old/PyJWT-2.0.1/tests/keys/jwk_ec_key_secp256k1.json        1970-01-01 
01:00:00.000000000 +0100
+++ new/PyJWT-2.1.0/tests/keys/jwk_ec_key_secp256k1.json        2021-04-28 
13:23:40.000000000 +0200
@@ -0,0 +1,8 @@
+{
+  "kty": "EC",
+  "kid": "bilbo.baggins.256k@hobbiton.example",
+  "crv": "secp256k1",
+  "x": "MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs",
+  "y": "7r8d-xF7QAgT5kSRdly6M8xeg4Jz83Gs_CQPQRH65QI",
+  "d": "XV7LOlEOANIaSxyil8yE8NPDT5jmVw_HQeCwNDzochQ"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/keys/jwk_ec_pub_P-256.json 
new/PyJWT-2.1.0/tests/keys/jwk_ec_pub_P-256.json
--- old/PyJWT-2.0.1/tests/keys/jwk_ec_pub_P-256.json    2020-08-24 
18:22:55.000000000 +0200
+++ new/PyJWT-2.1.0/tests/keys/jwk_ec_pub_P-256.json    2021-04-28 
13:23:40.000000000 +0200
@@ -2,6 +2,6 @@
   "kty": "EC",
   "kid": "bilbo.baggins.256@hobbiton.example",
   "crv": "P-256",
-  "x": "PTTjIY84aLtaZCxLTrG_d8I0G6YKCV7lg8M4xkKfwQ4=",
-  "y": "ank6KA34vv24HZLXlChVs85NEGlpg2sbqNmR_BcgyJU="
+  "x": "PTTjIY84aLtaZCxLTrG_d8I0G6YKCV7lg8M4xkKfwQ4",
+  "y": "ank6KA34vv24HZLXlChVs85NEGlpg2sbqNmR_BcgyJU"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/keys/jwk_ec_pub_secp256k1.json 
new/PyJWT-2.1.0/tests/keys/jwk_ec_pub_secp256k1.json
--- old/PyJWT-2.0.1/tests/keys/jwk_ec_pub_secp256k1.json        1970-01-01 
01:00:00.000000000 +0100
+++ new/PyJWT-2.1.0/tests/keys/jwk_ec_pub_secp256k1.json        2021-04-28 
13:23:40.000000000 +0200
@@ -0,0 +1,7 @@
+{
+  "kty": "EC",
+  "kid": "bilbo.baggins.256k@hobbiton.example",
+  "crv": "secp256k1",
+  "x": "MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs",
+  "y": "7r8d-xF7QAgT5kSRdly6M8xeg4Jz83Gs_CQPQRH65QI"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/keys/jwk_okp_key_Ed25519.json 
new/PyJWT-2.1.0/tests/keys/jwk_okp_key_Ed25519.json
--- old/PyJWT-2.0.1/tests/keys/jwk_okp_key_Ed25519.json 1970-01-01 
01:00:00.000000000 +0100
+++ new/PyJWT-2.1.0/tests/keys/jwk_okp_key_Ed25519.json 2021-04-28 
13:23:40.000000000 +0200
@@ -0,0 +1,6 @@
+{
+  "kty":"OKP",
+  "crv":"Ed25519",
+  "d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
+  "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/keys/jwk_okp_pub_Ed25519.json 
new/PyJWT-2.1.0/tests/keys/jwk_okp_pub_Ed25519.json
--- old/PyJWT-2.0.1/tests/keys/jwk_okp_pub_Ed25519.json 1970-01-01 
01:00:00.000000000 +0100
+++ new/PyJWT-2.1.0/tests/keys/jwk_okp_pub_Ed25519.json 2021-04-28 
13:23:40.000000000 +0200
@@ -0,0 +1,5 @@
+{
+  "kty":"OKP",
+  "crv":"Ed25519",
+  "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/test_algorithms.py 
new/PyJWT-2.1.0/tests/test_algorithms.py
--- old/PyJWT-2.0.1/tests/test_algorithms.py    2020-12-22 14:41:00.000000000 
+0100
+++ new/PyJWT-2.1.0/tests/test_algorithms.py    2021-04-28 13:43:48.000000000 
+0200
@@ -166,6 +166,7 @@
             "P-256": ECAlgorithm.SHA256,
             "P-384": ECAlgorithm.SHA384,
             "P-521": ECAlgorithm.SHA512,
+            "secp256k1": ECAlgorithm.SHA256,
         }
         for (curve, hash) in tests.items():
             algo = ECAlgorithm(hash)
@@ -185,8 +186,8 @@
 
         valid_points = {
             "P-256": {
-                "x": "PTTjIY84aLtaZCxLTrG_d8I0G6YKCV7lg8M4xkKfwQ4=",
-                "y": "ank6KA34vv24HZLXlChVs85NEGlpg2sbqNmR_BcgyJU=",
+                "x": "PTTjIY84aLtaZCxLTrG_d8I0G6YKCV7lg8M4xkKfwQ4",
+                "y": "ank6KA34vv24HZLXlChVs85NEGlpg2sbqNmR_BcgyJU",
             },
             "P-384": {
                 "x": 
"IDC-5s6FERlbC4Nc_4JhKW8sd51AhixtMdNUtPxhRFP323QY6cwWeIA3leyZhz-J",
@@ -196,6 +197,10 @@
                 "x": 
"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
                 "y": 
"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1",
             },
+            "secp256k1": {
+                "x": "MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs",
+                "y": "7r8d-xF7QAgT5kSRdly6M8xeg4Jz83Gs_CQPQRH65QI",
+            },
         }
 
         # Invalid JSON
@@ -223,7 +228,7 @@
             algo.from_jwk('{"kty": "EC", "x": "dGVzdHRlc3Q=", "y": 
"dGVzdA=="}')
 
         # EC coordinates length invalid
-        for curve in ("P-256", "P-384", "P-521"):
+        for curve in ("P-256", "P-384", "P-521", "secp256k1"):
             with pytest.raises(InvalidKeyError):
                 algo.from_jwk(
                     '{{"kty": "EC", "crv": "{}", "x": "dGVzdA==", '
@@ -653,6 +658,13 @@
         result = algo.verify(signing_input, key, signature)
         assert result
 
+        # private key can also be used.
+        with open(key_path("jwk_ec_key_P-521.json")) as keyfile:
+            private_key = algo.from_jwk(keyfile.read())
+
+        result = algo.verify(signing_input, private_key, signature)
+        assert result
+
 
 @crypto_required
 class TestEd25519Algorithms:
@@ -728,3 +740,95 @@
             jwt_pub_key_second = algo.prepare_key(jwt_pub_key_first)
 
         assert jwt_pub_key_first == jwt_pub_key_second
+
+    def test_ed25519_jwk_private_key_should_parse_and_verify(self):
+        algo = Ed25519Algorithm()
+
+        with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
+            key = algo.from_jwk(keyfile.read())
+
+        signature = algo.sign(b"Hello World!", key)
+        assert algo.verify(b"Hello World!", key.public_key(), signature)
+
+    def test_ed25519_jwk_public_key_should_parse_and_verify(self):
+        algo = Ed25519Algorithm()
+
+        with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
+            priv_key = algo.from_jwk(keyfile.read())
+
+        with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile:
+            pub_key = algo.from_jwk(keyfile.read())
+
+        signature = algo.sign(b"Hello World!", priv_key)
+        assert algo.verify(b"Hello World!", pub_key, signature)
+
+    def test_ed25519_jwk_fails_on_invalid_json(self):
+        algo = Ed25519Algorithm()
+
+        with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile:
+            valid_pub = json.loads(keyfile.read())
+        with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
+            valid_key = json.loads(keyfile.read())
+
+        # Invalid instance type
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(123)
+
+        # Invalid JSON
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk("<this isn't json>")
+
+        # Invalid kty, not "OKP"
+        v = valid_pub.copy()
+        v["kty"] = "oct"
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(v)
+
+        # Invalid crv, not "Ed25519"
+        v = valid_pub.copy()
+        v["crv"] = "P-256"
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(v)
+
+        # Missing x
+        v = valid_pub.copy()
+        del v["x"]
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(v)
+
+        # Invalid x
+        v = valid_pub.copy()
+        v["x"] = "123"
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(v)
+
+        # Invalid d
+        v = valid_key.copy()
+        v["d"] = "123"
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(v)
+
+    def test_ed25519_to_jwk_works_with_from_jwk(self):
+        algo = Ed25519Algorithm()
+
+        with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
+            priv_key_1 = algo.from_jwk(keyfile.read())
+
+        with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile:
+            pub_key_1 = algo.from_jwk(keyfile.read())
+
+        pub = algo.to_jwk(pub_key_1)
+        pub_key_2 = algo.from_jwk(pub)
+        pri = algo.to_jwk(priv_key_1)
+        priv_key_2 = algo.from_jwk(pri)
+
+        signature_1 = algo.sign(b"Hello World!", priv_key_1)
+        signature_2 = algo.sign(b"Hello World!", priv_key_2)
+        assert algo.verify(b"Hello World!", pub_key_2, signature_1)
+        assert algo.verify(b"Hello World!", pub_key_2, signature_2)
+
+    def test_ed25519_to_jwk_raises_exception_on_invalid_key(self):
+        algo = Ed25519Algorithm()
+
+        with pytest.raises(InvalidKeyError):
+            algo.to_jwk({"not": "a valid key"})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/test_api_jwk.py 
new/PyJWT-2.1.0/tests/test_api_jwk.py
--- old/PyJWT-2.0.1/tests/test_api_jwk.py       2020-12-21 17:55:46.000000000 
+0100
+++ new/PyJWT-2.1.0/tests/test_api_jwk.py       2021-04-28 13:23:40.000000000 
+0200
@@ -1,12 +1,20 @@
 import json
 
+import pytest
+
 from jwt.algorithms import has_crypto
 from jwt.api_jwk import PyJWK, PyJWKSet
+from jwt.exceptions import InvalidKeyError, PyJWKError
 
 from .utils import crypto_required, key_path
 
 if has_crypto:
-    from jwt.algorithms import RSAAlgorithm
+    from jwt.algorithms import (
+        ECAlgorithm,
+        Ed25519Algorithm,
+        HMACAlgorithm,
+        RSAAlgorithm,
+    )
 
 
 class TestPyJWK:
@@ -52,6 +60,158 @@
         assert jwk.key_id == "keyid-abc123"
         assert jwk.public_key_use == "sig"
 
+    @crypto_required
+    def test_should_load_key_without_alg_from_dict(self):
+
+        with open(key_path("jwk_rsa_pub.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "RSA"
+        assert isinstance(jwk.Algorithm, RSAAlgorithm)
+        assert jwk.Algorithm.hash_alg == RSAAlgorithm.SHA256
+
+    @crypto_required
+    def test_should_load_key_from_dict_with_algorithm(self):
+
+        with open(key_path("jwk_rsa_pub.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data, algorithm="RS256")
+
+        assert jwk.key_type == "RSA"
+        assert isinstance(jwk.Algorithm, RSAAlgorithm)
+        assert jwk.Algorithm.hash_alg == RSAAlgorithm.SHA256
+
+    @crypto_required
+    def test_should_load_key_ec_p256_from_dict(self):
+
+        with open(key_path("jwk_ec_pub_P-256.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "EC"
+        assert isinstance(jwk.Algorithm, ECAlgorithm)
+        assert jwk.Algorithm.hash_alg == ECAlgorithm.SHA256
+
+    @crypto_required
+    def test_should_load_key_ec_p384_from_dict(self):
+
+        with open(key_path("jwk_ec_pub_P-384.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "EC"
+        assert isinstance(jwk.Algorithm, ECAlgorithm)
+        assert jwk.Algorithm.hash_alg == ECAlgorithm.SHA384
+
+    @crypto_required
+    def test_should_load_key_ec_p521_from_dict(self):
+
+        with open(key_path("jwk_ec_pub_P-521.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "EC"
+        assert isinstance(jwk.Algorithm, ECAlgorithm)
+        assert jwk.Algorithm.hash_alg == ECAlgorithm.SHA512
+
+    @crypto_required
+    def test_should_load_key_ec_secp256k1_from_dict(self):
+
+        with open(key_path("jwk_ec_pub_secp256k1.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "EC"
+        assert isinstance(jwk.Algorithm, ECAlgorithm)
+        assert jwk.Algorithm.hash_alg == ECAlgorithm.SHA256
+
+    @crypto_required
+    def test_should_load_key_hmac_from_dict(self):
+
+        with open(key_path("jwk_hmac.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "oct"
+        assert isinstance(jwk.Algorithm, HMACAlgorithm)
+        assert jwk.Algorithm.hash_alg == HMACAlgorithm.SHA256
+
+    @crypto_required
+    def test_should_load_key_hmac_without_alg_from_dict(self):
+
+        with open(key_path("jwk_hmac.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        del key_data["alg"]
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "oct"
+        assert isinstance(jwk.Algorithm, HMACAlgorithm)
+        assert jwk.Algorithm.hash_alg == HMACAlgorithm.SHA256
+
+    @crypto_required
+    def test_should_load_key_okp_without_alg_from_dict(self):
+
+        with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile:
+            key_data = json.loads(keyfile.read())
+
+        jwk = PyJWK.from_dict(key_data)
+
+        assert jwk.key_type == "OKP"
+        assert isinstance(jwk.Algorithm, Ed25519Algorithm)
+
+    @crypto_required
+    def test_from_dict_should_throw_exception_if_arg_is_invalid(self):
+
+        with open(key_path("jwk_rsa_pub.json")) as keyfile:
+            valid_rsa_pub = json.loads(keyfile.read())
+        with open(key_path("jwk_ec_pub_P-256.json")) as keyfile:
+            valid_ec_pub = json.loads(keyfile.read())
+        with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile:
+            valid_okp_pub = json.loads(keyfile.read())
+
+        # Unknown algorithm
+        with pytest.raises(PyJWKError):
+            PyJWK.from_dict(valid_rsa_pub, algorithm="unknown")
+
+        # Missing kty
+        v = valid_rsa_pub.copy()
+        del v["kty"]
+        with pytest.raises(InvalidKeyError):
+            PyJWK.from_dict(v)
+
+        # Unknown kty
+        v = valid_rsa_pub.copy()
+        v["kty"] = "unknown"
+        with pytest.raises(InvalidKeyError):
+            PyJWK.from_dict(v)
+
+        # Unknown EC crv
+        v = valid_ec_pub.copy()
+        v["crv"] = "unknown"
+        with pytest.raises(InvalidKeyError):
+            PyJWK.from_dict(v)
+
+        # Unknown OKP crv
+        v = valid_okp_pub.copy()
+        v["crv"] = "unknown"
+        with pytest.raises(InvalidKeyError):
+            PyJWK.from_dict(v)
+
+        # Missing OKP crv
+        v = valid_okp_pub.copy()
+        del v["crv"]
+        with pytest.raises(InvalidKeyError):
+            PyJWK.from_dict(v)
+
 
 class TestPyJWKSet:
     @crypto_required
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/test_api_jws.py 
new/PyJWT-2.1.0/tests/test_api_jws.py
--- old/PyJWT-2.0.1/tests/test_api_jws.py       2020-12-22 14:41:00.000000000 
+0100
+++ new/PyJWT-2.1.0/tests/test_api_jws.py       2021-04-28 13:47:41.000000000 
+0200
@@ -32,7 +32,7 @@
 
 @pytest.fixture
 def payload():
-    """ Creates a sample jws claimset for use as a payload during tests """
+    """Creates a sample jws claimset for use as a payload during tests"""
     return b"hello world"
 
 
@@ -527,6 +527,7 @@
         "algo",
         [
             "ES256",
+            "ES256K",
             "ES384",
             "ES512",
         ],
@@ -557,10 +558,12 @@
 
         if has_crypto:
             assert "ES256" in jws_algorithms
+            assert "ES256K" in jws_algorithms
             assert "ES384" in jws_algorithms
             assert "ES512" in jws_algorithms
         else:
             assert "ES256" not in jws_algorithms
+            assert "ES256K" not in jws_algorithms
             assert "ES384" not in jws_algorithms
             assert "ES512" not in jws_algorithms
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/test_api_jwt.py 
new/PyJWT-2.1.0/tests/test_api_jwt.py
--- old/PyJWT-2.0.1/tests/test_api_jwt.py       2020-12-21 17:55:46.000000000 
+0100
+++ new/PyJWT-2.1.0/tests/test_api_jwt.py       2021-04-28 13:47:41.000000000 
+0200
@@ -27,7 +27,7 @@
 
 @pytest.fixture
 def payload():
-    """ Creates a sample JWT claimset for use as a payload during tests """
+    """Creates a sample JWT claimset for use as a payload during tests"""
     return {"iss": "jeff", "exp": utc_timestamp() + 15, "claim": "insanity"}
 
 
@@ -579,6 +579,22 @@
                 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)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.0.1/tests/test_jwks_client.py 
new/PyJWT-2.1.0/tests/test_jwks_client.py
--- old/PyJWT-2.0.1/tests/test_jwks_client.py   2020-12-21 17:55:46.000000000 
+0100
+++ new/PyJWT-2.1.0/tests/test_jwks_client.py   2021-04-28 13:23:40.000000000 
+0200
@@ -37,7 +37,7 @@
         response.__exit__ = mock.Mock()
         response.read.side_effect = [json.dumps(data)]
         urlopen_mock.return_value = response
-        yield
+        yield urlopen_mock
 
 
 @crypto_required
@@ -88,6 +88,38 @@
         assert signing_key.key_id == kid
         assert signing_key.public_key_use == "sig"
 
+    def test_get_signing_key_caches_result(self):
+        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json";
+        kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"
+
+        jwks_client = PyJWKClient(url)
+
+        with mocked_response(RESPONSE_DATA):
+            jwks_client.get_signing_key(kid)
+
+        # mocked_response does not allow urllib.request.urlopen to be called 
twice
+        # so a second mock is needed
+        with mocked_response(RESPONSE_DATA) as repeated_call:
+            jwks_client.get_signing_key(kid)
+
+        assert repeated_call.call_count == 0
+
+    def test_get_signing_key_does_not_cache_opt_out(self):
+        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json";
+        kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"
+
+        jwks_client = PyJWKClient(url, cache_keys=False)
+
+        with mocked_response(RESPONSE_DATA):
+            jwks_client.get_signing_key(kid)
+
+        # mocked_response does not allow urllib.request.urlopen to be called 
twice
+        # so a second mock is needed
+        with mocked_response(RESPONSE_DATA) as repeated_call:
+            jwks_client.get_signing_key(kid)
+
+        assert repeated_call.call_count == 1
+
     def test_get_signing_key_from_jwt(self):
         token = 
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA"
         url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json";

Reply via email to