Your message dated Sun, 20 Sep 2026 08:34:26 +0000
with message-id <[email protected]>
and subject line Bug#1145158: fixed in pyjwt 2.14.0-1
has caused the Debian Bug report #1145158,
regarding pyjwt: FTBFS building against python 3.15
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1145158: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1145158
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: src:pyjwt
Version: 2.13.0-1
User: [email protected]
Usertags: python3.15
Tags: patch

Hi!

While rebuilding the python related packages against the python version
3.15rc1 we found that python-botocore fails to build from source[1].

There is an open pull request upstream to fix the problems in the
build[2], we backported that fix to allow packages in our sandbox that build
against pyjwt to be able to build.

Please consider including this patch in your next upload.

Happy hacking,

[1]: 
https://debusine.debian.net/debian/r-python-python3.15/work-request/1072657/
[2]: https://github.com/jpadilla/pyjwt/pull/1181
--
"Can you imagine what I would do if I could do all I can?" -- Sun Tzu
Saludos /\/\ /\ >< `/
commit e58da927aa78125ec53a66dec1755d4b561cd7c0
Author: arpitjain099 <[email protected]>
Date:   Thu Jun 4 07:36:22 2026 +0900

    Normalize base64 alphabet in base64url_decode to avoid Py3.15 FutureWarning
    
    base64url_decode passed input straight to base64.urlsafe_b64decode after
    padding. When the input carried standard-alphabet characters (+ or /),
    Python 3.15 emits FutureWarning about invalid characters in URL-safe
    Base64 data, and CPython has announced it will eventually discard those
    characters (silently corrupting the decoded bytes).
    
    Translate + and / to - and _ before decoding so valid URL-safe input
    never warns and historical standard-alphabet input keeps decoding to the
    same bytes deterministically. The test_compressed_jwt fixture also
    carried a standard-Base64 payload segment (a literal /) rather than the
    URL-safe form an RFC 7515 JWT requires; rebuild it (and its HMAC
    signature) on the URL-safe alphabet.
    
    Fixes #1167
    
    Signed-off-by: arpitjain099 <[email protected]>

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d52f953..f383bdb 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -7,6 +7,17 @@ This project adheres to `Semantic Versioning <https://semver.org/>`__.
 `Unreleased <https://github.com/jpadilla/pyjwt/compare/2.13.0...HEAD>`__
 ------------------------------------------------------------------------
 
+Fixed
+~~~~~
+
+- Normalise the standard Base64 alphabet (``+`` / ``/``) to the URL-safe
+  alphabet in ``base64url_decode`` so valid URL-safe input no longer trips
+  the Python 3.15 ``FutureWarning: invalid character '/' in URL-safe Base64
+  data`` and standard-alphabet input keeps decoding deterministically once
+  CPython starts discarding those characters. Also corrects the
+  ``test_compressed_jwt`` fixture, which carried a standard-Base64 payload
+  segment instead of a URL-safe one (`#1167 <https://github.com/jpadilla/pyjwt/issues/1167>`__).
+
 `v2.13.0 <https://github.com/jpadilla/pyjwt/compare/2.12.1...2.13.0>`__
 -----------------------------------------------------------------------
 
diff --git a/jwt/utils.py b/jwt/utils.py
index 56e89bb..dcb48e6 100644
--- a/jwt/utils.py
+++ b/jwt/utils.py
@@ -22,8 +22,18 @@ def force_bytes(value: Union[bytes, str]) -> bytes:
         raise TypeError("Expected a string value")
 
 
+# Translate the standard Base64 alphabet to the URL-safe alphabet so that
+# callers passing standard-alphabet data (``+`` / ``/``) are normalised before
+# the decode. From Python 3.15 ``base64.urlsafe_b64decode`` emits a
+# ``FutureWarning`` for ``+`` / ``/`` and has announced it will eventually
+# discard those characters, which would silently corrupt the decoded bytes.
+# Normalising up front keeps valid URL-safe input warning-free and keeps the
+# historical lenient handling of standard-alphabet input deterministic.
+_STD_TO_URLSAFE = bytes.maketrans(b"+/", b"-_")
+
+
 def base64url_decode(input: Union[bytes, str]) -> bytes:
-    input_bytes = force_bytes(input)
+    input_bytes = force_bytes(input).translate(_STD_TO_URLSAFE)
 
     rem = len(input_bytes) % 4
 
diff --git a/tests/test_compressed_jwt.py b/tests/test_compressed_jwt.py
index 1968cb4..d80f49e 100644
--- a/tests/test_compressed_jwt.py
+++ b/tests/test_compressed_jwt.py
@@ -19,11 +19,11 @@ def test_decodes_complete_valid_jwt_with_compressed_payload() -> None:
     example_payload = {"hello": "world"}
     example_secret = "secret"
     # payload made with the pako (https://nodeca.github.io/pako/) library in Javascript:
