Some JWT-using protocols (such as APNS) require extra items in the JWT header, which the spec allows. So implement a way to feed extras into the header generation instead of always using a static header.

Patch attached.

--
You received this message because you are subscribed to the Google Groups 
"prosody-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/prosody-dev/aAb7yznCG_zV2-0U%40singpolyma-beefy.lan.
# HG changeset patch
# User Stephen Paul Weber <[email protected]>
# Date 1745287965 18000
#      Mon Apr 21 21:12:45 2025 -0500
# Node ID c99380a4044970017ae0f99f92454721f071c18e
# Parent  488483e1d915ecc17dde510c09c1a155ea19c908
util.jwt: support header extras

Some JWT-using protocols (such as APNS) require extra items in the JWT header, which the spec allows. So implement a way to feed extras into the header generation instead of always using a static header.

diff -r 488483e1d915 -r c99380a40449 spec/util_jwt_spec.lua
--- a/spec/util_jwt_spec.lua	Fri Apr 18 12:25:38 2025 +0100
+++ b/spec/util_jwt_spec.lua	Mon Apr 21 21:12:45 2025 -0500
@@ -188,7 +188,7 @@
 	end
 
 	local function do_sign_verify_test(algorithm, signing_key, verifying_key, expect_success, expect_token)
-		local sign = jwt.new_signer(algorithm, signing_key);
+		local sign = jwt.new_signer(algorithm, signing_key, { kid = "testkey" });
 
 		local test_payload = {
 			sub = "1234567890";
@@ -206,7 +206,6 @@
 		do_verify_test(algorithm, verifying_key, token, expect_success and test_payload or false);
 	end
 
-
 	for _, algorithm_tests in ipairs(test_cases) do
 		local algorithm = algorithm_tests.algorithm;
 		local keypairs = algorithm_tests.keys;
diff -r 488483e1d915 -r c99380a40449 util/jwt.lua
--- a/util/jwt.lua	Fri Apr 18 12:25:38 2025 +0100
+++ b/util/jwt.lua	Mon Apr 21 21:12:45 2025 -0500
@@ -29,8 +29,15 @@
 	return signed, signature, bpayload;
 end
 
-local function new_static_header(algorithm_name)
-	return b64url('{"alg":"'..algorithm_name..'","typ":"JWT"}') .. '.';
+local function new_header(algorithm_name, extras)
+	local header = {
+		alg = algorithm_name,
+		typ = "JWT"
+	}
+	for k, v in pairs(extras or {}) do
+		header[k] = v
+	end
+	return b64url(json.encode(header)) .. '.';
 end
 
 local function decode_raw_payload(raw_payload)
@@ -45,13 +52,12 @@
 
 -- HS*** family
 local function new_hmac_algorithm(name)
-	local static_header = new_static_header(name);
-
 	local hmac = hashes["hmac_sha"..name:sub(-3)];
 
-	local function sign(key, payload)
+	local function sign(key, payload, header_extras)
+		local header = new_header(name, header_extras)
 		local encoded_payload = json.encode(payload);
-		local signed = static_header .. b64url(encoded_payload);
+		local signed = header .. b64url(encoded_payload);
 		local signature = hmac(key, signed);
 		return signed .. "." .. b64url(signature);
 	end
@@ -76,12 +82,11 @@
 end
 
 local function new_crypto_algorithm(name, key_type, c_sign, c_verify, sig_encode, sig_decode)
-	local static_header = new_static_header(name);
-
 	return {
-		sign = function (private_key, payload)
+		sign = function (private_key, payload, header_extras)
+			local header = new_header(name, header_extras)
 			local encoded_payload = json.encode(payload);
-			local signed = static_header .. b64url(encoded_payload);
+			local signed = header .. b64url(encoded_payload);
 
 			local signature = c_sign(private_key, signed);
 			if sig_encode then
@@ -173,7 +178,7 @@
 		if not payload.exp then
 			payload.exp = (issued_at or os.time()) + default_ttl;
 		end
-		return sign(key, payload);
+		return sign(key, payload, (options and options.header_extras) or {});
 	end
 end
 

Attachment: signature.asc
Description: PGP signature

Reply via email to