truelayer-signing 0.1.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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +14 -0
  3. data/LICENSE-APACHE +176 -0
  4. data/LICENSE-MIT +21 -0
  5. data/README.md +82 -0
  6. data/Rakefile +8 -0
  7. data/doc/CHANGELOG_md.html +132 -0
  8. data/doc/JWT/Decode.html +97 -0
  9. data/doc/JWT/Encode.html +97 -0
  10. data/doc/JWT/JWK/EC.html +169 -0
  11. data/doc/JWT/JWK.html +91 -0
  12. data/doc/JWT.html +95 -0
  13. data/doc/LICENSE-APACHE.html +177 -0
  14. data/doc/LICENSE-MIT.html +105 -0
  15. data/doc/README_md.html +197 -0
  16. data/doc/Rakefile.html +106 -0
  17. data/doc/TrueLayerSigning/Config.html +211 -0
  18. data/doc/TrueLayerSigning/Error.html +97 -0
  19. data/doc/TrueLayerSigning/JwsBase.html +317 -0
  20. data/doc/TrueLayerSigning/JwsHeader.html +268 -0
  21. data/doc/TrueLayerSigning/Signer.html +186 -0
  22. data/doc/TrueLayerSigning/Verifier.html +327 -0
  23. data/doc/TrueLayerSigning.html +226 -0
  24. data/doc/TrueLayerSigningExamples.html +217 -0
  25. data/doc/created.rid +21 -0
  26. data/doc/css/fonts.css +167 -0
  27. data/doc/css/rdoc.css +662 -0
  28. data/doc/examples/sign-request/Gemfile.html +99 -0
  29. data/doc/examples/sign-request/Gemfile_lock.html +143 -0
  30. data/doc/examples/sign-request/README_md.html +138 -0
  31. data/doc/examples/webhook-server/Gemfile.html +99 -0
  32. data/doc/examples/webhook-server/Gemfile_lock.html +142 -0
  33. data/doc/examples/webhook-server/README_md.html +139 -0
  34. data/doc/fonts/Lato-Light.ttf +0 -0
  35. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  36. data/doc/fonts/Lato-Regular.ttf +0 -0
  37. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  38. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  39. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  40. data/doc/images/add.png +0 -0
  41. data/doc/images/arrow_up.png +0 -0
  42. data/doc/images/brick.png +0 -0
  43. data/doc/images/brick_link.png +0 -0
  44. data/doc/images/bug.png +0 -0
  45. data/doc/images/bullet_black.png +0 -0
  46. data/doc/images/bullet_toggle_minus.png +0 -0
  47. data/doc/images/bullet_toggle_plus.png +0 -0
  48. data/doc/images/date.png +0 -0
  49. data/doc/images/delete.png +0 -0
  50. data/doc/images/find.png +0 -0
  51. data/doc/images/loadingAnimation.gif +0 -0
  52. data/doc/images/macFFBgHack.png +0 -0
  53. data/doc/images/package.png +0 -0
  54. data/doc/images/page_green.png +0 -0
  55. data/doc/images/page_white_text.png +0 -0
  56. data/doc/images/page_white_width.png +0 -0
  57. data/doc/images/plugin.png +0 -0
  58. data/doc/images/ruby.png +0 -0
  59. data/doc/images/tag_blue.png +0 -0
  60. data/doc/images/tag_green.png +0 -0
  61. data/doc/images/transparent.png +0 -0
  62. data/doc/images/wrench.png +0 -0
  63. data/doc/images/wrench_orange.png +0 -0
  64. data/doc/images/zoom.png +0 -0
  65. data/doc/index.html +118 -0
  66. data/doc/js/darkfish.js +84 -0
  67. data/doc/js/navigation.js +105 -0
  68. data/doc/js/navigation.js.gz +0 -0
  69. data/doc/js/search.js +110 -0
  70. data/doc/js/search_index.js +1 -0
  71. data/doc/js/search_index.js.gz +0 -0
  72. data/doc/js/searcher.js +229 -0
  73. data/doc/js/searcher.js.gz +0 -0
  74. data/doc/table_of_contents.html +269 -0
  75. data/examples/sign-request/Gemfile +4 -0
  76. data/examples/sign-request/Gemfile.lock +41 -0
  77. data/examples/sign-request/README.md +27 -0
  78. data/examples/sign-request/main.rb +46 -0
  79. data/examples/webhook-server/Gemfile +3 -0
  80. data/examples/webhook-server/Gemfile.lock +15 -0
  81. data/examples/webhook-server/README.md +30 -0
  82. data/examples/webhook-server/main.rb +98 -0
  83. data/lib/truelayer-signing/config.rb +21 -0
  84. data/lib/truelayer-signing/errors.rb +3 -0
  85. data/lib/truelayer-signing/jwt.rb +20 -0
  86. data/lib/truelayer-signing/signer.rb +34 -0
  87. data/lib/truelayer-signing/utils.rb +90 -0
  88. data/lib/truelayer-signing/verifier.rb +76 -0
  89. data/lib/truelayer-signing.rb +35 -0
  90. data/test/test-truelayer-signing.rb +372 -0
  91. data/truelayer-signing.gemspec +25 -0
  92. metadata +151 -0