-    # Buffer.from(pako.deflateRaw('{"hello": "world"}')).toString('base64')
+    # Buffer.from(pako.deflateRaw('{"hello": "world"}')).toString('base64url')
     example_jwt = (
         b"eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9"
-        b".q1bKSM3JyVeyUlAqzy/KSVGqBQA="
-        b".08wHYeuh1rJXmcBcMrz6NxmbxAnCQp2rGTKfRNIkxiw="
+        b".q1bKSM3JyVeyUlAqzy_KSVGqBQA"
+        b".AAn1elCJC5MQCYFwTwa2tjjtyLgqLUVU-Y1vFBsU8jo"
     )
     decoded = CompressedPyJWT().decode_complete(
         example_jwt, example_secret, algorithms=["HS256"]
@@ -33,7 +33,7 @@ def test_decodes_complete_valid_jwt_with_compressed_payload() -> None:
         "header": {"alg": "HS256", "typ": "JWT"},
         "payload": example_payload,
         "signature": (
-            b"\xd3\xcc\x07a\xeb\xa1\xd6\xb2W\x99\xc0\\2\xbc\xfa7"
-            b"\x19\x9b\xc4\t\xc2B\x9d\xab\x192\x9fD\xd2$\xc6,"
+            b"\x00\t\xf5zP\x89\x0b\x93\x10\t\x81pO\x06\xb6\xb6"
+            b"8\xed\xc8\xb8*-ET\xf9\x8do\x14\x1b\x14\xf2:"
         ),
     }
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 2ad2314..7d18509 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,10 +1,18 @@
+import base64
+import warnings
 from contextlib import nullcontext
 
 import pytest
 
 from contextlib import AbstractContextManager
 
