@sd-jwt/core 0.3.0 → 0.3.2-next.100

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 (107) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +27 -82
  3. package/dist/index.d.mts +118 -0
  4. package/dist/index.d.ts +118 -0
  5. package/dist/index.js +675 -0
  6. package/dist/index.mjs +654 -0
  7. package/package.json +62 -48
  8. package/src/decoy.ts +15 -0
  9. package/src/index.ts +313 -0
  10. package/src/jwt.ts +107 -0
  11. package/src/kbjwt.ts +61 -0
  12. package/src/sdjwt.ts +337 -0
  13. package/src/test/decoy.spec.ts +30 -0
  14. package/src/test/index.spec.ts +528 -0
  15. package/src/test/jwt.spec.ts +141 -0
  16. package/src/test/kbjwt.spec.ts +341 -0
  17. package/src/test/pass.spec.ts +6 -0
  18. package/src/test/sdjwt.spec.ts +382 -0
  19. package/test/app-e2e.spec.ts +248 -0
  20. package/test/array_data_types.json +29 -0
  21. package/test/array_full_sd.json +21 -0
  22. package/test/array_in_sd.json +13 -0
  23. package/test/array_nested_in_plain.json +20 -0
  24. package/test/array_none_disclosed.json +17 -0
  25. package/test/array_of_nulls.json +15 -0
  26. package/test/array_of_objects.json +58 -0
  27. package/test/array_of_scalars.json +15 -0
  28. package/test/array_recursive_sd.json +35 -0
  29. package/test/array_recursive_sd_some_disclosed.json +55 -0
  30. package/test/complex.json +43 -0
  31. package/test/header_mod.json +44 -0
  32. package/test/json_serialization.json +44 -0
  33. package/test/key_binding.json +44 -0
  34. package/test/no_sd.json +36 -0
  35. package/test/object_data_types.json +60 -0
  36. package/test/recursions.json +98 -0
  37. package/tsconfig.json +7 -0
  38. package/vitest.config.mts +4 -0
  39. package/build/index.d.ts +0 -13
  40. package/build/index.js +0 -20
  41. package/build/index.js.map +0 -1
  42. package/build/jwt/error.d.ts +0 -2
  43. package/build/jwt/error.js +0 -7
  44. package/build/jwt/error.js.map +0 -1
  45. package/build/jwt/index.d.ts +0 -2
  46. package/build/jwt/index.js +0 -19
  47. package/build/jwt/index.js.map +0 -1
  48. package/build/jwt/jwt.d.ts +0 -208
  49. package/build/jwt/jwt.js +0 -325
  50. package/build/jwt/jwt.js.map +0 -1
  51. package/build/keyBinding/index.d.ts +0 -1
  52. package/build/keyBinding/index.js +0 -18
  53. package/build/keyBinding/index.js.map +0 -1
  54. package/build/keyBinding/keyBinding.d.ts +0 -64
  55. package/build/keyBinding/keyBinding.js +0 -119
  56. package/build/keyBinding/keyBinding.js.map +0 -1
  57. package/build/sdJwt/decoys.d.ts +0 -3
  58. package/build/sdJwt/decoys.js +0 -35
  59. package/build/sdJwt/decoys.js.map +0 -1
  60. package/build/sdJwt/disclosureFrame.d.ts +0 -8
  61. package/build/sdJwt/disclosureFrame.js +0 -87
  62. package/build/sdJwt/disclosureFrame.js.map +0 -1
  63. package/build/sdJwt/disclosures.d.ts +0 -33
  64. package/build/sdJwt/disclosures.js +0 -114
  65. package/build/sdJwt/disclosures.js.map +0 -1
  66. package/build/sdJwt/error.d.ts +0 -2
  67. package/build/sdJwt/error.js +0 -7
  68. package/build/sdJwt/error.js.map +0 -1
  69. package/build/sdJwt/index.d.ts +0 -6
  70. package/build/sdJwt/index.js +0 -23
  71. package/build/sdJwt/index.js.map +0 -1
  72. package/build/sdJwt/sdJwt.d.ts +0 -206
  73. package/build/sdJwt/sdJwt.js +0 -442
  74. package/build/sdJwt/sdJwt.js.map +0 -1
  75. package/build/sdJwt/types.d.ts +0 -5
  76. package/build/sdJwt/types.js +0 -3
  77. package/build/sdJwt/types.js.map +0 -1
  78. package/build/sdJwtVc/error.d.ts +0 -2
  79. package/build/sdJwtVc/error.js +0 -7
  80. package/build/sdJwtVc/error.js.map +0 -1
  81. package/build/sdJwtVc/index.d.ts +0 -2
  82. package/build/sdJwtVc/index.js +0 -19
  83. package/build/sdJwtVc/index.js.map +0 -1
  84. package/build/sdJwtVc/sdJwtVc.d.ts +0 -47
  85. package/build/sdJwtVc/sdJwtVc.js +0 -149
  86. package/build/sdJwtVc/sdJwtVc.js.map +0 -1
  87. package/build/signatureAndEncryptionAlgorithm.d.ts +0 -105
  88. package/build/signatureAndEncryptionAlgorithm.js +0 -110
  89. package/build/signatureAndEncryptionAlgorithm.js.map +0 -1
  90. package/build/types/disclosure.d.ts +0 -5
  91. package/build/types/disclosure.js +0 -3
  92. package/build/types/disclosure.js.map +0 -1
  93. package/build/types/index.d.ts +0 -5
  94. package/build/types/index.js +0 -22
  95. package/build/types/index.js.map +0 -1
  96. package/build/types/saltGenerator.d.ts +0 -17
  97. package/build/types/saltGenerator.js +0 -3
  98. package/build/types/saltGenerator.js.map +0 -1
  99. package/build/types/signer.d.ts +0 -2
  100. package/build/types/signer.js +0 -3
  101. package/build/types/signer.js.map +0 -1
  102. package/build/types/utils.d.ts +0 -2
  103. package/build/types/utils.js +0 -3
  104. package/build/types/utils.js.map +0 -1
  105. package/build/types/verifier.d.ts +0 -14
  106. package/build/types/verifier.js +0 -3
  107. package/build/types/verifier.js.map +0 -1
