truelayer-signing 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/Gemfile +2 -1
- data/lib/truelayer-signing/jwt.rb +49 -12
- data/lib/truelayer-signing/verifier.rb +1 -18
- data/test/test-truelayer-signing.rb +4 -16
- data/truelayer-signing.gemspec +4 -4
- metadata +6 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a85a356c6af43716627f510dc07d9fb08e400dab2ff005dc1bdd55d32d074012
|
|
4
|
+
data.tar.gz: 5f3ad1c1b8402e2fbfa39323f5c8a95682285284030e675ea94c3c66034ddf6d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79c0a907ed21bfd4987c3ce74bdb4bf07fe1a7c5be6560e6766c7d6a5a34f9f2b5f6c91a14765c4bac799813e7174be3f200aef10ef1f894fa8b52ab015cce5d
|
|
7
|
+
data.tar.gz: 9cddb3eeda2211d0409430639b0a62fb603290500b2b5a6d4f6d2b078db5f4c79ed2fada5f9bd5a1bb0e66c073e084be57e8a96ad982b81b1ee49db2e1eb7534
|
data/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
- ...
|
|
11
11
|
|
|
12
|
+
## [0.3.0] – 2026-06-16
|
|
13
|
+
|
|
14
|
+
- Require Ruby `>= 3.2`
|
|
15
|
+
- Update pinned version for `jwt` from `~> 2.7.0` to `~> 3.2.0`
|
|
16
|
+
|
|
12
17
|
## [0.2.3] – 2024-07-29
|
|
13
18
|
|
|
14
19
|
- Fix `Gemspec/AddRuntimeDependency` linter error
|
|
@@ -26,7 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
26
31
|
## [0.2.0] – 2023-06-13
|
|
27
32
|
|
|
28
33
|
- Add support for signature verification using a JWKS: `TrueLayerSigning.verify_with_jwks(jwks)`
|
|
29
|
-
|
|
34
|
+
and `TrueLayerSigning.extract_jws_header(signature)`.
|
|
30
35
|
|
|
31
36
|
## [0.1.2] – 2023-05-19
|
|
32
37
|
|
data/Gemfile
CHANGED
|
@@ -6,23 +6,52 @@
|
|
|
6
6
|
module JWT
|
|
7
7
|
module_function
|
|
8
8
|
|
|
9
|
+
class TrueLayerToken < Token
|
|
10
|
+
# See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/token.rb#L61-L63
|
|
11
|
+
def encoded_payload
|
|
12
|
+
@encoded_payload ||= ::JWT::Base64.url_encode(payload)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
9
16
|
class TrueLayerEncode < Encode
|
|
17
|
+
# See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/encode.rb#L15-L19
|
|
18
|
+
def initialize(options)
|
|
19
|
+
super
|
|
20
|
+
|
|
21
|
+
@token = TrueLayerToken.new(payload: options[:payload], header: options[:headers])
|
|
22
|
+
@key = options[:key]
|
|
23
|
+
@algorithm = options[:algorithm]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class TrueLayerEncodedToken < EncodedToken
|
|
10
28
|
private
|
|
11
29
|
|
|
12
|
-
# See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/
|
|
13
|
-
def
|
|
14
|
-
|
|
30
|
+
# See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/encoded_token.rb#L189-L198
|
|
31
|
+
def decode_payload
|
|
32
|
+
raise JWT::DecodeError, "Encoded payload is empty" if encoded_payload == ""
|
|
33
|
+
|
|
34
|
+
if unencoded_payload?
|
|
35
|
+
verify_claims!(crit: ["b64"])
|
|
36
|
+
return parse_unencoded(encoded_payload)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
::JWT::Base64.url_decode(encoded_payload || "")
|
|
15
40
|
end
|
|
16
41
|
end
|
|
17
42
|
|
|
18
43
|
class TrueLayerDecode < Decode
|
|
19
|
-
|
|
44
|
+
# See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/decode.rb#L22-L30
|
|
45
|
+
def initialize(jwt, key, verify, options, &keyfinder)
|
|
46
|
+
super
|
|
20
47
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
raise JWT::DecodeError, "Nil JSON web token" unless jwt
|
|
49
|
+
|
|
50
|
+
@token = TrueLayerEncodedToken.new(jwt)
|
|
51
|
+
@key = key
|
|
52
|
+
@options = options
|
|
53
|
+
@verify = verify
|
|
54
|
+
@keyfinder = keyfinder
|
|
26
55
|
end
|
|
27
56
|
end
|
|
28
57
|
|
|
@@ -35,8 +64,16 @@ module JWT
|
|
|
35
64
|
).segments
|
|
36
65
|
end
|
|
37
66
|
|
|
38
|
-
# rubocop:disable Style/
|
|
39
|
-
def truelayer_decode(jwt, key, verify = true, options, &keyfinder)
|
|
67
|
+
# rubocop:disable Style/OptionalBooleanParameter, Naming/BlockForwarding
|
|
68
|
+
def truelayer_decode(jwt, key, verify = true, options = {}, &keyfinder)
|
|
69
|
+
# Support both `truelayer_decode(jwt, key, options)` and
|
|
70
|
+
# `truelayer_decode(jwt, key, verify, options)` call styles by treating a
|
|
71
|
+
# Hash in the `verify` position as the `options` argument.
|
|
72
|
+
if verify.is_a?(Hash)
|
|
73
|
+
options = verify
|
|
74
|
+
verify = true
|
|
75
|
+
end
|
|
76
|
+
|
|
40
77
|
TrueLayerDecode.new(
|
|
41
78
|
jwt,
|
|
42
79
|
key,
|
|
@@ -45,5 +82,5 @@ module JWT
|
|
|
45
82
|
&keyfinder
|
|
46
83
|
).decode_segments
|
|
47
84
|
end
|
|
48
|
-
# rubocop:enable Style/
|
|
85
|
+
# rubocop:enable Style/OptionalBooleanParameter, Naming/BlockForwarding
|
|
49
86
|
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module TrueLayerSigning
|
|
4
4
|
class Verifier < JwsBase
|
|
5
|
-
EXPECTED_EC_KEY_COORDS_LENGTH = 66
|
|
6
|
-
|
|
7
5
|
attr_reader :required_headers, :key_type, :key_value
|
|
8
6
|
|
|
9
7
|
def initialize(args)
|
|
@@ -109,27 +107,12 @@ module TrueLayerSigning
|
|
|
109
107
|
|
|
110
108
|
raise(Error, "JWKS does not include given `kid` value") unless jwk
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
JWT::JWK::EC.import(valid_jwk).public_key
|
|
110
|
+
JWT::JWK::EC.import(jwk).public_key
|
|
115
111
|
else
|
|
116
112
|
raise(Error, "Type of public key not recognised")
|
|
117
113
|
end
|
|
118
114
|
end
|
|
119
115
|
|
|
120
|
-
def apply_zero_padding_as_needed(jwk)
|
|
121
|
-
valid_jwk = jwk.clone
|
|
122
|
-
|
|
123
|
-
%i(x y).each do |elem|
|
|
124
|
-
coords = Base64.urlsafe_decode64(valid_jwk[elem])
|
|
125
|
-
diff = EXPECTED_EC_KEY_COORDS_LENGTH - coords.length
|
|
126
|
-
|
|
127
|
-
valid_jwk[elem] = Base64.urlsafe_encode64(("\x00" * diff) + coords) if diff.positive?
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
valid_jwk
|
|
131
|
-
end
|
|
132
|
-
|
|
133
116
|
def validate_required_headers!(headers)
|
|
134
117
|
raise(Error, "Signature missing required header(s)") if required_headers&.any? do |key|
|
|
135
118
|
!headers.key?(key.downcase)
|
|
@@ -123,7 +123,7 @@ class TrueLayerSigningTest < Minitest::Test
|
|
|
123
123
|
body = { currency: "GBP", max_amount_in_minor: 50_000_00, name: "Foo???" }.to_json
|
|
124
124
|
idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
|
|
125
125
|
path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
|
|
126
|
-
tl_signature = read_file("../../test-resources/tl-signature.txt")
|
|
126
|
+
tl_signature = read_file("../../test-resources/tl-signature.txt").strip
|
|
127
127
|
|
|
128
128
|
result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
|
|
129
129
|
.set_method(:post)
|
|
@@ -403,7 +403,7 @@ class TrueLayerSigningTest < Minitest::Test
|
|
|
403
403
|
end
|
|
404
404
|
|
|
405
405
|
def test_verify_with_jwks_should_succeed
|
|
406
|
-
hook_signature = read_file("../../test-resources/webhook-signature.txt")
|
|
406
|
+
hook_signature = read_file("../../test-resources/webhook-signature.txt").strip
|
|
407
407
|
jwks = read_file("../../test-resources/jwks.json")
|
|
408
408
|
body = { event_type: "example", event_id: "18b2842b-a57b-4887-a0a6-d3c7c36f1020" }.to_json
|
|
409
409
|
|
|
@@ -418,18 +418,6 @@ class TrueLayerSigningTest < Minitest::Test
|
|
|
418
418
|
|
|
419
419
|
def test_verify_with_jwks_with_zero_padding_missing_should_succeed
|
|
420
420
|
jwks = read_file("resources/missing-zero-padding-test-jwks.json")
|
|
421
|
-
|
|
422
|
-
# JWKS with EC key missing zero padded coords is not supported by `jwt` gem
|
|
423
|
-
|
|
424
|
-
jwks_as_json = JSON.parse(jwks, symbolize_names: true)
|
|
425
|
-
jwk_missing_padding = jwks_as_json[:keys].find { |e| e[:kty] == "EC" }
|
|
426
|
-
imported_jwk = JWT::JWK::EC.import(jwk_missing_padding)
|
|
427
|
-
|
|
428
|
-
error = assert_raises(OpenSSL::PKey::EC::Point::Error) { imported_jwk.public_key.check_key }
|
|
429
|
-
assert_equal("EC_POINT_bn2point: invalid encoding", error.message)
|
|
430
|
-
|
|
431
|
-
# But supported by `truelayer-signing` using zero padding (prepend)
|
|
432
|
-
|
|
433
421
|
payload = read_file("resources/missing-zero-padding-test-payload.json")
|
|
434
422
|
body = JSON.parse(payload).to_json
|
|
435
423
|
|
|
@@ -438,11 +426,11 @@ class TrueLayerSigningTest < Minitest::Test
|
|
|
438
426
|
.set_path("/a147f26a-f07e-47e3-9526-d52f1f1fdd55")
|
|
439
427
|
.add_header("x-tl-webhook-timestamp", "2023-06-09T15:40:30Z")
|
|
440
428
|
.set_body(body)
|
|
441
|
-
.verify(read_file("resources/missing-zero-padding-test-signature.txt"))
|
|
429
|
+
.verify(read_file("resources/missing-zero-padding-test-signature.txt").strip)
|
|
442
430
|
end
|
|
443
431
|
|
|
444
432
|
def test_verify_with_jwks_with_wrong_timestamp_should_fail
|
|
445
|
-
hook_signature = read_file("../../test-resources/webhook-signature.txt")
|
|
433
|
+
hook_signature = read_file("../../test-resources/webhook-signature.txt").strip
|
|
446
434
|
jwks = read_file("../../test-resources/jwks.json")
|
|
447
435
|
body = { event_type: "example", event_id: "18b2842b-a57b-4887-a0a6-d3c7c36f1020" }.to_json
|
|
448
436
|
|
data/truelayer-signing.gemspec
CHANGED
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
|
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |s|
|
|
6
6
|
s.name = "truelayer-signing"
|
|
7
|
-
s.version = "0.
|
|
7
|
+
s.version = "0.3.0"
|
|
8
8
|
s.summary = "Ruby gem to produce and verify TrueLayer API requests signatures"
|
|
9
9
|
s.description = "TrueLayer provides instant access to open banking to " \
|
|
10
10
|
"easily integrate next-generation payments and financial data into any app." \
|
|
@@ -27,10 +27,10 @@ Gem::Specification.new do |s|
|
|
|
27
27
|
"Rakefile",
|
|
28
28
|
"lib/**/*.*",
|
|
29
29
|
"test/**/*.*",
|
|
30
|
-
"truelayer-signing.gemspec"
|
|
30
|
+
"truelayer-signing.gemspec"
|
|
31
31
|
]
|
|
32
32
|
s.require_path = "lib"
|
|
33
33
|
|
|
34
|
-
s.required_ruby_version = ">= 2
|
|
35
|
-
s.add_dependency("jwt", "~> 2.
|
|
34
|
+
s.required_ruby_version = ">= 3.2"
|
|
35
|
+
s.add_dependency("jwt", "~> 3.2.0")
|
|
36
36
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: truelayer-signing
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kevin Plattret
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: jwt
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 2.
|
|
18
|
+
version: 3.2.0
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 2.
|
|
25
|
+
version: 3.2.0
|
|
27
26
|
description: TrueLayer provides instant access to open banking to easily integrate
|
|
28
27
|
next-generation payments and financial data into any app.This helps easily sign
|
|
29
28
|
TrueLayer API requests using a JSON web signature.
|
|
@@ -58,7 +57,6 @@ licenses:
|
|
|
58
57
|
metadata:
|
|
59
58
|
bug_tracker_uri: https://github.com/TrueLayer/truelayer-signing/issues
|
|
60
59
|
changelog_uri: https://github.com/TrueLayer/truelayer-signing/blob/main/ruby/CHANGELOG.md
|
|
61
|
-
post_install_message:
|
|
62
60
|
rdoc_options: []
|
|
63
61
|
require_paths:
|
|
64
62
|
- lib
|
|
@@ -66,15 +64,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
66
64
|
requirements:
|
|
67
65
|
- - ">="
|
|
68
66
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: '2
|
|
67
|
+
version: '3.2'
|
|
70
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
69
|
requirements:
|
|
72
70
|
- - ">="
|
|
73
71
|
- !ruby/object:Gem::Version
|
|
74
72
|
version: '0'
|
|
75
73
|
requirements: []
|
|
76
|
-
rubygems_version: 3.
|
|
77
|
-
signing_key:
|
|
74
|
+
rubygems_version: 3.6.9
|
|
78
75
|
specification_version: 4
|
|
79
76
|
summary: Ruby gem to produce and verify TrueLayer API requests signatures
|
|
80
77
|
test_files: []
|