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-11-06 18:15:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-PyJWT (Old)
 and      /work/SRC/openSUSE:Factory/.python-PyJWT.new.1890 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-PyJWT"

Sat Nov  6 18:15:41 2021 rev:23 rq:929638 version:2.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-PyJWT/python-PyJWT.changes        
2021-05-20 19:23:53.862232039 +0200
+++ /work/SRC/openSUSE:Factory/.python-PyJWT.new.1890/python-PyJWT.changes      
2021-11-06 18:18:03.956885640 +0100
@@ -1,0 +2,17 @@
+Wed Nov  3 08:57:35 UTC 2021 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- Update to 2.3.0
+  * Revert "Remove arbitrary kwargs." (#701)
+  * Add exception chaining (#702)
+- from version 2.2.0
+  * Remove arbitrary kwargs. (#657)
+  * Use timezone package as Python 3.5+ is required. (#694)
+  * Assume JWK without the "use" claim is valid for signing
+    as per RFC7517 (#668)
+  * Prefer `headers["alg"]` to `algorithm` in `jwt.encode()`. (#673)
+  * Fix aud validation to support {'aud': null} case. (#670)
+  * Make `typ` optional in JWT to be compliant with RFC7519. (#644)
+  * Remove upper bound on cryptography version. (#693)
+  * Add support for Ed448/EdDSA. (#675)
+
+-------------------------------------------------------------------

Old:
----
  PyJWT-2.1.0.tar.gz

New:
----
  PyJWT-2.3.0.tar.gz

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

Other differences:
------------------
++++++ python-PyJWT.spec ++++++
--- /var/tmp/diff_new_pack.qER18i/_old  2021-11-06 18:18:04.404885872 +0100
+++ /var/tmp/diff_new_pack.qER18i/_new  2021-11-06 18:18:04.408885873 +0100
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %global skip_python2 1
 Name:           python-PyJWT
-Version:        2.1.0
+Version:        2.3.0
 Release:        0
 Summary:        JSON Web Token implementation in Python
 License:        MIT

++++++ PyJWT-2.1.0.tar.gz -> PyJWT-2.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/.pre-commit-config.yaml 
new/PyJWT-2.3.0/.pre-commit-config.yaml
--- old/PyJWT-2.1.0/.pre-commit-config.yaml     2021-04-28 13:47:41.000000000 
+0200
+++ new/PyJWT-2.3.0/.pre-commit-config.yaml     2021-10-16 14:23:39.000000000 
+0200
@@ -1,36 +1,36 @@
 repos:
   - repo: https://github.com/psf/black
-    rev: 21.4b0
+    rev: 21.9b0
     hooks:
       - id: black
         args: ["--target-version=py36"]
 
   - repo: https://github.com/asottile/blacken-docs
-    rev: v1.10.0
+    rev: v1.11.0
     hooks:
       - id: blacken-docs
         args: ["--target-version=py36"]
 
   - repo: https://github.com/PyCQA/flake8
-    rev: 3.9.1
+    rev: 4.0.1
     hooks:
       - id: flake8
         language_version: python3.8
 
   - repo: https://github.com/PyCQA/isort
-    rev: 5.8.0
+    rev: 5.9.3
     hooks:
       - id: isort
 
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v3.4.0
+    rev: v4.0.1
     hooks:
       - id: trailing-whitespace
       - id: end-of-file-fixer
       - id: debug-statements
 
   - repo: https://github.com/mgedmin/check-manifest
-    rev: "0.46"
+    rev: "0.47"
     hooks:
       - id: check-manifest
         args: [--no-build-isolation]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/CHANGELOG.rst 
new/PyJWT-2.3.0/CHANGELOG.rst
--- old/PyJWT-2.1.0/CHANGELOG.rst       2021-04-28 13:59:20.000000000 +0200
+++ new/PyJWT-2.3.0/CHANGELOG.rst       2021-10-16 17:53:51.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.1.0...HEAD>`__
+`Unreleased <https://github.com/jpadilla/pyjwt/compare/2.3.0...HEAD>`__
 -----------------------------------------------------------------------
 
 Changed
@@ -16,6 +16,41 @@
 Added
 ~~~~~
 
+`v2.3.0 <https://github.com/jpadilla/pyjwt/compare/2.2.0...2.3.0>`__
+-----------------------------------------------------------------------
+
+Fixed
+~~~~~
+
+- Revert "Remove arbitrary kwargs." `#701 
<https://github.com/jpadilla/pyjwt/pull/701>`__
+
+Added
+~~~~~
+
+- Add exception chaining `#702 <https://github.com/jpadilla/pyjwt/pull/702>`__
+
+`v2.2.0 <https://github.com/jpadilla/pyjwt/compare/2.1.0...2.2.0>`__
+-----------------------------------------------------------------------
+
+Changed
+~~~~~~~
+
+- Remove arbitrary kwargs. `#657 
<https://github.com/jpadilla/pyjwt/pull/657>`__
+- Use timezone package as Python 3.5+ is required. `#694 
<https://github.com/jpadilla/pyjwt/pull/694>`__
+
+Fixed
+~~~~~
+- Assume JWK without the "use" claim is valid for signing as per RFC7517 `#668 
<https://github.com/jpadilla/pyjwt/pull/668>`__
+- Prefer `headers["alg"]` to `algorithm` in `jwt.encode()`. `#673 
<https://github.com/jpadilla/pyjwt/pull/673>`__
+- Fix aud validation to support {'aud': null} case. `#670 
<https://github.com/jpadilla/pyjwt/pull/670>`__
+- Make `typ` optional in JWT to be compliant with RFC7519. `#644 
<https://github.com/jpadilla/pyjwt/pull/644>`__
+-  Remove upper bound on cryptography version. `#693 
<https://github.com/jpadilla/pyjwt/pull/693>`__
+
+Added
+~~~~~
+
+- Add support for Ed448/EdDSA. `#675 
<https://github.com/jpadilla/pyjwt/pull/675>`__
+
 `v2.1.0 <https://github.com/jpadilla/pyjwt/compare/2.0.1...2.1.0>`__
 --------------------------------------------------------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/PKG-INFO new/PyJWT-2.3.0/PKG-INFO
--- old/PyJWT-2.1.0/PKG-INFO    2021-04-28 14:01:11.608931300 +0200
+++ new/PyJWT-2.3.0/PKG-INFO    2021-10-16 17:54:33.864155000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: PyJWT
-Version: 2.1.0
+Version: 2.3.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: dev
 Provides-Extra: tests
 Provides-Extra: docs
+Provides-Extra: dev
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/PyJWT.egg-info/PKG-INFO 
new/PyJWT-2.3.0/PyJWT.egg-info/PKG-INFO
--- old/PyJWT-2.1.0/PyJWT.egg-info/PKG-INFO     2021-04-28 14:01:11.000000000 
+0200
+++ new/PyJWT-2.3.0/PyJWT.egg-info/PKG-INFO     2021-10-16 17:54:33.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: PyJWT
-Version: 2.1.0
+Version: 2.3.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: dev
 Provides-Extra: tests
 Provides-Extra: docs
+Provides-Extra: dev
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/PyJWT.egg-info/SOURCES.txt 
new/PyJWT-2.3.0/PyJWT.egg-info/SOURCES.txt
--- old/PyJWT-2.1.0/PyJWT.egg-info/SOURCES.txt  2021-04-28 14:01:11.000000000 
+0200
+++ new/PyJWT-2.3.0/PyJWT.egg-info/SOURCES.txt  2021-10-16 17:54:33.000000000 
+0200
@@ -56,7 +56,9 @@
 tests/keys/jwk_ec_pub_secp256k1.json
 tests/keys/jwk_hmac.json
 tests/keys/jwk_okp_key_Ed25519.json
+tests/keys/jwk_okp_key_Ed448.json
 tests/keys/jwk_okp_pub_Ed25519.json
+tests/keys/jwk_okp_pub_Ed448.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.1.0/PyJWT.egg-info/requires.txt 
new/PyJWT-2.3.0/PyJWT.egg-info/requires.txt
--- old/PyJWT-2.1.0/PyJWT.egg-info/requires.txt 2021-04-28 14:01:11.000000000 
+0200
+++ new/PyJWT-2.3.0/PyJWT.egg-info/requires.txt 2021-10-16 17:54:33.000000000 
+0200
@@ -1,12 +1,12 @@
 
 [crypto]
-cryptography<4.0.0,>=3.3.1
+cryptography>=3.3.1
 
 [dev]
 sphinx
 sphinx-rtd-theme
 zope.interface
-cryptography<4.0.0,>=3.3.1
+cryptography>=3.3.1
 pytest<7.0.0,>=6.0.0
 coverage[toml]==5.0.4
 mypy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/docs/algorithms.rst 
new/PyJWT-2.3.0/docs/algorithms.rst
--- old/PyJWT-2.1.0/docs/algorithms.rst 2021-04-28 13:43:46.000000000 +0200
+++ new/PyJWT-2.3.0/docs/algorithms.rst 2021-10-06 12:37:16.000000000 +0200
@@ -17,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 - Ed25519 signature using SHA-512. Provides 128-bit security
+* EdDSA - Both Ed25519 signature using SHA-512 and Ed448 signature using SHA-3 
are supported. Ed25519 and Ed448 provide 128-bit and 224-bit security 
respectively.
 
 Asymmetric (Public-key) Algorithms
 ----------------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/docs/api.rst new/PyJWT-2.3.0/docs/api.rst
--- old/PyJWT-2.1.0/docs/api.rst        2021-04-28 13:43:48.000000000 +0200
+++ new/PyJWT-2.3.0/docs/api.rst        2021-08-08 21:28:32.000000000 +0200
@@ -13,8 +13,9 @@
         * for **asymmetric algorithms**: PEM-formatted private key, a 
multiline string
         * for **symmetric algorithms**: plain string, sufficiently long for 
security
 
-    :param str algorithm: algorithm to sign the token with, e.g. ``"ES256"``
-    :param dict headers: additional JWT header fields, e.g. 
``dict(kid="my-key-id")``
+    :param str algorithm: algorithm to sign the token with, e.g. ``"ES256"``.
+        If ``headers`` includes ``alg``, it will be preferred to this 
parameter.
+    :param dict headers: additional JWT header fields, e.g. 
``dict(kid="my-key-id")``.
     :param json.JSONEncoder json_encoder: custom JSON encoder for ``payload`` 
and ``headers``
     :rtype: str
     :returns: a JSON Web Token
@@ -43,19 +44,23 @@
 
     :param dict options: extended decoding and validation options
 
-        * ``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
+        * ``require=[]`` list of claims that must be present.
+          Example: ``require=["exp", "iat", "nbf"]``.
+          **Only verifies that the claims exists**. Does not verify that the 
claims are valid.
+        * ``verify_aud=verify_signature`` check that ``aud`` (audience) claim 
matches ``audience``
+        * ``verify_iss=verify_signature`` check that ``iss`` (issuer) claim 
matches ``issuer``
+        * ``verify_exp=verify_signature`` check that ``exp`` (expiration) 
claim value is in the future
+        * ``verify_iat=verify_signature`` check that ``iat`` (issued at) claim 
value is an integer
+        * ``verify_nbf=verify_signature`` check that ``nbf`` (not before) 
claim value is in the past
+
+        .. warning::
+
+            ``exp``, ``iat`` and ``nbf`` will only be verified if present.
+            Please pass respective value to ``require`` if you want to make
+            sure that they are always present (and therefore always verified
+            if ``verify_exp``, ``verify_iat``, and ``verify_nbf`` respectively
+            is set to ``True``).
 
     :param Iterable audience: optional, the value for ``verify_aud`` check
     :param str issuer: optional, the value for ``verify_iss`` check
@@ -63,7 +68,58 @@
     :rtype: dict
     :returns: the JWT claims
 
-.. note:: TODO: Document PyJWS / PyJWT classes
+.. function:: decode_complete(jwt, key="", algorithms=None, options=None, 
audience=None, issuer=None, leeway=0)
+
+    Identical to ``jwt.decode`` except for return value which is a dictionary 
containing the token header (JOSE Header),
+    the token payload (JWT Payload), and token signature (JWT Signature) on 
the keys "header", "payload",
+    and "signature" respectively.
+
+    :param str jwt: the token to be decoded
+    :param str key: the key suitable for the allowed algorithm
+
+    :param list algorithms: allowed algorithms, e.g. ``["ES256"]``
+
+        .. 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\*).
+
+    :param dict options: extended decoding and validation options
+
+        * ``verify_signature=True`` verify the JWT cryptographic signature
+        * ``require=[]`` list of claims that must be present.
+          Example: ``require=["exp", "iat", "nbf"]``.
+          **Only verifies that the claims exists**. Does not verify that the 
claims are valid.
+        * ``verify_aud=verify_signature`` check that ``aud`` (audience) claim 
matches ``audience``
+        * ``verify_iss=verify_signature`` check that ``iss`` (issuer) claim 
matches ``issuer``
+        * ``verify_exp=verify_signature`` check that ``exp`` (expiration) 
claim value is in the future
+        * ``verify_iat=verify_signature`` check that ``iat`` (issued at) claim 
value is an integer
+        * ``verify_nbf=verify_signature`` check that ``nbf`` (not before) 
claim value is in the past
+
+        .. warning::
+
+            ``exp``, ``iat`` and ``nbf`` will only be verified if present.
+            Please pass respective value to ``require`` if you want to make
+            sure that they are always present (and therefore always verified
+            if ``verify_exp``, ``verify_iat``, and ``verify_nbf`` respectively
+            is set to ``True``).
+
+    :param Iterable audience: optional, the value for ``verify_aud`` check
+    :param str issuer: optional, the value for ``verify_iss`` check
+    :param float leeway: a time margin in seconds for the expiration check
+    :rtype: dict
+    :returns: Decoded JWT with the JOSE Header on the key ``header``, the JWS
+     Payload on the key ``payload``, and the JWS Signature on the key 
``signature``.
+
+.. note:: TODO: Document PyJWS class
 
 Exceptions
 ----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/docs/installation.rst 
new/PyJWT-2.3.0/docs/installation.rst
--- old/PyJWT-2.1.0/docs/installation.rst       2020-08-24 16:24:03.000000000 
+0200
+++ new/PyJWT-2.3.0/docs/installation.rst       2021-08-08 21:55:24.000000000 
+0200
@@ -7,6 +7,9 @@
 
     $ pip install pyjwt
 
+
+.. _installation_cryptography:
+
 Cryptographic Dependencies (Optional)
 -------------------------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/docs/usage.rst 
new/PyJWT-2.3.0/docs/usage.rst
--- old/PyJWT-2.1.0/docs/usage.rst      2021-04-28 13:23:40.000000000 +0200
+++ new/PyJWT-2.3.0/docs/usage.rst      2021-10-06 12:37:16.000000000 +0200
@@ -17,6 +17,8 @@
 Encoding & Decoding Tokens with RS256 (RSA)
 -------------------------------------------
 
+RSA encoding and decoding require the ``cryptography`` module. See 
:ref:`installation_cryptography`.
+
 .. code-block:: pycon
 
     >>> import jwt
@@ -117,7 +119,7 @@
 .. code-block:: python
 
     jwt.encode({"exp": 1371720939}, "secret")
-    jwt.encode({"exp": datetime.utcnow()}, "secret")
+    jwt.encode({"exp": datetime.now(tz=timezone.utc)}, "secret")
 
 Expiration time is automatically verified in `jwt.decode()` and raises
 `jwt.ExpiredSignatureError` if the expiration time is in the past:
@@ -131,7 +133,7 @@
         ...
 
 Expiration time will be compared to the current UTC time (as given by
-`timegm(datetime.utcnow().utctimetuple())`), so be sure to use a UTC timestamp
+`timegm(datetime.now(tz=timezone.utc).utctimetuple())`), so be sure to use a 
UTC timestamp
 or datetime in encoding.
 
 You can turn off expiration time verification with the `verify_exp` parameter 
in the options argument.
@@ -145,7 +147,8 @@
 .. code-block:: python
 
     jwt_payload = jwt.encode(
-        {"exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=30)}, 
"secret"
+        {"exp": datetime.datetime.now(tz=timezone.utc) + 
datetime.timedelta(seconds=30)},
+        "secret",
     )
 
     time.sleep(32)
@@ -179,7 +182,7 @@
 .. code-block:: python
 
     jwt.encode({"nbf": 1371720939}, "secret")
-    jwt.encode({"nbf": datetime.utcnow()}, "secret")
+    jwt.encode({"nbf": datetime.now(tz=timezone.utc)}, "secret")
 
 Issuer Claim (iss)
 ~~~~~~~~~~~~~~~~~~
@@ -257,7 +260,7 @@
 .. code-block:: python
 
     jwt.encode({"iat": 1371720939}, "secret")
-    jwt.encode({"iat": datetime.utcnow()}, "secret")
+    jwt.encode({"iat": datetime.now(tz=timezone.utc)}, "secret")
 
 Requiring Presence of Claims
 ----------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/jwt/__init__.py 
new/PyJWT-2.3.0/jwt/__init__.py
--- old/PyJWT-2.1.0/jwt/__init__.py     2021-04-28 13:59:20.000000000 +0200
+++ new/PyJWT-2.3.0/jwt/__init__.py     2021-10-16 17:53:51.000000000 +0200
@@ -25,7 +25,7 @@
 )
 from .jwks_client import PyJWKClient
 
-__version__ = "2.1.0"
+__version__ = "2.3.0"
 
 __title__ = "PyJWT"
 __description__ = "JSON Web Token implementation in Python"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/jwt/algorithms.py 
new/PyJWT-2.3.0/jwt/algorithms.py
--- old/PyJWT-2.1.0/jwt/algorithms.py   2021-04-28 13:43:48.000000000 +0200
+++ new/PyJWT-2.3.0/jwt/algorithms.py   2021-10-06 12:37:16.000000000 +0200
@@ -22,6 +22,10 @@
         EllipticCurvePrivateKey,
         EllipticCurvePublicKey,
     )
+    from cryptography.hazmat.primitives.asymmetric.ed448 import (
+        Ed448PrivateKey,
+        Ed448PublicKey,
+    )
     from cryptography.hazmat.primitives.asymmetric.ed25519 import (
         Ed25519PrivateKey,
         Ed25519PublicKey,
@@ -93,7 +97,7 @@
                 "PS256": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256),
                 "PS384": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384),
                 "PS512": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512),
-                "EdDSA": Ed25519Algorithm(),
+                "EdDSA": OKPAlgorithm(),
             }
         )
 
@@ -243,22 +247,21 @@
             self.hash_alg = hash_alg
 
         def prepare_key(self, key):
-            if isinstance(key, RSAPrivateKey) or isinstance(key, RSAPublicKey):
+            if isinstance(key, (RSAPrivateKey, RSAPublicKey)):
                 return key
 
-            if isinstance(key, (bytes, str)):
-                key = force_bytes(key)
-
-                try:
-                    if key.startswith(b"ssh-rsa"):
-                        key = load_ssh_public_key(key)
-                    else:
-                        key = load_pem_private_key(key, password=None)
-                except ValueError:
-                    key = load_pem_public_key(key)
-            else:
+            if not isinstance(key, (bytes, str)):
                 raise TypeError("Expecting a PEM-formatted key.")
 
+            key = force_bytes(key)
+
+            try:
+                if key.startswith(b"ssh-rsa"):
+                    key = load_ssh_public_key(key)
+                else:
+                    key = load_pem_private_key(key, password=None)
+            except ValueError:
+                key = load_pem_public_key(key)
             return key
 
         @staticmethod
@@ -395,27 +398,24 @@
             self.hash_alg = hash_alg
 
         def prepare_key(self, key):
-            if isinstance(key, EllipticCurvePrivateKey) or isinstance(
-                key, EllipticCurvePublicKey
-            ):
+            if isinstance(key, (EllipticCurvePrivateKey, 
EllipticCurvePublicKey)):
                 return key
 
-            if isinstance(key, (bytes, str)):
-                key = force_bytes(key)
+            if not isinstance(key, (bytes, str)):
+                raise TypeError("Expecting a PEM-formatted key.")
 
-                # Attempt to load key. We don't know if it's
-                # a Signing Key or a Verifying Key, so we try
-                # the Verifying Key first.
-                try:
-                    if key.startswith(b"ecdsa-sha2-"):
-                        key = load_ssh_public_key(key)
-                    else:
-                        key = load_pem_public_key(key)
-                except ValueError:
-                    key = load_pem_private_key(key, password=None)
+            key = force_bytes(key)
 
-            else:
-                raise TypeError("Expecting a PEM-formatted key.")
+            # Attempt to load key. We don't know if it's
+            # a Signing Key or a Verifying Key, so we try
+            # the Verifying Key first.
+            try:
+                if key.startswith(b"ecdsa-sha2-"):
+                    key = load_ssh_public_key(key)
+                else:
+                    key = load_pem_public_key(key)
+            except ValueError:
+                key = load_pem_private_key(key, password=None)
 
             return key
 
@@ -534,9 +534,9 @@
             except InvalidSignature:
                 return False
 
-    class Ed25519Algorithm(Algorithm):
+    class OKPAlgorithm(Algorithm):
         """
-        Performs signing and verification operations using Ed25519
+        Performs signing and verification operations using EdDSA
 
         This class requires ``cryptography>=2.6`` to be installed.
         """
@@ -546,7 +546,10 @@
 
         def prepare_key(self, key):
 
-            if isinstance(key, (Ed25519PrivateKey, Ed25519PublicKey)):
+            if isinstance(
+                key,
+                (Ed25519PrivateKey, Ed25519PublicKey, Ed448PrivateKey, 
Ed448PublicKey),
+            ):
                 return key
 
             if isinstance(key, (bytes, str)):
@@ -565,9 +568,10 @@
 
         def sign(self, msg, key):
             """
-            Sign a message ``msg`` using the Ed25519 private key ``key``
+            Sign a message ``msg`` using the EdDSA private key ``key``
             :param str|bytes msg: Message to sign
-            :param Ed25519PrivateKey key: A :class:`.Ed25519PrivateKey` 
instance
+            :param Ed25519PrivateKey}Ed448PrivateKey key: A 
:class:`.Ed25519PrivateKey`
+                or :class:`.Ed448PrivateKey` iinstance
             :return bytes signature: The signature, as bytes
             """
             msg = bytes(msg, "utf-8") if type(msg) is not bytes else msg
@@ -575,18 +579,19 @@
 
         def verify(self, msg, key, sig):
             """
-            Verify a given ``msg`` against a signature ``sig`` using the 
Ed25519 key ``key``
+            Verify a given ``msg`` against a signature ``sig`` using the EdDSA 
key ``key``
 
-            :param str|bytes sig: Ed25519 signature to check ``msg`` against
+            :param str|bytes sig: EdDSA signature to check ``msg`` against
             :param str|bytes msg: Message to sign
-            :param Ed25519PrivateKey|Ed25519PublicKey key: A private or public 
Ed25519 key instance
+            :param 
Ed25519PrivateKey|Ed25519PublicKey|Ed448PrivateKey|Ed448PublicKey key:
+                A private or public EdDSA key instance
             :return bool verified: True if signature is valid, False if not.
             """
             try:
                 msg = bytes(msg, "utf-8") if type(msg) is not bytes else msg
                 sig = bytes(sig, "utf-8") if type(sig) is not bytes else sig
 
-                if isinstance(key, Ed25519PrivateKey):
+                if isinstance(key, (Ed25519PrivateKey, Ed448PrivateKey)):
                     key = key.public_key()
                 key.verify(sig, msg)
                 return True  # If no exception was raised, the signature is 
valid.
@@ -595,21 +600,21 @@
 
         @staticmethod
         def to_jwk(key):
-            if isinstance(key, Ed25519PublicKey):
+            if isinstance(key, (Ed25519PublicKey, Ed448PublicKey)):
                 x = key.public_bytes(
                     encoding=Encoding.Raw,
                     format=PublicFormat.Raw,
                 )
-
+                crv = "Ed25519" if isinstance(key, Ed25519PublicKey) else 
"Ed448"
                 return json.dumps(
                     {
                         "x": base64url_encode(force_bytes(x)).decode(),
                         "kty": "OKP",
-                        "crv": "Ed25519",
+                        "crv": crv,
                     }
                 )
 
-            if isinstance(key, Ed25519PrivateKey):
+            if isinstance(key, (Ed25519PrivateKey, Ed448PrivateKey)):
                 d = key.private_bytes(
                     encoding=Encoding.Raw,
                     format=PrivateFormat.Raw,
@@ -621,12 +626,13 @@
                     format=PublicFormat.Raw,
                 )
 
+                crv = "Ed25519" if isinstance(key, Ed25519PrivateKey) else 
"Ed448"
                 return json.dumps(
                     {
                         "x": base64url_encode(force_bytes(x)).decode(),
                         "d": base64url_encode(force_bytes(d)).decode(),
                         "kty": "OKP",
-                        "crv": "Ed25519",
+                        "crv": crv,
                     }
                 )
 
@@ -648,7 +654,7 @@
                 raise InvalidKeyError("Not an Octet Key Pair")
 
             curve = obj.get("crv")
-            if curve != "Ed25519":
+            if curve != "Ed25519" and curve != "Ed448":
                 raise InvalidKeyError(f"Invalid curve: {curve}")
 
             if "x" not in obj:
@@ -657,8 +663,12 @@
 
             try:
                 if "d" not in obj:
-                    return Ed25519PublicKey.from_public_bytes(x)
+                    if curve == "Ed25519":
+                        return Ed25519PublicKey.from_public_bytes(x)
+                    return Ed448PublicKey.from_public_bytes(x)
                 d = base64url_decode(obj.get("d"))
-                return Ed25519PrivateKey.from_private_bytes(d)
+                if curve == "Ed25519":
+                    return Ed25519PrivateKey.from_private_bytes(d)
+                return Ed448PrivateKey.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.1.0/jwt/api_jws.py 
new/PyJWT-2.3.0/jwt/api_jws.py
--- old/PyJWT-2.1.0/jwt/api_jws.py      2020-12-21 17:55:46.000000000 +0100
+++ new/PyJWT-2.3.0/jwt/api_jws.py      2021-10-16 14:23:39.000000000 +0200
@@ -77,7 +77,7 @@
         self,
         payload: bytes,
         key: str,
-        algorithm: str = "HS256",
+        algorithm: Optional[str] = "HS256",
         headers: Optional[Dict] = None,
         json_encoder: Optional[Type[json.JSONEncoder]] = None,
     ) -> str:
@@ -86,8 +86,9 @@
         if algorithm is None:
             algorithm = "none"
 
-        if algorithm not in self._valid_algs:
-            pass
+        # Prefer headers["alg"] if present to algorithm parameter.
+        if headers and "alg" in headers and headers["alg"]:
+            algorithm = headers["alg"]
 
         # Header
         header = {"typ": self.header_typ, "alg": algorithm}
@@ -95,6 +96,8 @@
         if headers:
             self._validate_headers(headers)
             header.update(headers)
+            if not header["typ"]:
+                del header["typ"]
 
         json_header = json.dumps(
             header, separators=(",", ":"), cls=json_encoder
@@ -110,14 +113,14 @@
             key = alg_obj.prepare_key(key)
             signature = alg_obj.sign(signing_input, key)
 
-        except KeyError:
+        except KeyError as e:
             if not has_crypto and algorithm in requires_cryptography:
                 raise NotImplementedError(
                     "Algorithm '%s' could not be found. Do you have 
cryptography "
                     "installed?" % algorithm
-                )
+                ) from e
             else:
-                raise NotImplementedError("Algorithm not supported")
+                raise NotImplementedError("Algorithm not supported") from e
 
         segments.append(base64url_encode(signature))
 
@@ -235,8 +238,8 @@
             if not alg_obj.verify(signing_input, key, signature):
                 raise InvalidSignatureError("Signature verification failed")
 
-        except KeyError:
-            raise InvalidAlgorithmError("Algorithm not supported")
+        except KeyError as e:
+            raise InvalidAlgorithmError("Algorithm not supported") from e
 
     def _validate_headers(self, headers):
         if "kid" in headers:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/jwt/api_jwt.py 
new/PyJWT-2.3.0/jwt/api_jwt.py
--- old/PyJWT-2.1.0/jwt/api_jwt.py      2021-04-28 13:23:40.000000000 +0200
+++ new/PyJWT-2.3.0/jwt/api_jwt.py      2021-10-16 14:23:39.000000000 +0200
@@ -1,7 +1,7 @@
 import json
 from calendar import timegm
 from collections.abc import Iterable, Mapping
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
 from typing import Any, Dict, List, Optional, Type, Union
 
 from . import api_jws
@@ -38,7 +38,7 @@
         self,
         payload: Dict[str, Any],
         key: str,
-        algorithm: str = "HS256",
+        algorithm: Optional[str] = "HS256",
         headers: Optional[Dict] = None,
         json_encoder: Optional[Type[json.JSONEncoder]] = None,
     ) -> str:
@@ -130,7 +130,7 @@
 
         self._validate_required_claims(payload, options)
 
-        now = timegm(datetime.utcnow().utctimetuple())
+        now = timegm(datetime.now(tz=timezone.utc).utctimetuple())
 
         if "iat" in payload and options["verify_iat"]:
             self._validate_iat(payload, now, leeway)
@@ -177,19 +177,18 @@
             raise ExpiredSignatureError("Signature has expired")
 
     def _validate_aud(self, payload, audience):
-        if audience is None and "aud" not in payload:
-            return
+        if audience is None:
+            if "aud" not in payload or not payload["aud"]:
+                return
+            # Application did not specify an audience, but
+            # the token has the 'aud' claim
+            raise InvalidAudienceError("Invalid audience")
 
-        if audience is not None and "aud" not in payload:
+        if "aud" not in payload or not payload["aud"]:
             # Application specified an audience, but it could not be
             # verified since the token does not contain a claim.
             raise MissingRequiredClaimError("aud")
 
-        if audience is None and "aud" in payload:
-            # Application did not specify an audience, but
-            # the token has the 'aud' claim
-            raise InvalidAudienceError("Invalid audience")
-
         audience_claims = payload["aud"]
 
         if isinstance(audience_claims, str):
@@ -202,7 +201,7 @@
         if isinstance(audience, str):
             audience = [audience]
 
-        if not any(aud in audience_claims for aud in audience):
+        if all(aud not in audience_claims for aud in audience):
             raise InvalidAudienceError("Invalid audience")
 
     def _validate_iss(self, payload, issuer):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/jwt/jwks_client.py 
new/PyJWT-2.3.0/jwt/jwks_client.py
--- old/PyJWT-2.1.0/jwt/jwks_client.py  2021-04-28 13:23:40.000000000 +0200
+++ new/PyJWT-2.3.0/jwt/jwks_client.py  2021-10-06 12:37:16.000000000 +0200
@@ -26,13 +26,13 @@
 
     def get_signing_keys(self) -> List[PyJWK]:
         jwk_set = self.get_jwk_set()
-        signing_keys = []
+        signing_keys = [
+            jwk_set_key
+            for jwk_set_key in jwk_set.keys
+            if jwk_set_key.public_key_use in ["sig", None] and 
jwk_set_key.key_id
+        ]
 
-        for jwk_set_key in jwk_set.keys:
-            if jwk_set_key.public_key_use == "sig" and jwk_set_key.key_id:
-                signing_keys.append(jwk_set_key)
-
-        if len(signing_keys) == 0:
+        if not signing_keys:
             raise PyJWKClientError("The JWKS endpoint did not contain any 
signing keys")
 
         return signing_keys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/jwt/utils.py new/PyJWT-2.3.0/jwt/utils.py
--- old/PyJWT-2.1.0/jwt/utils.py        2020-12-21 17:55:46.000000000 +0100
+++ new/PyJWT-2.3.0/jwt/utils.py        2021-10-06 12:37:16.000000000 +0200
@@ -59,8 +59,7 @@
 
 def number_to_bytes(num: int, num_bytes: int) -> bytes:
     padded_hex = "%0*x" % (2 * num_bytes, num)
-    big_endian = binascii.a2b_hex(padded_hex.encode("ascii"))
-    return big_endian
+    return binascii.a2b_hex(padded_hex.encode("ascii"))
 
 
 def bytes_to_number(string: bytes) -> int:
@@ -72,7 +71,7 @@
     byte_length = 0
 
     while remaining != 0:
-        remaining = remaining >> 8
+        remaining >>= 8
         byte_length += 1
 
     return val.to_bytes(byte_length, "big", signed=False)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/setup.cfg new/PyJWT-2.3.0/setup.cfg
--- old/PyJWT-2.1.0/setup.cfg   2021-04-28 14:01:11.610274000 +0200
+++ new/PyJWT-2.3.0/setup.cfg   2021-10-16 17:54:33.865428000 +0200
@@ -44,7 +44,7 @@
        sphinx-rtd-theme
        zope.interface
 crypto = 
-       cryptography>=3.3.1,<4.0.0
+       cryptography>=3.3.1
 tests = 
        pytest>=6.0.0,<7.0.0
        coverage[toml]==5.0.4
@@ -52,7 +52,7 @@
        sphinx
        sphinx-rtd-theme
        zope.interface
-       cryptography>=3.3.1,<4.0.0
+       cryptography>=3.3.1
        pytest>=6.0.0,<7.0.0
        coverage[toml]==5.0.4
        mypy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/keys/jwk_okp_key_Ed448.json 
new/PyJWT-2.3.0/tests/keys/jwk_okp_key_Ed448.json
--- old/PyJWT-2.1.0/tests/keys/jwk_okp_key_Ed448.json   1970-01-01 
01:00:00.000000000 +0100
+++ new/PyJWT-2.3.0/tests/keys/jwk_okp_key_Ed448.json   2021-10-06 
12:37:16.000000000 +0200
@@ -0,0 +1,9 @@
+{
+  "kty": "OKP",
+  "kid": "sig_ed448_01",
+  "crv": "Ed448",
+  "use": "sig",
+  "x": 
"kvqP7TzMosCQCpNcW8qY2HmVmpPYUEIGn-sQWQgoWlAZbWpnXpXqAT6yMoYA08pkJm7P_HKZoHwA",
+  "d": 
"Zh5xx0r_0tq39xj-8jGuCwAA6wsDim2ME7cX_iXzqDRgPN8lsZZHu60AO7m31Fa4NtHO07eU63q8",
+  "alg": "EdDSA"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/keys/jwk_okp_pub_Ed448.json 
new/PyJWT-2.3.0/tests/keys/jwk_okp_pub_Ed448.json
--- old/PyJWT-2.1.0/tests/keys/jwk_okp_pub_Ed448.json   1970-01-01 
01:00:00.000000000 +0100
+++ new/PyJWT-2.3.0/tests/keys/jwk_okp_pub_Ed448.json   2021-10-06 
12:37:16.000000000 +0200
@@ -0,0 +1,8 @@
+{
+  "kty": "OKP",
+  "kid": "sig_ed448_01",
+  "crv": "Ed448",
+  "use": "sig",
+  "x": 
"kvqP7TzMosCQCpNcW8qY2HmVmpPYUEIGn-sQWQgoWlAZbWpnXpXqAT6yMoYA08pkJm7P_HKZoHwA",
+  "alg": "EdDSA"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/test_algorithms.py 
new/PyJWT-2.3.0/tests/test_algorithms.py
--- old/PyJWT-2.1.0/tests/test_algorithms.py    2021-04-28 13:43:48.000000000 
+0200
+++ new/PyJWT-2.3.0/tests/test_algorithms.py    2021-10-06 12:37:16.000000000 
+0200
@@ -11,12 +11,7 @@
 from .utils import crypto_required, key_path
 
 if has_crypto:
-    from jwt.algorithms import (
-        ECAlgorithm,
-        Ed25519Algorithm,
-        RSAAlgorithm,
-        RSAPSSAlgorithm,
-    )
+    from jwt.algorithms import ECAlgorithm, OKPAlgorithm, RSAAlgorithm, 
RSAPSSAlgorithm
 
 
 class TestAlgorithms:
@@ -667,12 +662,12 @@
 
 
 @crypto_required
-class TestEd25519Algorithms:
+class TestOKPAlgorithms:
     hello_world_sig = 
b"Qxa47mk/azzUgmY2StAOguAd4P7YBLpyCfU3JdbaiWnXM4o4WibXwmIHvNYgN3frtE2fcyd8OYEaOiD/KiwkCg=="
     hello_world = b"Hello World!"
 
-    def test_ed25519_should_reject_non_string_key(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_should_reject_non_string_key(self):
+        algo = OKPAlgorithm()
 
         with pytest.raises(TypeError):
             algo.prepare_key(None)
@@ -683,14 +678,14 @@
         with open(key_path("testkey_ed25519.pub")) as keyfile:
             algo.prepare_key(keyfile.read())
 
-    def test_ed25519_should_accept_unicode_key(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_should_accept_unicode_key(self):
+        algo = OKPAlgorithm()
 
         with open(key_path("testkey_ed25519")) as ec_key:
             algo.prepare_key(ec_key.read())
 
-    def test_ed25519_sign_should_generate_correct_signature_value(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_sign_should_generate_correct_signature_value(self):
+        algo = OKPAlgorithm()
 
         jwt_message = self.hello_world
 
@@ -706,8 +701,8 @@
         result = algo.verify(jwt_message, jwt_pub_key, expected_sig)
         assert result
 
-    def test_ed25519_verify_should_return_false_if_signature_invalid(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_verify_should_return_false_if_signature_invalid(self):
+        algo = OKPAlgorithm()
 
         jwt_message = self.hello_world
         jwt_sig = base64.b64decode(self.hello_world_sig)
@@ -720,8 +715,8 @@
         result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
         assert not result
 
-    def test_ed25519_verify_should_return_true_if_signature_valid(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_verify_should_return_true_if_signature_valid(self):
+        algo = OKPAlgorithm()
 
         jwt_message = self.hello_world
         jwt_sig = base64.b64decode(self.hello_world_sig)
@@ -732,8 +727,8 @@
         result = algo.verify(jwt_message, jwt_pub_key, jwt_sig)
         assert result
 
-    def test_ed25519_prepare_key_should_be_idempotent(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_prepare_key_should_be_idempotent(self):
+        algo = OKPAlgorithm()
 
         with open(key_path("testkey_ed25519.pub")) as keyfile:
             jwt_pub_key_first = algo.prepare_key(keyfile.read())
@@ -741,8 +736,8 @@
 
         assert jwt_pub_key_first == jwt_pub_key_second
 
-    def test_ed25519_jwk_private_key_should_parse_and_verify(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_jwk_private_key_should_parse_and_verify(self):
+        algo = OKPAlgorithm()
 
         with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
             key = algo.from_jwk(keyfile.read())
@@ -750,8 +745,19 @@
         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()
+    def 
test_okp_ed25519_jwk_private_key_should_parse_and_verify_with_private_key_as_is(
+        self,
+    ):
+        algo = OKPAlgorithm()
+
+        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, signature)
+
+    def test_okp_ed25519_jwk_public_key_should_parse_and_verify(self):
+        algo = OKPAlgorithm()
 
         with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
             priv_key = algo.from_jwk(keyfile.read())
@@ -762,8 +768,8 @@
         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()
+    def test_okp_ed25519_jwk_fails_on_invalid_json(self):
+        algo = OKPAlgorithm()
 
         with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile:
             valid_pub = json.loads(keyfile.read())
@@ -790,6 +796,12 @@
         with pytest.raises(InvalidKeyError):
             algo.from_jwk(v)
 
+        # Invalid crv, "Ed448"
+        v = valid_pub.copy()
+        v["crv"] = "Ed448"
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(v)
+
         # Missing x
         v = valid_pub.copy()
         del v["x"]
@@ -808,8 +820,8 @@
         with pytest.raises(InvalidKeyError):
             algo.from_jwk(v)
 
-    def test_ed25519_to_jwk_works_with_from_jwk(self):
-        algo = Ed25519Algorithm()
+    def test_okp_ed25519_to_jwk_works_with_from_jwk(self):
+        algo = OKPAlgorithm()
 
         with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
             priv_key_1 = algo.from_jwk(keyfile.read())
@@ -827,8 +839,111 @@
         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()
+    def test_okp_to_jwk_raises_exception_on_invalid_key(self):
+        algo = OKPAlgorithm()
 
         with pytest.raises(InvalidKeyError):
             algo.to_jwk({"not": "a valid key"})
+
+    def test_okp_ed448_jwk_private_key_should_parse_and_verify(self):
+        algo = OKPAlgorithm()
+
+        with open(key_path("jwk_okp_key_Ed448.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_okp_ed448_jwk_private_key_should_parse_and_verify_with_private_key_as_is(
+        self,
+    ):
+        algo = OKPAlgorithm()
+
+        with open(key_path("jwk_okp_key_Ed448.json")) as keyfile:
+            key = algo.from_jwk(keyfile.read())
+
+        signature = algo.sign(b"Hello World!", key)
+        assert algo.verify(b"Hello World!", key, signature)
+
+    def test_okp_ed448_jwk_public_key_should_parse_and_verify(self):
+        algo = OKPAlgorithm()
+
+        with open(key_path("jwk_okp_key_Ed448.json")) as keyfile:
+            priv_key = algo.from_jwk(keyfile.read())
+
+        with open(key_path("jwk_okp_pub_Ed448.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_okp_ed448_jwk_fails_on_invalid_json(self):
+        algo = OKPAlgorithm()
+
+        with open(key_path("jwk_okp_pub_Ed448.json")) as keyfile:
+            valid_pub = json.loads(keyfile.read())
+        with open(key_path("jwk_okp_key_Ed448.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 "Ed448"
+        v = valid_pub.copy()
+        v["crv"] = "P-256"
+        with pytest.raises(InvalidKeyError):
+            algo.from_jwk(v)
+
+        # Invalid crv, "Ed25519"
+        v = valid_pub.copy()
+        v["crv"] = "Ed25519"
+        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_okp_ed448_to_jwk_works_with_from_jwk(self):
+        algo = OKPAlgorithm()
+
+        with open(key_path("jwk_okp_key_Ed448.json")) as keyfile:
+            priv_key_1 = algo.from_jwk(keyfile.read())
+
+        with open(key_path("jwk_okp_pub_Ed448.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)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/test_api_jwk.py 
new/PyJWT-2.3.0/tests/test_api_jwk.py
--- old/PyJWT-2.1.0/tests/test_api_jwk.py       2021-04-28 13:23:40.000000000 
+0200
+++ new/PyJWT-2.3.0/tests/test_api_jwk.py       2021-10-06 12:37:16.000000000 
+0200
@@ -9,12 +9,7 @@
 from .utils import crypto_required, key_path
 
 if has_crypto:
-    from jwt.algorithms import (
-        ECAlgorithm,
-        Ed25519Algorithm,
-        HMACAlgorithm,
-        RSAAlgorithm,
-    )
+    from jwt.algorithms import ECAlgorithm, HMACAlgorithm, OKPAlgorithm, 
RSAAlgorithm
 
 
 class TestPyJWK:
@@ -166,7 +161,7 @@
         jwk = PyJWK.from_dict(key_data)
 
         assert jwk.key_type == "OKP"
-        assert isinstance(jwk.Algorithm, Ed25519Algorithm)
+        assert isinstance(jwk.Algorithm, OKPAlgorithm)
 
     @crypto_required
     def test_from_dict_should_throw_exception_if_arg_is_invalid(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/test_api_jws.py 
new/PyJWT-2.3.0/tests/test_api_jws.py
--- old/PyJWT-2.1.0/tests/test_api_jws.py       2021-04-28 13:47:41.000000000 
+0200
+++ new/PyJWT-2.3.0/tests/test_api_jws.py       2021-08-08 21:28:32.000000000 
+0200
@@ -166,6 +166,32 @@
         exception = context.value
         assert str(exception) == "Algorithm not supported"
 
+    def test_encode_with_headers_alg_none(self, jws, payload):
+        msg = jws.encode(payload, key=None, headers={"alg": "none"})
+        with pytest.raises(DecodeError) as context:
+            jws.decode(msg, algorithms=["none"])
+        assert str(context.value) == "Signature verification failed"
+
+    @crypto_required
+    def test_encode_with_headers_alg_es256(self, jws, payload):
+        with open(key_path("testkey_ec.priv"), "rb") as ec_priv_file:
+            priv_key = load_pem_private_key(ec_priv_file.read(), password=None)
+        with open(key_path("testkey_ec.pub"), "rb") as ec_pub_file:
+            pub_key = load_pem_public_key(ec_pub_file.read())
+
+        msg = jws.encode(payload, priv_key, headers={"alg": "ES256"})
+        assert b"hello world" == jws.decode(msg, pub_key, algorithms=["ES256"])
+
+    @crypto_required
+    def test_encode_with_alg_hs256_and_headers_alg_es256(self, jws, payload):
+        with open(key_path("testkey_ec.priv"), "rb") as ec_priv_file:
+            priv_key = load_pem_private_key(ec_priv_file.read(), password=None)
+        with open(key_path("testkey_ec.pub"), "rb") as ec_pub_file:
+            pub_key = load_pem_public_key(ec_pub_file.read())
+
+        msg = jws.encode(payload, priv_key, algorithm="HS256", headers={"alg": 
"ES256"})
+        assert b"hello world" == jws.decode(msg, pub_key, algorithms=["ES256"])
+
     def test_decode_algorithm_param_should_be_case_sensitive(self, jws):
         example_jws = (
             "eyJhbGciOiJoczI1NiIsInR5cCI6IkpXVCJ9"  # alg = hs256
@@ -624,6 +650,65 @@
         assert "testheader" in header_obj
         assert header_obj["testheader"] == headers["testheader"]
 
+    def test_encode_with_typ(self, jws):
+        payload = """
+        {
+          "iss": "https://scim.example.com";,
+          "iat": 1458496404,
+          "jti": "4d3559ec67504aaba65d40b0363faad8",
+          "aud": [
+            "https://scim.example.com/Feeds/98d52461fa5bbc879593b7754";,
+            "https://scim.example.com/Feeds/5d7604516b1d08641d7676ee7";
+          ],
+          "events": {
+            "urn:ietf:params:scim:event:create": {
+              "ref":
+                  "https://scim.example.com/Users/44f6142df96bd6ab61e7521d9";,
+              "attributes": ["id", "name", "userName", "password", "emails"]
+            }
+          }
+        }
+        """
+        token = jws.encode(
+            payload.encode("utf-8"), "secret", headers={"typ": "secevent+jwt"}
+        )
+
+        header = token[0 : token.index(".")].encode()
+        header = base64url_decode(header)
+        header_obj = json.loads(header)
+
+        assert "typ" in header_obj
+        assert header_obj["typ"] == "secevent+jwt"
+
+    def test_encode_with_typ_empty_string(self, jws, payload):
+        token = jws.encode(payload, "secret", headers={"typ": ""})
+
+        header = token[0 : token.index(".")].encode()
+        header = base64url_decode(header)
+        header_obj = json.loads(header)
+
+        assert "typ" not in header_obj
+
+    def test_encode_with_typ_none(self, jws, payload):
+        token = jws.encode(payload, "secret", headers={"typ": None})
+
+        header = token[0 : token.index(".")].encode()
+        header = base64url_decode(header)
+        header_obj = json.loads(header)
+
+        assert "typ" not in header_obj
+
+    def test_encode_with_typ_without_keywords(self, jws, payload):
+        headers = {"foo": "bar"}
+        token = jws.encode(payload, "secret", "HS256", headers, None)
+
+        header = token[0 : token.index(".")].encode()
+        header = base64url_decode(header)
+        header_obj = json.loads(header)
+
+        assert "foo" in header_obj
+        assert header_obj["foo"] == "bar"
+
     def test_encode_fails_on_invalid_kid_types(self, jws, payload):
         with pytest.raises(InvalidTokenError) as exc:
             jws.encode(payload, "secret", headers={"kid": 123})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/test_api_jwt.py 
new/PyJWT-2.3.0/tests/test_api_jwt.py
--- old/PyJWT-2.1.0/tests/test_api_jwt.py       2021-04-28 13:47:41.000000000 
+0200
+++ new/PyJWT-2.3.0/tests/test_api_jwt.py       2021-10-16 14:23:39.000000000 
+0200
@@ -1,7 +1,7 @@
 import json
 import time
 from calendar import timegm
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
 from decimal import Decimal
 
 import pytest
@@ -16,6 +16,7 @@
     InvalidIssuerError,
     MissingRequiredClaimError,
 )
+from jwt.utils import base64url_decode
 
 from .utils import crypto_required, key_path, utc_timestamp
 
@@ -167,6 +168,32 @@
                 lambda: jwt.encode(t, "secret", algorithms=["HS256"]),
             )
 
+    def test_encode_with_typ(self, jwt):
+        payload = {
+            "iss": "https://scim.example.com";,
+            "iat": 1458496404,
+            "jti": "4d3559ec67504aaba65d40b0363faad8",
+            "aud": [
+                "https://scim.example.com/Feeds/98d52461fa5bbc879593b7754";,
+                "https://scim.example.com/Feeds/5d7604516b1d08641d7676ee7";,
+            ],
+            "events": {
+                "urn:ietf:params:scim:event:create": {
+                    "ref": 
"https://scim.example.com/Users/44f6142df96bd6ab61e7521d9";,
+                    "attributes": ["id", "name", "userName", "password", 
"emails"],
+                }
+            },
+        }
+        token = jwt.encode(
+            payload, "secret", algorithm="HS256", headers={"typ": 
"secevent+jwt"}
+        )
+        header = token[0 : token.index(".")].encode()
+        header = base64url_decode(header)
+        header_obj = json.loads(header)
+
+        assert "typ" in header_obj
+        assert header_obj["typ"] == "secevent+jwt"
+
     def test_decode_raises_exception_if_exp_is_not_int(self, jwt):
         # >>> jwt.encode({'exp': 'not-an-int'}, 'secret')
         example_jwt = (
@@ -202,9 +229,19 @@
         with pytest.raises(DecodeError):
             jwt.decode(example_jwt, "secret", algorithms=["HS256"])
 
+    def test_decode_raises_exception_if_aud_is_none(self, jwt):
+        # >>> jwt.encode({'aud': None}, 'secret')
+        example_jwt = (
+            "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9."
+            "eyJhdWQiOm51bGx9."
+            "-Peqc-pTugGvrc5C8Bnl0-X1V_5fv-aVb_7y7nGBVvQ"
+        )
+        decoded = jwt.decode(example_jwt, "secret", algorithms=["HS256"])
+        assert decoded["aud"] is None
+
     def test_encode_datetime(self, jwt):
         secret = "secret"
-        current_datetime = datetime.utcnow()
+        current_datetime = datetime.now(tz=timezone.utc)
         payload = {
             "exp": current_datetime,
             "iat": current_datetime,
@@ -413,6 +450,15 @@
 
         assert exc.value.claim == "aud"
 
+    def test_raise_exception_token_with_aud_none_and_without_audience(self, 
jwt):
+        payload = {"some": "payload", "aud": None}
+        token = jwt.encode(payload, "secret")
+
+        with pytest.raises(MissingRequiredClaimError) as exc:
+            jwt.decode(token, "secret", audience="urn:me", 
algorithms=["HS256"])
+
+        assert exc.value.claim == "aud"
+
     def test_check_issuer_when_valid(self, jwt):
         issuer = "urn:foo"
         payload = {"some": "payload", "iss": "urn:foo"}
@@ -442,7 +488,7 @@
     def test_skip_check_exp(self, jwt):
         payload = {
             "some": "payload",
-            "exp": datetime.utcnow() - timedelta(days=1),
+            "exp": datetime.now(tz=timezone.utc) - timedelta(days=1),
         }
         token = jwt.encode(payload, "secret")
         jwt.decode(
@@ -519,7 +565,7 @@
     def test_skip_check_iat(self, jwt):
         payload = {
             "some": "payload",
-            "iat": datetime.utcnow() + timedelta(days=1),
+            "iat": datetime.now(tz=timezone.utc) + timedelta(days=1),
         }
         token = jwt.encode(payload, "secret")
         jwt.decode(
@@ -532,7 +578,7 @@
     def test_skip_check_nbf(self, jwt):
         payload = {
             "some": "payload",
-            "nbf": datetime.utcnow() + timedelta(days=1),
+            "nbf": datetime.now(tz=timezone.utc) + timedelta(days=1),
         }
         token = jwt.encode(payload, "secret")
         jwt.decode(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/test_jwks_client.py 
new/PyJWT-2.3.0/tests/test_jwks_client.py
--- old/PyJWT-2.1.0/tests/test_jwks_client.py   2021-04-28 13:23:40.000000000 
+0200
+++ new/PyJWT-2.3.0/tests/test_jwks_client.py   2021-10-06 12:37:16.000000000 
+0200
@@ -61,6 +61,20 @@
         assert len(signing_keys) == 1
         assert isinstance(signing_keys[0], PyJWK)
 
+    def test_get_signing_keys_if_no_use_provided(self):
+        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json";
+
+        mocked_key = RESPONSE_DATA["keys"][0].copy()
+        del mocked_key["use"]
+        response = {"keys": [mocked_key]}
+
+        with mocked_response(response):
+            jwks_client = PyJWKClient(url)
+            signing_keys = jwks_client.get_signing_keys()
+
+        assert len(signing_keys) == 1
+        assert isinstance(signing_keys[0], PyJWK)
+
     def test_get_signing_keys_raises_if_none_found(self):
         url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json";
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-2.1.0/tests/utils.py 
new/PyJWT-2.3.0/tests/utils.py
--- old/PyJWT-2.1.0/tests/utils.py      2020-12-21 17:55:46.000000000 +0100
+++ new/PyJWT-2.3.0/tests/utils.py      2021-10-06 12:37:16.000000000 +0200
@@ -1,6 +1,6 @@
 import os
 from calendar import timegm
-from datetime import datetime
+from datetime import datetime, timezone
 
 import pytest
 
@@ -8,7 +8,7 @@
 
 
 def utc_timestamp():
-    return timegm(datetime.utcnow().utctimetuple())
+    return timegm(datetime.now(tz=timezone.utc).utctimetuple())
 
 
 def key_path(key_name):

Reply via email to