-from jwt.utils import force_bytes, from_base64url_uint, is_ssh_key, to_base64url_uint
+from jwt.utils import (
+    base64url_decode,
+    force_bytes,
+    from_base64url_uint,
+    is_ssh_key,
+    to_base64url_uint,
+)
 
 
 @pytest.mark.parametrize(
@@ -41,6 +49,34 @@ def test_from_base64url_uint(inputval: bytes, expected: int) -> None:
     assert actual == expected
 
 
+def test_base64url_decode_handles_standard_alphabet() -> None:
+    # The same bytes encoded with the standard ("+/") and the URL-safe ("-_")
+    # alphabets must decode to the same value. ``base64url_decode`` normalises
+    # the standard alphabet so historical callers keep working.
+    raw = bytes(range(256))
+    standard = base64.b64encode(raw)
+    urlsafe = base64.urlsafe_b64encode(raw).rstrip(b"=")
+
+    assert b"+" in standard or b"/" in standard
+    assert base64url_decode(standard) == raw
+    assert base64url_decode(urlsafe) == raw
+
+
+def test_base64url_decode_does_not_warn_on_urlsafe_input() -> None:
+    # Valid URL-safe input must never trigger the Python 3.15+ FutureWarning
+    # about "+"/"/" in URL-safe Base64 data (jpadilla/pyjwt#1167).
+    raw = bytes(range(256))
+    standard = base64.b64encode(raw)
+    urlsafe = base64.urlsafe_b64encode(raw).rstrip(b"=")
+
+    with warnings.catch_warnings():
+        warnings.simplefilter("error", FutureWarning)
+        assert base64url_decode(urlsafe) == raw
+        # A standard-alphabet input is normalised first, so it must also be
+        # decoded without emitting the FutureWarning.
+        assert base64url_decode(standard) == raw
+
+
 def test_force_bytes_raises_error_on_invalid_object() -> None:
     with pytest.raises(TypeError):
         force_bytes({})  # type: ignore[arg-type]

--- End Message ---
--- Begin Message ---
Source: pyjwt
Source-Version: 2.14.0-1
Done: Carsten Schoenert <[email protected]>

We believe that the bug you reported is fixed in the latest version of
pyjwt, which is due to be installed in the Debian FTP archive.

A summary of the changes between this version and the previous one is
attached.

Thank you for reporting the bug, which will now be closed.  If you
have further comments please address them to [email protected],
and the maintainer will reopen the bug report if appropriate.

Debian distribution maintenance software
pp.
Carsten Schoenert <[email protected]> (supplier of updated pyjwt package)

(This message was generated automatically at their request; if you
believe that there is a problem with it please contact the archive
administrators by mailing [email protected])


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Format: 1.8
Date: Sun, 20 Sep 2026 09:52:55 +0200
Source: pyjwt
Architecture: source
Version: 2.14.0-1
Distribution: unstable
Urgency: medium
Maintainer: Debian Python Team <[email protected]>
Changed-By: Carsten Schoenert <[email protected]>
Closes: 1145158
Changes:
 pyjwt (2.14.0-1) unstable; urgency=medium
 .
   * Team upload
   * [316abb3] New upstream version 2.14.0
   * [86d7464] Rebuild patch queue from patch-queue branch
     Added patch:
     Normalize-base64-alphabet-in-base64url_decode-to-avoid-Py.patch
     (Closes: #1145158)
   * [e57d3b3] d/control: Bump debhelper-compat version to 14
Checksums-Sha1:
 638c0f7e6f2578df1431bd61dbe1afdc245506c1 2404 pyjwt_2.14.0-1.dsc
 d0a443c06d4efe093d8178e4434bf05e98a77026 113148 pyjwt_2.14.0.orig.tar.gz
 84dbcb9169928b39df5c7fe454cf0bdee07f2aad 74196 pyjwt_2.14.0-1.debian.tar.xz
 170840cefc2aa1676e5765e3a67877ea23bff0fd 8538 pyjwt_2.14.0-1_amd64.buildinfo
Checksums-Sha256:
 ca0be6767dcc5eb346e67267221708da0542898c61c8d6394171823f989c7a92 2404 
pyjwt_2.14.0-1.dsc
 2bd279e13ae8cdc29b00b22df34005b2ac5767849f18be9789bdd0fad3d770fe 113148 
pyjwt_2.14.0.orig.tar.gz
 d04dc5faa71aa31cea24d60027288a669cb4ab345419185d9a200b1792127426 74196 
pyjwt_2.14.0-1.debian.tar.xz
 b8fe96c646462326a821bf90330cda4fed82529c01dd753c7eb3d2673620579b 8538 
pyjwt_2.14.0-1_amd64.buildinfo
Files:
 07e6810127c00afd13ea6a987ea3b6cc 2404 python optional pyjwt_2.14.0-1.dsc
 f91509507990d3429e7e3e6ef171a3b4 113148 python optional 
pyjwt_2.14.0.orig.tar.gz
 224eef32bcb9c07703bc4022fc0aacba 74196 python optional 
pyjwt_2.14.0-1.debian.tar.xz
 1d18c7282b0833819a9e0d8f1be533d5 8538 python optional 
pyjwt_2.14.0-1_amd64.buildinfo

-----BEGIN PGP SIGNATURE-----

iQJMBAEBCgA2FiEEtw38bxNP7PwBHmKqgwFgFCUdHbAFAmqvkpsYHGMuc2Nob2Vu
ZXJ0QHQtb25saW5lLmRlAAoJEIMBYBQlHR2w3y0P/1lsTyx1TUilUNs1Z1FC3vpT
hk9fC/l1Iu9ZCBs07Mz8Bynr6+L1YeKw5NN9RVMq/ppWiIZCbVa8SK8su778zJP0
HdGI3GbnDKsngUBfXTV2fHh1aicOLap0EGufq4XnZMHpNVAHhsqSBnUtp7Qf67y3
ai0iTFX136rESp9lPWdNU5DnBTk/Jmxe6cCg5Bb8DKc5hf9IG4vLlUgaFxeQe7si
jjrj8AC0SRT3Y1QZNF+T2KqHnAoDKblMvehc/sHSB0gX9q0ynDmB/mbKs+PQvIXf
S+TcDSaXlNUTGpUsk32y5WIdQwpkdiwFGIqFgCUwFihbQtajf5cScYgRjn/ARzLg
ELUslB1C+X02sV6teWCFNhRIXQMhi4FCHAoqx3TBQsfgCzDoCzeX4hzrSigTKHQM
mlP+6UBTQxq1p00CRVd63Dd23PhDsEj8Jsw7jBrxI6VYiKOcx+VKdXtwGad2gYCN
ye761TMDDGwVL560URT/hj7rgyhFgKugIgMwW+LC21lnTRWZCMv/EHy/ckhjo1TK
QfV3+BFXmn/suuXV/dNSZT3SJae6BIEQl3h0tOG64D2kAY2WVdZ+F17W73h6R7sz
0PS+LaH+pWtk0kM95YQ7YxXe/4sgCdrilEF0834bO2UdT/7gu2b5Oy4tSgBxdmV2
JlSG/6xHEEBanIKinTsM
=Cba9
-----END PGP SIGNATURE-----

Attachment: pgpgjSKnsD3u2.pgp
Description: PGP signature


--- End Message ---

Reply via email to