@@ -0,0 +1,372 @@
1
+ require "minitest/autorun"
2
+ require "truelayer-signing"
3
+
4
+ CERTIFICATE_ID = "45fc75cf-5649-4134-84b3-192c2c78e990".freeze
5
+ PRIVATE_KEY = File.read(File.expand_path("../../test-resources/ec512-private.pem",
6
+ File.dirname(__FILE__))).freeze
7
+ PUBLIC_KEY = File.read(File.expand_path("../../test-resources/ec512-public.pem",
8
+ File.dirname(__FILE__))).freeze
9
+
10
+ TrueLayerSigning.certificate_id = CERTIFICATE_ID.freeze
11
+ TrueLayerSigning.private_key = PRIVATE_KEY.freeze
12
+
13
+ class TrueLayerSigningTest < Minitest::Test
14
+ def test_full_request_signature_should_succeed
15
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00, name: "Foo???" }.to_json
16
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
17
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
18
+
19
+ tl_signature = TrueLayerSigning.sign_with_pem
20
+ .set_method(:post)
21
+ .set_path(path)
22
+ .add_header("Idempotency-Key", idempotency_key)
23
+ .set_body(body)
24
+ .sign
25
+
26
+ result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
27
+ .set_method(:post)
28
+ .set_path(path)
29
+ .require_header("Idempotency-Key")
30
+ .add_header("X-Whatever", "aoitbeh")
31
+ .add_header("Idempotency-Key", idempotency_key)
32
+ .set_body(body)
33
+ .verify(tl_signature)
34
+
35
+ refute(result.first.include?("\nX-Whatever: aoitbeh\n"))
36
+ assert(result.first.include?("\nIdempotency-Key: " + idempotency_key + "\n"))
37
+ assert(result.first
38
+ .start_with?("POST /merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping\n"))
39
+ end
40
+
41
+ def test_full_request_signature_without_headers_should_succeed
42
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
43
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
44
+
45
+ tl_signature = TrueLayerSigning.sign_with_pem
46
+ .set_method(:post)
47
+ .set_path(path)
48
+ .set_body(body)
49
+ .sign
50
+
51
+ result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
52
+ .set_method(:post)
53
+ .set_path(path)
54
+ .add_header("X-Whatever", "aoitbeh")
55
+ .set_body(body)
56
+ .verify(tl_signature)
57
+
58
+ refute(result.first.include?("\nX-Whatever: aoitbeh\n"))
59
+ refute(result.first.include?("\nIdempotency-Key: "))
60
+ end
61
+
62
+ def test_mismatched_signature_with_attached_valid_body_should_fail
63
+ # Signature for `/bar` but with a valid jws-body pre-attached.
64
+ # If we run a simple jws verify on this unchanged, it'll work!
65
+ tl_signature = "eyJhbGciOiJFUzUxMiIsImtpZCI6IjQ1ZmM3NWNmLTU2ND" +
66
+ "ktndeZnC04NGIzLTE5MmMyYzc4ZTk5MCIsInRsX3ZlcnNpb24iOiIyIiwidGxfaGV" +
67
+ "hZGVycyI6IiJ9.UE9TVCAvYmFyCnt9.ARLa7Q5b8k5CIhfy1qrS-IkNqCDeE-VFRD" +
68
+ "z7Lb0fXUMOi_Ktck-R7BHDMXFDzbI5TyaxIo5TGHZV_cs0fg96dlSxAERp3UaN2oC" +
69
+ "QHIE5gQ4m5uU3ee69XfwwU_RpEIMFypycxwq1HOf4LzTLXqP_CDT8DdyX8oTwYdUB" +
70
+ "d2d3D17Wd9UA"
71
+
72
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
73
+ .set_method(:post)
74
+ .set_path("/foo")
75
+ .set_body("{}")
76
+
77
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
78
+ assert_equal("Invalid signature format", error.message)
79
+ end
80
+
81
+ def test_mismatched_signature_with_attached_valid_body_and_trailing_dots_should_fail
82
+ # Signature for `/bar` but with a valid jws-body pre-attached.
83
+ # If we run a simple jws verify on this unchanged, it'll work!
84
+ tl_signature = "eyJhbGciOiJFUzUxMiIsImtpZCI6IjQ1ZmM3NWNmLTU2ND" +
85
+ "ktndeZnC04NGIzLTE5MmMyYzc4ZTk5MCIsInRsX3ZlcnNpb24iOiIyIiwidGxfaGV" +
86
+ "hZGVycyI6IiJ9.UE9TVCAvYmFyCnt9.ARLa7Q5b8k5CIhfy1qrS-IkNqCDeE-VFRD" +
87
+ "z7Lb0fXUMOi_Ktck-R7BHDMXFDzbI5TyaxIo5TGHZV_cs0fg96dlSxAERp3UaN2oC" +
88
+ "QHIE5gQ4m5uU3ee69XfwwU_RpEIMFypycxwq1HOf4LzTLXqP_CDT8DdyX8oTwYdUB" +
89
+ "d2d3D17Wd9UA...."
90
+
91
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
92
+ .set_method(:post)
93
+ .set_path("/foo")
94
+ .set_body("{}")
95
+
96
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
97
+ assert_equal("Invalid signature format", error.message)
98
+ end
99
+
100
+ def test_full_request_with_static_signature_should_succeed
101
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00, name: "Foo???" }.to_json
102
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
103
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
104
+ tl_signature = File.read(File.expand_path("../../test-resources/tl-signature.txt",
105
+ File.dirname(__FILE__)))
106
+
107
+ result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
108
+ .set_method(:post)
109
+ .set_path(path)
110
+ .add_header("X-Whatever-2", "t2345d")
111
+ .add_header("Idempotency-Key", idempotency_key)
112
+ .set_body(body)
113
+ .verify(tl_signature)
114
+
115
+ refute(result.first.include?("\nX-Whatever-2: t2345d\n"))
116
+ assert(result.first.include?("\nIdempotency-Key: " + idempotency_key + "\n"))
117
+ assert(result.first
118
+ .start_with?("POST /merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping\n"))
119
+ end
120
+
121
+ def test_full_request_with_invalid_signature_should_fail
122
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00, name: "Foo???" }.to_json
123
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
124
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
125
+ tl_signature = "an-invalid..signature"
126
+
127
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
128
+ .set_method(:post)
129
+ .set_path(path)
130
+ .add_header("X-Whatever-2", "t2345d")
131
+ .add_header("Idempotency-Key", idempotency_key)
132
+ .set_body(body)
133
+
134
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
135
+ assert_equal("Invalid base64 for header", error.message)
136
+ end
137
+
138
+ def test_verify_without_signed_trailing_slash_should_succeed
139
+ body = { foo: "bar" }.to_json
140
+
141
+ tl_signature = TrueLayerSigning.sign_with_pem
142
+ .set_method(:post)
143
+ .set_path("/tl-webhook/")
144
+ .set_body(body)
145
+ .sign
146
+
147
+ result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
148
+ .set_method(:post)
149
+ .set_path("/tl-webhook") # different
150
+ .set_body(body)
151
+ .verify(tl_signature)
152
+
153
+ assert(result.first.start_with?("POST /tl-webhook/\n"))
154
+ end
155
+
156
+ def test_verify_with_unsigned_trailing_slash_should_succeed
157
+ body = { foo: "bar" }.to_json
158
+
159
+ tl_signature = TrueLayerSigning.sign_with_pem
160
+ .set_method(:post)
161
+ .set_path("/tl-webhook")
162
+ .set_body(body)
163
+ .sign
164
+
165
+ result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
166
+ .set_method(:post)
167
+ .set_path("/tl-webhook/") # different
168
+ .set_body(body)
169
+ .verify(tl_signature)
170
+
171
+ assert(result.first.start_with?("POST /tl-webhook\n"))
172
+ end
173
+
174
+ def test_sign_an_invalid_path_should_fail
175
+ signer = TrueLayerSigning.sign_with_pem
176
+ error = assert_raises(TrueLayerSigning::Error) { signer.set_path("https://example.com/path") }
177
+ assert_equal("Path must start with '/'", error.message)
178
+ end
179
+
180
+ def test_verify_an_invalid_path_should_fail
181
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
182
+ error = assert_raises(TrueLayerSigning::Error) { verifier.set_path("https://example.com/path") }
183
+ assert_equal("Path must start with '/'", error.message)
184
+ end
185
+
186
+ def test_full_request_signature_with_method_mismatch_should_fail
187
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
188
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
189
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
190
+
191
+ tl_signature = TrueLayerSigning.sign_with_pem
192
+ .set_method(:post)
193
+ .set_path(path)
194
+ .add_header("Idempotency-Key", idempotency_key)
195
+ .set_body(body)
196
+ .sign
197
+
198
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
199
+ .set_method(:delete) # different
200
+ .set_path(path)
201
+ .add_header("X-Whatever", "aoitbeh")
202
+ .add_header("Idempotency-Key", idempotency_key)
203
+ .set_body(body)
204
+
205
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
206
+ assert_equal("Signature verification failed", error.message)
207
+ end
208
+
209
+ def test_full_request_signature_with_path_mismatch_should_fail
210
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
211
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
212
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
213
+
214
+ tl_signature = TrueLayerSigning.sign_with_pem
215
+ .set_method(:post)
216
+ .set_path(path)
217
+ .add_header("Idempotency-Key", idempotency_key)
218
+ .set_body(body)
219
+ .sign
220
+
221
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
222
+ .set_method(:post)
223
+ .set_path("/merchant_accounts/67b5b1cf-1d0c-45d4-a2ea-61bdc044327c/sweeping") # different
224
+ .add_header("X-Whatever", "aoitbeh")
225
+ .add_header("Idempotency-Key", idempotency_key)
226
+ .set_body(body)
227
+
228
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
229
+ assert_equal("Signature verification failed", error.message)
230
+ end
231
+
232
+ def test_full_request_signature_with_header_mismatch_should_fail
233
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
234
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
235
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
236
+
237
+ tl_signature = TrueLayerSigning.sign_with_pem
238
+ .set_method(:post)
239
+ .set_path(path)
240
+ .add_header("Idempotency-Key", idempotency_key)
241
+ .set_body(body)
242
+ .sign
243
+
244
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
245
+ .set_method(:post)
246
+ .set_path(path)
247
+ .add_header("X-Whatever", "aoitbeh")
248
+ .add_header("Idempotency-Key", "something-else") # different
249
+ .set_body(body)
250
+
251
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
252
+ assert_equal("Signature verification failed", error.message)
253
+ end
254
+
255
+ def test_full_request_signature_with_body_mismatch_should_fail
256
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
257
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
258
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
259
+
260
+ tl_signature = TrueLayerSigning.sign_with_pem
261
+ .set_method(:post)
262
+ .set_path(path)
263
+ .add_header("Idempotency-Key", idempotency_key)
264
+ .set_body(body)
265
+ .sign
266
+
267
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
268
+ .set_method(:post)
269
+ .set_path(path)
270
+ .add_header("X-Whatever", "aoitbeh")
271
+ .add_header("Idempotency-Key", idempotency_key)
272
+ .set_body({ max_amount_in_minor: 12_34 }.to_json) # different
273
+
274
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
275
+ assert_equal("Signature verification failed", error.message)
276
+ end
277
+
278
+ def test_full_request_signature_missing_signed_header_should_fail
279
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
280
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
281
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
282
+
283
+ tl_signature = TrueLayerSigning.sign_with_pem
284
+ .set_method(:post)
285
+ .set_path(path)
286
+ .add_header("Idempotency-Key", idempotency_key)
287
+ .set_body(body)
288
+ .sign
289
+
290
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
291
+ .set_method(:post)
292
+ .set_path(path)
293
+ .add_header("X-Whatever", "aoitbeh")
294
+ # missing 'Idempotency-Key' header
295
+ .set_body(body)
296
+
297
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
298
+ assert_equal("Missing header(s) declared in signature", error.message)
299
+ end
300
+
301
+ def test_full_request_signature_missing_required_header_should_fail
302
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
303
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
304
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
305
+
306
+ tl_signature = TrueLayerSigning.sign_with_pem
307
+ .set_method(:post)
308
+ .set_path(path)
309
+ .add_header("Idempotency-Key", idempotency_key)
310
+ .set_body(body)
311
+ .sign
312
+
313
+ verifier = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
314
+ .set_method(:post)
315
+ .set_path(path)
316
+ .require_header("X-Required") # missing from signature
317
+ .add_header("Idempotency-Key", idempotency_key)
318
+ .set_body(body)
319
+
320
+ error = assert_raises(TrueLayerSigning::Error) { verifier.verify(tl_signature) }
321
+ assert_equal("Signature missing required header(s)", error.message)
322
+ end
323
+
324
+ def test_full_request_signature_required_header_case_insensitive_should_succeed
325
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
326
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
327
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
328
+
329
+ tl_signature = TrueLayerSigning.sign_with_pem
330
+ .set_method(:post)
331
+ .set_path(path)
332
+ .add_header("Idempotency-Key", idempotency_key)
333
+ .set_body(body)
334
+ .sign
335
+
336
+ result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
337
+ .set_method(:post)
338
+ .set_path(path)
339
+ .require_header("IdEmPoTeNcY-KeY") # case insensitive
340
+ .add_header("Idempotency-Key", idempotency_key)
341
+ .set_body(body)
342
+ .verify(tl_signature)
343
+
344
+ assert(result.first
345
+ .start_with?("POST /merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping\n"))
346
+ end
347
+
348
+ def test_verify_with_flexible_header_case_and_order_should_succeed
349
+ body = { currency: "GBP", max_amount_in_minor: 50_000_00 }.to_json
350
+ idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
351
+ path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
352
+
353
+ tl_signature = TrueLayerSigning.sign_with_pem
354
+ .set_method(:post)
355
+ .set_path(path)
356
+ .add_header("Idempotency-Key", idempotency_key)
357
+ .add_header("X-Custom", "123")
358
+ .set_body(body)
359
+ .sign
360
+
361
+ result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
362
+ .set_method(:post)
363
+ .set_path(path)
364
+ .add_header("X-CUSTOM", "123") # different case and order
365
+ .add_header("idempotency-key", idempotency_key) # different case and order
366
+ .set_body(body)
367
+ .verify(tl_signature)
368
+
369
+ assert(result.first
370
+ .start_with?("POST /merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping\n"))
371
+ end
372
+ end
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH.unshift(::File.join(::File.dirname(__FILE__), "lib"))
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "truelayer-signing"
5
+ s.version = "0.1.0"
6
+ s.summary = "Ruby gem to produce and verify TrueLayer API requests signatures"
7
+ s.description = "TrueLayer provides instant access to open banking to " \
8
+ "easily integrate next-generation payments and financial data into any app." \
9
+ "This helps easily sign TrueLayer API requests using a JSON web signature."
10
+ s.author = "Kevin Plattret"
11
+ s.email = "kevin@truelayer.com"
12
+ s.homepage = "https://github.com/truelayer/truelayer-signing/ruby"
13
+ s.licenses = ["Apache-2.0", "MIT"]
14
+
15
+ s.metadata = {
16
+ "bug_tracker_uri" => "https://github.com/truelayer/truelayer-signing/issues",
17
+ "changelog_uri" => "https://github.com/truelayer/truelayer-signing/tree/ruby/CHANGELOG.md",
18
+ }
19
+
20
+ s.files = Dir["./**/*"]
21
+ s.require_paths = ["lib"]
22
+
23
+ s.required_ruby_version = ">= 2.7"
24
+ s.add_runtime_dependency("jwt", "2.6")
25
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: truelayer-signing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Plattret
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-01-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: '2.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: '2.6'
27
+ description: TrueLayer provides instant access to open banking to easily integrate
28
+ next-generation payments and financial data into any app.This helps easily sign
29
+ TrueLayer API requests using a JSON web signature.
30
+ email: kevin@truelayer.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - "./CHANGELOG.md"
36
+ - "./LICENSE-APACHE"
37
+ - "./LICENSE-MIT"
38
+ - "./README.md"
39
+ - "./Rakefile"
40
+ - "./doc/CHANGELOG_md.html"
41
+ - "./doc/JWT.html"
42
+ - "./doc/JWT/Decode.html"
43
+ - "./doc/JWT/Encode.html"
44
+ - "./doc/JWT/JWK.html"
45
+ - "./doc/JWT/JWK/EC.html"
46
+ - "./doc/LICENSE-APACHE.html"
47
+ - "./doc/LICENSE-MIT.html"
48
+ - "./doc/README_md.html"
49
+ - "./doc/Rakefile.html"
50
+ - "./doc/TrueLayerSigning.html"
51
+ - "./doc/TrueLayerSigning/Config.html"
52
+ - "./doc/TrueLayerSigning/Error.html"
53
+ - "./doc/TrueLayerSigning/JwsBase.html"
54
+ - "./doc/TrueLayerSigning/JwsHeader.html"
55
+ - "./doc/TrueLayerSigning/Signer.html"
56
+ - "./doc/TrueLayerSigning/Verifier.html"
57
+ - "./doc/TrueLayerSigningExamples.html"
58
+ - "./doc/created.rid"
59
+ - "./doc/css/fonts.css"
60
+ - "./doc/css/rdoc.css"
61
+ - "./doc/examples/sign-request/Gemfile.html"
62
+ - "./doc/examples/sign-request/Gemfile_lock.html"
63
+ - "./doc/examples/sign-request/README_md.html"
64
+ - "./doc/examples/webhook-server/Gemfile.html"
65
+ - "./doc/examples/webhook-server/Gemfile_lock.html"
66
+ - "./doc/examples/webhook-server/README_md.html"
67
+ - "./doc/fonts/Lato-Light.ttf"
68
+ - "./doc/fonts/Lato-LightItalic.ttf"
69
+ - "./doc/fonts/Lato-Regular.ttf"
70
+ - "./doc/fonts/Lato-RegularItalic.ttf"
71
+ - "./doc/fonts/SourceCodePro-Bold.ttf"
72
+ - "./doc/fonts/SourceCodePro-Regular.ttf"
73
+ - "./doc/images/add.png"
74
+ - "./doc/images/arrow_up.png"
75
+ - "./doc/images/brick.png"
76
+ - "./doc/images/brick_link.png"
77
+ - "./doc/images/bug.png"
78
+ - "./doc/images/bullet_black.png"
79
+ - "./doc/images/bullet_toggle_minus.png"
80
+ - "./doc/images/bullet_toggle_plus.png"
81
+ - "./doc/images/date.png"
82
+ - "./doc/images/delete.png"
83
+ - "./doc/images/find.png"
84
+ - "./doc/images/loadingAnimation.gif"
85
+ - "./doc/images/macFFBgHack.png"
86
+ - "./doc/images/package.png"
87
+ - "./doc/images/page_green.png"
88
+ - "./doc/images/page_white_text.png"
89
+ - "./doc/images/page_white_width.png"
90
+ - "./doc/images/plugin.png"
91
+ - "./doc/images/ruby.png"
92
+ - "./doc/images/tag_blue.png"
93
+ - "./doc/images/tag_green.png"
94
+ - "./doc/images/transparent.png"
95
+ - "./doc/images/wrench.png"
96
+ - "./doc/images/wrench_orange.png"
97
+ - "./doc/images/zoom.png"
98
+ - "./doc/index.html"
99
+ - "./doc/js/darkfish.js"
100
+ - "./doc/js/navigation.js"
101
+ - "./doc/js/navigation.js.gz"
102
+ - "./doc/js/search.js"
103
+ - "./doc/js/search_index.js"
104
+ - "./doc/js/search_index.js.gz"
105
+ - "./doc/js/searcher.js"
106
+ - "./doc/js/searcher.js.gz"
107
+ - "./doc/table_of_contents.html"
108
+ - "./examples/sign-request/Gemfile"
109
+ - "./examples/sign-request/Gemfile.lock"
110
+ - "./examples/sign-request/README.md"
111
+ - "./examples/sign-request/main.rb"
112
+ - "./examples/webhook-server/Gemfile"
113
+ - "./examples/webhook-server/Gemfile.lock"
114
+ - "./examples/webhook-server/README.md"
115
+ - "./examples/webhook-server/main.rb"
116
+ - "./lib/truelayer-signing.rb"
117
+ - "./lib/truelayer-signing/config.rb"
118
+ - "./lib/truelayer-signing/errors.rb"
119
+ - "./lib/truelayer-signing/jwt.rb"
120
+ - "./lib/truelayer-signing/signer.rb"
121
+ - "./lib/truelayer-signing/utils.rb"
122
+ - "./lib/truelayer-signing/verifier.rb"
123
+ - "./test/test-truelayer-signing.rb"
124
+ - "./truelayer-signing.gemspec"
125
+ homepage: https://github.com/truelayer/truelayer-signing/ruby
126
+ licenses:
127
+ - Apache-2.0
128
+ - MIT
129
+ metadata:
130
+ bug_tracker_uri: https://github.com/truelayer/truelayer-signing/issues
131
+ changelog_uri: https://github.com/truelayer/truelayer-signing/tree/ruby/CHANGELOG.md
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '2.7'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubygems_version: 3.4.1
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Ruby gem to produce and verify TrueLayer API requests signatures
151
+ test_files: []