Skip to content

Commit 17a4da7

Browse files
committed
Fixed an issue where the SDK would fail to validate webhook signature verification if the signature key was not the one returned in the /public/v3/public-key endpoint (#104)
1 parent 0d09a39 commit 17a4da7

File tree

3 files changed

+17
-2
lines changed

3 files changed

+17
-2
lines changed

CHANGES.rst

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG for LaunchKey Python SDK
44
-----
55

66
* Fixed an issue where the SDK would improperly report which key was used when the encryption and signature keys differed
7+
* Fixed an issue where the SDK would fail to validate webhook signature verification if the signature key was not the one returned in the public-key endpoint
78

89
3.8.0
910
-----

launchkey/transports/jose_auth.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,10 @@ def verify_jwt_request(self, compact_jwt, subject, method, path, body):
572572
:return: The claims of the JWT
573573
"""
574574
try:
575-
self._set_current_kid()
575+
# Ensure key exists in cache, fetch from API by ID otherwise
576+
kid = JWT().unpack(compact_jwt).headers["kid"]
577+
public_key = self._find_key_by_kid(kid)
578+
self._cache_public_key(kid, public_key)
576579
payload = self._get_jwt_payload(compact_jwt)
577580
except JWKESTException as reason:
578581
raise JWTValidationFailure("Unable to parse JWT", reason=reason)

tests/test_jose_auth_transport.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,13 @@ def setUp(self):
453453
self._transport.content_hash_function().hexdigest.return_value = self._content_hash
454454

455455
self._jwt_patch = patch("launchkey.transports.jose_auth.JWT", return_value=MagicMock(spec=JWT)).start()
456-
self._jwt_patch.return_value.unpack.return_value.headers = faux_jwt_headers
456+
self._faux_jwt_headers = {
457+
"alg": "RS512",
458+
"typ": "JWT",
459+
# This should be different than the kid from the transport headers
460+
"kid": "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff"
461+
}
462+
self._jwt_patch.return_value.unpack.return_value.headers = self._faux_jwt_headers
457463

458464
patcher = patch('launchkey.transports.jose_auth.sha256')
459465
patched = patcher.start()
@@ -474,6 +480,11 @@ def test_none_path_returns_payload(self):
474480
actual = self._transport.verify_jwt_request(MagicMock(), self.jwt_payload['sub'], 'POST', None, MagicMock())
475481
self.assertEqual(actual, self.jwt_payload)
476482

483+
def test_jwt_signed_with_key_not_in_cache_fetches_key(self):
484+
self._verify_jwt_request()
485+
self._transport.get.assert_called_once_with(
486+
"/public/v3/public-key/%s" % self._faux_jwt_headers["kid"])
487+
477488
def test_none_path_still_requires_jwt_request_path(self):
478489
del self.jwt_payload['request']['path']
479490
with self.assertRaises(JWTValidationFailure):

0 commit comments

Comments
 (0)