@@ -0,0 +1,528 @@
1
+ import { SDJwtInstance, SdJwtPayload } from '../index';
2
+ import { Signer, Verifier } from '@sd-jwt/types';
3
+ import Crypto, { KeyLike } from 'node:crypto';
4
+ import { describe, expect, test } from 'vitest';
5
+ import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
6
+ import { KbVerifier, JwtPayload } from '@sd-jwt/types';
7
+ import { importJWK, exportJWK, JWK } from 'jose';
8
+
9
+ export const createSignerVerifier = () => {
10
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
11
+ const signer: Signer = async (data: string) => {
12
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
13
+ return Buffer.from(sig).toString('base64url');
14
+ };
15
+ const verifier: Verifier = async (data: string, sig: string) => {
16
+ return Crypto.verify(
17
+ null,
18
+ Buffer.from(data),
19
+ publicKey,
20
+ Buffer.from(sig, 'base64url'),
21
+ );
22
+ };
23
+ return { signer, verifier };
24
+ };
25
+
26
+ describe('index', () => {
27
+ test('create', async () => {
28
+ const sdjwt = new SDJwtInstance<SdJwtPayload>();
29
+ expect(sdjwt).toBeDefined();
30
+ });
31
+
32
+ test('kbJwt', async () => {
33
+ const { signer, verifier } = createSignerVerifier();
34
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
35
+ signer,
36
+ signAlg: 'EdDSA',
37
+ verifier,
38
+ hasher: digest,
39
+ saltGenerator: generateSalt,
40
+ kbSigner: signer,
41
+ kbSignAlg: 'EdDSA',
42
+ });
43
+ const credential = await sdjwt.issue(
44
+ {
45
+ foo: 'bar',
46
+ iss: 'Issuer',
47
+ iat: new Date().getTime(),
48
+ vct: '',
49
+ },
50
+ {
51
+ _sd: ['foo'],
52
+ },
53
+ );
54
+
55
+ expect(credential).toBeDefined();
56
+
57
+ const presentation = await sdjwt.present(credential, ['foo'], {
58
+ kb: {
59
+ payload: {
60
+ aud: '1',
61
+ iat: 1,
62
+ nonce: '342',
63
+ },
64
+ },
65
+ });
66
+
67
+ expect(presentation).toBeDefined();
68
+ });
69
+
70
+ test('issue', async () => {
71
+ const { signer, verifier } = createSignerVerifier();
72
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
73
+ signer,
74
+ signAlg: 'EdDSA',
75
+ verifier,
76
+ hasher: digest,
77
+ saltGenerator: generateSalt,
78
+ });
79
+ const credential = await sdjwt.issue(
80
+ {
81
+ foo: 'bar',
82
+ iss: 'Issuer',
83
+ iat: new Date().getTime(),
84
+ vct: '',
85
+ },
86
+ {
87
+ _sd: ['foo'],
88
+ },
89
+ );
90
+
91
+ expect(credential).toBeDefined();
92
+ });
93
+
94
+ test('verify failed', async () => {
95
+ const { signer } = createSignerVerifier();
96
+ const { publicKey } = Crypto.generateKeyPairSync('ed25519');
97
+ const failedverifier: Verifier = async (data: string, sig: string) => {
98
+ return Crypto.verify(
99
+ null,
100
+ Buffer.from(data),
101
+ publicKey,
102
+ Buffer.from(sig, 'base64url'),
103
+ );
104
+ };
105
+
106
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
107
+ signer,
108
+ signAlg: 'EdDSA',
109
+ verifier: failedverifier,
110
+ hasher: digest,
111
+ saltGenerator: generateSalt,
112
+ });
113
+
114
+ const credential = await sdjwt.issue(
115
+ {
116
+ foo: 'bar',
117
+ iss: 'Issuer',
118
+ iat: new Date().getTime(),
119
+ vct: '',
120
+ },
121
+ {
122
+ _sd: ['foo'],
123
+ },
124
+ );
125
+
126
+ try {
127
+ await sdjwt.verify(credential);
128
+ } catch (e) {
129
+ expect(e).toBeDefined();
130
+ }
131
+ });
132
+
133
+ test('verify failed with kbJwt', async () => {
134
+ const { signer, verifier } = createSignerVerifier();
135
+ const { publicKey } = Crypto.generateKeyPairSync('ed25519');
136
+ const failedverifier: Verifier = async (data: string, sig: string) => {
137
+ return Crypto.verify(
138
+ null,
139
+ Buffer.from(data),
140
+ publicKey,
141
+ Buffer.from(sig, 'base64url'),
142
+ );
143
+ };
144
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
145
+ signer,
146
+ signAlg: 'EdDSA',
147
+ verifier,
148
+ hasher: digest,
149
+ saltGenerator: generateSalt,
150
+ kbSigner: signer,
151
+ kbVerifier: failedverifier,
152
+ kbSignAlg: 'EdDSA',
153
+ });
154
+
155
+ const credential = await sdjwt.issue(
156
+ {
157
+ foo: 'bar',
158
+ iss: 'Issuer',
159
+ iat: new Date().getTime(),
160
+ vct: '',
161
+ },
162
+ {
163
+ _sd: ['foo'],
164
+ },
165
+ );
166
+
167
+ const presentation = await sdjwt.present(credential, ['foo'], {
168
+ kb: {
169
+ payload: {
170
+ aud: '',
171
+ iat: 1,
172
+ nonce: '342',
173
+ },
174
+ },
175
+ });
176
+
177
+ try {
178
+ await sdjwt.verify(presentation);
179
+ } catch (e) {
180
+ expect(e).toBeDefined();
181
+ }
182
+ });
183
+
184
+ test('verify with kbJwt', async () => {
185
+ const { signer, verifier } = createSignerVerifier();
186
+
187
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
188
+
189
+ //TODO: maybe we can pass a minial class of the jwt to pass the token
190
+ const kbVerifier: KbVerifier = async (
191
+ data: string,
192
+ sig: string,
193
+ payload: JwtPayload,
194
+ ) => {
195
+ let publicKey: JsonWebKey;
196
+ if (payload.cnf) {
197
+ // use the key from the cnf
198
+ publicKey = payload.cnf.jwk;
199
+ } else {
200
+ throw Error('key binding not supported');
201
+ }
202
+ // get the key of the holder to verify the signature
203
+ return Crypto.verify(
204
+ null,
205
+ Buffer.from(data),
206
+ (await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
207
+ Buffer.from(sig, 'base64url'),
208
+ );
209
+ };
210
+
211
+ const kbSigner = (data: string) => {
212
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
213
+ return Buffer.from(sig).toString('base64url');
214
+ };
215
+
216
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
217
+ signer,
218
+ signAlg: 'EdDSA',
219
+ verifier,
220
+ hasher: digest,
221
+ saltGenerator: generateSalt,
222
+ kbSigner: kbSigner,
223
+ kbVerifier: kbVerifier,
224
+ kbSignAlg: 'EdDSA',
225
+ });
226
+ const credential = await sdjwt.issue(
227
+ {
228
+ foo: 'bar',
229
+ iat: new Date().getTime(),
230
+ cnf: {
231
+ jwk: await exportJWK(publicKey),
232
+ },
233
+ },
234
+ {
235
+ _sd: ['foo'],
236
+ },
237
+ );
238
+
239
+ const presentation = await sdjwt.present(credential, ['foo'], {
240
+ kb: {
241
+ payload: {
242
+ aud: '1',
243
+ iat: 1,
244
+ nonce: '342',
245
+ },
246
+ },
247
+ });
248
+
249
+ const results = await sdjwt.verify(presentation, ['foo'], true);
250
+ expect(results).toBeDefined();
251
+ });
252
+
253
+ test('Hasher not found', async () => {
254
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({});
255
+ try {
256
+ const credential = await sdjwt.issue(
257
+ {
258
+ foo: 'bar',
259
+ iss: 'Issuer',
260
+ iat: new Date().getTime(),
261
+ vct: '',
262
+ },
263
+ {
264
+ _sd: ['foo'],
265
+ },
266
+ );
267
+
268
+ expect(credential).toBeDefined();
269
+ } catch (e) {
270
+ expect(e).toBeDefined();
271
+ }
272
+ });
273
+
274
+ test('SaltGenerator not found', async () => {
275
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
276
+ hasher: digest,
277
+ });
278
+ try {
279
+ const credential = await sdjwt.issue(
280
+ {
281
+ foo: 'bar',
282
+ iss: 'Issuer',
283
+ iat: new Date().getTime(),
284
+ vct: '',
285
+ },
286
+ {
287
+ _sd: ['foo'],
288
+ },
289
+ );
290
+
291
+ expect(credential).toBeDefined();
292
+ } catch (e) {
293
+ expect(e).toBeDefined();
294
+ }
295
+ });
296
+
297
+ test('Signer not found', async () => {
298
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
299
+ hasher: digest,
300
+ saltGenerator: generateSalt,
301
+ });
302
+ try {
303
+ const credential = await sdjwt.issue(
304
+ {
305
+ foo: 'bar',
306
+ iss: 'Issuer',
307
+ iat: new Date().getTime(),
308
+ vct: '',
309
+ },
310
+ {
311
+ _sd: ['foo'],
312
+ },
313
+ );
314
+
315
+ expect(credential).toBeDefined();
316
+ } catch (e) {
317
+ expect(e).toBeDefined();
318
+ }
319
+ });
320
+
321
+ test('Verifier not found', async () => {
322
+ const { signer, verifier } = createSignerVerifier();
323
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
324
+ signer,
325
+ hasher: digest,
326
+ saltGenerator: generateSalt,
327
+ kbSigner: signer,
328
+ kbVerifier: verifier,
329
+ signAlg: 'EdDSA',
330
+ kbSignAlg: 'EdDSA',
331
+ });
332
+
333
+ const credential = await sdjwt.issue(
334
+ {
335
+ foo: 'bar',
336
+ iss: 'Issuer',
337
+ iat: new Date().getTime(),
338
+ vct: '',
339
+ },
340
+ {
341
+ _sd: ['foo'],
342
+ },
343
+ );
344
+
345
+ const presentation = await sdjwt.present(credential, ['foo'], {
346
+ kb: {
347
+ payload: {
348
+ aud: '1',
349
+ iat: 1,
350
+ nonce: '342',
351
+ },
352
+ },
353
+ });
354
+ try {
355
+ const results = await sdjwt.verify(presentation, ['foo'], true);
356
+ } catch (e) {
357
+ expect(e).toBeDefined();
358
+ }
359
+ });
360
+
361
+ test('kbSigner not found', async () => {
362
+ const { signer, verifier } = createSignerVerifier();
363
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
364
+ signer,
365
+ verifier,
366
+ hasher: digest,
367
+ saltGenerator: generateSalt,
368
+ kbVerifier: verifier,
369
+ signAlg: 'EdDSA',
370
+ kbSignAlg: 'EdDSA',
371
+ });
372
+
373
+ const credential = await sdjwt.issue(
374
+ {
375
+ foo: 'bar',
376
+ iss: 'Issuer',
377
+ iat: new Date().getTime(),
378
+ vct: '',
379
+ },
380
+ {
381
+ _sd: ['foo'],
382
+ },
383
+ );
384
+ try {
385
+ const presentation = await sdjwt.present(credential, ['foo'], {
386
+ kb: {
387
+ payload: {
388
+ aud: '1',
389
+ iat: 1,
390
+ nonce: '342',
391
+ },
392
+ },
393
+ });
394
+ } catch (e) {
395
+ expect(e).toBeDefined();
396
+ }
397
+ });
398
+
399
+ test('kbVerifier not found', async () => {
400
+ const { signer, verifier } = createSignerVerifier();
401
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
402
+ signer,
403
+ verifier,
404
+ hasher: digest,
405
+ saltGenerator: generateSalt,
406
+ kbSigner: signer,
407
+ signAlg: 'EdDSA',
408
+ kbSignAlg: 'EdDSA',
409
+ });
410
+
411
+ const credential = await sdjwt.issue(
412
+ {
413
+ foo: 'bar',
414
+ iss: 'Issuer',
415
+ iat: new Date().getTime(),
416
+ vct: '',
417
+ },
418
+ {
419
+ _sd: ['foo'],
420
+ },
421
+ );
422
+
423
+ const presentation = await sdjwt.present(credential, ['foo'], {
424
+ kb: {
425
+ payload: {
426
+ aud: '1',
427
+ iat: 1,
428
+ nonce: '342',
429
+ },
430
+ },
431
+ });
432
+ try {
433
+ const results = await sdjwt.verify(presentation, ['foo'], true);
434
+ } catch (e) {
435
+ expect(e).toBeDefined();
436
+ }
437
+ });
438
+
439
+ test('kbSignAlg not found', async () => {
440
+ const { signer, verifier } = createSignerVerifier();
441
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
442
+ signer,
443
+ verifier,
444
+ hasher: digest,
445
+ saltGenerator: generateSalt,
446
+ kbSigner: signer,
447
+ signAlg: 'EdDSA',
448
+ });
449
+
450
+ const credential = await sdjwt.issue(
451
+ {
452
+ foo: 'bar',
453
+ iss: 'Issuer',
454
+ iat: new Date().getTime(),
455
+ vct: '',
456
+ },
457
+ {
458
+ _sd: ['foo'],
459
+ },
460
+ );
461
+
462
+ const presentation = sdjwt.present(credential, ['foo'], {
463
+ kb: {
464
+ payload: {
465
+ aud: '1',
466
+ iat: 1,
467
+ nonce: '342',
468
+ },
469
+ },
470
+ });
471
+ expect(presentation).rejects.toThrow(
472
+ 'Key Binding sign algorithm not specified',
473
+ );
474
+ });
475
+
476
+ test('hasher is not found', async () => {
477
+ const { signer } = createSignerVerifier();
478
+ const sdjwt_create = new SDJwtInstance<SdJwtPayload>({
479
+ signer,
480
+ hasher: digest,
481
+ saltGenerator: generateSalt,
482
+ signAlg: 'EdDSA',
483
+ });
484
+ const credential = await sdjwt_create.issue(
485
+ {
486
+ foo: 'bar',
487
+ iss: 'Issuer',
488
+ iat: new Date().getTime(),
489
+ vct: '',
490
+ },
491
+ {
492
+ _sd: ['foo'],
493
+ },
494
+ );
495
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({});
496
+ expect(sdjwt.keys('')).rejects.toThrow('Hasher not found');
497
+ expect(sdjwt.presentableKeys('')).rejects.toThrow('Hasher not found');
498
+ expect(sdjwt.getClaims('')).rejects.toThrow('Hasher not found');
499
+ expect(() => sdjwt.decode('')).toThrowError('Hasher not found');
500
+ expect(sdjwt.present(credential, ['foo'])).rejects.toThrow(
501
+ 'Hasher not found',
502
+ );
503
+ });
504
+
505
+ test('presentableKeys', async () => {
506
+ const { signer } = createSignerVerifier();
507
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
508
+ signer,
509
+ hasher: digest,
510
+ saltGenerator: generateSalt,
511
+ signAlg: 'EdDSA',
512
+ });
513
+ const credential = await sdjwt.issue(
514
+ {
515
+ foo: 'bar',
516
+ iss: 'Issuer',
517
+ iat: new Date().getTime(),
518
+ vct: '',
519
+ },
520
+ {
521
+ _sd: ['foo'],
522
+ },
523
+ );
524
+ const keys = await sdjwt.presentableKeys(credential);
525
+ expect(keys).toBeDefined();
526
+ expect(keys).toEqual(['foo']);
527
+ });
528
+ });
@@ -0,0 +1,141 @@
1
+ import { SDJWTException } from '@sd-jwt/utils';
2
+ import { Jwt } from '../jwt';
3
+ import Crypto from 'node:crypto';
4
+ import { Signer, Verifier } from '@sd-jwt/types';
5
+ import { describe, expect, test } from 'vitest';
6
+
7
+ describe('JWT', () => {
8
+ test('create', async () => {
9
+ const jwt = new Jwt({
10
+ header: { alg: 'EdDSA' },
11
+ payload: { foo: 'bar' },
12
+ });
13
+
14
+ expect(jwt.header).toEqual({ alg: 'EdDSA' });
15
+ expect(jwt.payload).toEqual({ foo: 'bar' });
16
+ });
17
+
18
+ test('returns decoded JWT when correct JWT string is provided', () => {
19
+ // Two objects are created separately, the first: { alg: 'HS256', typ: 'JWT' } represents a JWT Header and the second: { sub: '1234567890', name: 'John Doe' } represents a JWT Payload.
20
+ // These objects are turned into strings with JSON.stringify. The resulting strings are encoded with base64 encoding using Buffer.from(string).toString('base64').
21
+ // These base64 encoded strings are concatenated with a period (.) between them, following the structure of a JWT, which is composed of three Base64-URL strings separated by dots (header.payload.signature).
22
+ // A 'signature' string is added at the end to represent a JWT signature.
23
+ // So, the jwt variable ends up being a string with the format of a base64Url encoded Header, a period, a base64Url encoded Payload, another period, and a 'signature' string.
24
+ // It's important to note that the 'signature' here is just a placeholder string and not an actual cryptographic signature generated from the header and payload data.
25
+ const jwt = `${Buffer.from(
26
+ JSON.stringify({ alg: 'HS256', typ: 'JWT' }),
27
+ ).toString('base64')}.${Buffer.from(
28
+ JSON.stringify({ sub: '1234567890', name: 'John Doe' }),
29
+ ).toString('base64')}.signature`;
30
+ const result = Jwt.decodeJWT(jwt);
31
+ expect(result).toEqual({
32
+ header: { alg: 'HS256', typ: 'JWT' },
33
+ payload: { sub: '1234567890', name: 'John Doe' },
34
+ signature: 'signature',
35
+ });
36
+ });
37
+
38
+ test('throws an error when JWT string is not correctly formed', () => {
39
+ const jwt = 'abc.def';
40
+ expect(() => Jwt.decodeJWT(jwt)).toThrow('Invalid JWT as input');
41
+ });
42
+
43
+ test('throws an error when JWT parts are missing', () => {
44
+ const jwt = `${Buffer.from(
45
+ JSON.stringify({ alg: 'HS256', typ: 'JWT' }),
46
+ ).toString('base64')}`;
47
+ expect(() => Jwt.decodeJWT(jwt)).toThrow('Invalid JWT as input');
48
+ });
49
+
50
+ test('set', async () => {
51
+ const jwt = new Jwt();
52
+ jwt.setHeader({ alg: 'EdDSA' });
53
+ jwt.setPayload({ foo: 'bar' });
54
+
55
+ expect(jwt.header).toEqual({ alg: 'EdDSA' });
56
+ expect(jwt.payload).toEqual({ foo: 'bar' });
57
+ });
58
+
59
+ test('sign', async () => {
60
+ const { privateKey } = Crypto.generateKeyPairSync('ed25519');
61
+ const testSigner: Signer = async (data: string) => {
62
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
63
+ return Buffer.from(sig).toString('base64url');
64
+ };
65
+ const jwt = new Jwt({
66
+ header: { alg: 'EdDSA' },
67
+ payload: { foo: 'bar' },
68
+ });
69
+
70
+ const encodedJwt = await jwt.sign(testSigner);
71
+ expect(typeof encodedJwt).toBe('string');
72
+ });
73
+
74
+ test('verify', async () => {
75
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
76
+ const testSigner: Signer = async (data: string) => {
77
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
78
+ return Buffer.from(sig).toString('base64url');
79
+ };
80
+ const testVerifier: Verifier = async (data: string, sig: string) => {
81
+ return Crypto.verify(
82
+ null,
83
+ Buffer.from(data),
84
+ publicKey,
85
+ Buffer.from(sig, 'base64url'),
86
+ );
87
+ };
88
+
89
+ const jwt = new Jwt({
90
+ header: { alg: 'EdDSA' },
91
+ payload: { foo: 'bar' },
92
+ });
93
+
94
+ const encodedJwt = await jwt.sign(testSigner);
95
+ const newJwt = Jwt.fromEncode(encodedJwt);
96
+ const verified = await newJwt.verify(testVerifier);
97
+ expect(verified).toStrictEqual({
98
+ header: { alg: 'EdDSA' },
99
+ payload: { foo: 'bar' },
100
+ });
101
+ try {
102
+ await newJwt.verify(() => false);
103
+ } catch (e: unknown) {
104
+ expect(e).toBeInstanceOf(SDJWTException);
105
+ }
106
+ });
107
+
108
+ test('encode', async () => {
109
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
110
+ const testSigner: Signer = async (data: string) => {
111
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
112
+ return Buffer.from(sig).toString('base64url');
113
+ };
114
+
115
+ const jwt = new Jwt({
116
+ header: { alg: 'EdDSA' },
117
+ payload: { foo: 'bar' },
118
+ });
119
+
120
+ const encodedJwt = await jwt.sign(testSigner);
121
+ const newJwt = Jwt.fromEncode(encodedJwt);
122
+ const newEncodedJwt = newJwt.encodeJwt();
123
+ expect(newEncodedJwt).toBe(encodedJwt);
124
+ });
125
+
126
+ test('decode failed', () => {
127
+ expect(() => Jwt.fromEncode('asfasfas')).toThrow();
128
+ });
129
+
130
+ test('encode failed', async () => {
131
+ const jwt = new Jwt({
132
+ header: { alg: 'EdDSA' },
133
+ });
134
+
135
+ try {
136
+ jwt.encodeJwt();
137
+ } catch (e: unknown) {
138
+ expect(e).toBeInstanceOf(SDJWTException);
139
+ }
140
+ });
141
+ });