From 23b7736adb61b7f11a44df61fd4249d25429fb0e Mon Sep 17 00:00:00 2001
From: eNBeWe <enbewe+ccc@enbewe.de>
Date: Mon, 12 Aug 2024 21:54:58 +0000
Subject: [PATCH] Fix OIDC token endpoint crash on Debian Buster/Bullseye

The return type of jwt.encode() changed from bytes in v1.x (Buster/Bullseye)
to str in v2.x (Bookworm). This let json.dumps crash on Buster und Bullseye
with "TypeError: Object of type bytes is not JSON serializable".

Flask v1.x (Buster/Bullseye) automatically uses simplejson.dumps instead of
json.dumps if it is installed. simplejson.dumps auto-converts bytes to str per
default. simplejson also happend to be installed in our CI images. This
prevented the bug from surfacing in CI tests. We removed simplejson from our
CI images in an external change.

Co-authored-by: Julian Rother <julian@cccv.de>
---
 tests/models/test_oauth2.py | 1 +
 uffd/models/oauth2.py       | 6 +++++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/tests/models/test_oauth2.py b/tests/models/test_oauth2.py
index 52b6629..3a2dc85 100644
--- a/tests/models/test_oauth2.py
+++ b/tests/models/test_oauth2.py
@@ -71,6 +71,7 @@ class TestOAuth2Key(UffdTestCase):
 
 	def test_encode_jwt(self):
 		jwtdata = self.key.encode_jwt({'aud': 'test', 'foo': 'bar'})
+		self.assertIsInstance(jwtdata, str) # Regression check for #165
 		self.assertEqual(
 			jwt.get_unverified_header(jwtdata),
 			# typ is optional, x5u/x5c/jku/jwk are discoraged by OIDC Core 1.0 spec section 2
diff --git a/uffd/models/oauth2.py b/uffd/models/oauth2.py
index bdd5038..ded3a2d 100644
--- a/uffd/models/oauth2.py
+++ b/uffd/models/oauth2.py
@@ -243,7 +243,11 @@ class OAuth2Key(db.Model):
 	def encode_jwt(self, payload):
 		if not self.active:
 			raise jwt.exceptions.InvalidKeyError(f'Key {self.id} not active')
-		return jwt.encode(payload, key=self.private_key, algorithm=self.algorithm, headers={'kid': self.id})
+		res = jwt.encode(payload, key=self.private_key, algorithm=self.algorithm, headers={'kid': self.id})
+		# pyjwt pre-v2 compat (Buster/Bullseye)
+		if isinstance(res, bytes):
+			res = res.decode()
+		return res
 
 	# Hash algorithm for at_hash/c_hash from OpenID Connect Core 1.0 section 3.1.3.6
 	def oidc_hash(self, value):
-- 
GitLab