@sd-jwt/core 0.7.2 → 0.8.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.
@@ -0,0 +1,90 @@
1
+ import { SDJWTException } from '@sd-jwt/utils';
2
+ import { splitSdJwt } from '@sd-jwt/decode';
3
+ import { SD_SEPARATOR } from '@sd-jwt/types';
4
+
5
+ export type FlattenJSONData = {
6
+ jwtData: {
7
+ protected: string;
8
+ payload: string;
9
+ signature: string;
10
+ };
11
+ disclosures: Array<string>;
12
+ kb_jwt?: string;
13
+ };
14
+
15
+ export type FlattenJSONSerialized = {
16
+ payload: string;
17
+ signature: string;
18
+ protected: string;
19
+ header: {
20
+ disclosures: Array<string>;
21
+ kb_jwt?: string;
22
+ };
23
+ };
24
+
25
+ export class FlattenJSON {
26
+ public disclosures: Array<string>;
27
+ public kb_jwt?: string;
28
+
29
+ public payload: string;
30
+ public signature: string;
31
+ public protected: string;
32
+
33
+ constructor(data: FlattenJSONData) {
34
+ this.disclosures = data.disclosures;
35
+ this.kb_jwt = data.kb_jwt;
36
+ this.payload = data.jwtData.payload;
37
+ this.signature = data.jwtData.signature;
38
+ this.protected = data.jwtData.protected;
39
+ }
40
+
41
+ public static fromEncode(encodedSdJwt: string) {
42
+ const { jwt, disclosures, kbJwt } = splitSdJwt(encodedSdJwt);
43
+
44
+ const { 0: protectedHeader, 1: payload, 2: signature } = jwt.split('.');
45
+ if (!protectedHeader || !payload || !signature) {
46
+ throw new SDJWTException('Invalid JWT');
47
+ }
48
+
49
+ return new FlattenJSON({
50
+ jwtData: {
51
+ protected: protectedHeader,
52
+ payload,
53
+ signature,
54
+ },
55
+ disclosures,
56
+ kb_jwt: kbJwt,
57
+ });
58
+ }
59
+
60
+ public static fromSerialized(json: FlattenJSONSerialized) {
61
+ return new FlattenJSON({
62
+ jwtData: {
63
+ protected: json.protected,
64
+ payload: json.payload,
65
+ signature: json.signature,
66
+ },
67
+ disclosures: json.header.disclosures,
68
+ kb_jwt: json.header.kb_jwt,
69
+ });
70
+ }
71
+
72
+ public toJson(): FlattenJSONSerialized {
73
+ return {
74
+ payload: this.payload,
75
+ signature: this.signature,
76
+ protected: this.protected,
77
+ header: {
78
+ disclosures: this.disclosures,
79
+ kb_jwt: this.kb_jwt,
80
+ },
81
+ };
82
+ }
83
+
84
+ public toEncoded() {
85
+ const jwt = `${this.protected}.${this.payload}.${this.signature}`;
86
+ const disclosures = this.disclosures.join(SD_SEPARATOR);
87
+ const kb_jwt = this.kb_jwt ?? '';
88
+ return [jwt, disclosures, kb_jwt].join(SD_SEPARATOR);
89
+ }
90
+ }
@@ -0,0 +1,140 @@
1
+ import { base64urlEncode, SDJWTException } from '@sd-jwt/utils';
2
+ import { splitSdJwt } from '@sd-jwt/decode';
3
+ import { SD_SEPARATOR, type Signer } from '@sd-jwt/types';
4
+
5
+ export type GeneralJSONData = {
6
+ payload: string;
7
+ disclosures: Array<string>;
8
+ kb_jwt?: string;
9
+ signatures: Array<{
10
+ protected: string;
11
+ signature: string;
12
+ kid?: string;
13
+ }>;
14
+ };
15
+
16
+ export type GeneralJSONSerialized = {
17
+ payload: string;
18
+ signatures: Array<{
19
+ header: {
20
+ disclosures?: Array<string>;
21
+ kid?: string;
22
+ kb_jwt?: string;
23
+ };
24
+ protected: string;
25
+ signature: string;
26
+ }>;
27
+ };
28
+
29
+ export class GeneralJSON {
30
+ public payload: string;
31
+ public disclosures: Array<string>;
32
+ public kb_jwt?: string;
33
+ public signatures: Array<{
34
+ protected: string;
35
+ signature: string;
36
+ kid?: string;
37
+ }>;
38
+
39
+ constructor(data: GeneralJSONData) {
40
+ this.payload = data.payload;
41
+ this.disclosures = data.disclosures;
42
+ this.kb_jwt = data.kb_jwt;
43
+ this.signatures = data.signatures;
44
+ }
45
+
46
+ public static fromEncode(encodedSdJwt: string) {
47
+ const { jwt, disclosures, kbJwt } = splitSdJwt(encodedSdJwt);
48
+
49
+ const { 0: protectedHeader, 1: payload, 2: signature } = jwt.split('.');
50
+ if (!protectedHeader || !payload || !signature) {
51
+ throw new SDJWTException('Invalid JWT');
52
+ }
53
+
54
+ return new GeneralJSON({
55
+ payload,
56
+ disclosures,
57
+ kb_jwt: kbJwt,
58
+ signatures: [
59
+ {
60
+ protected: protectedHeader,
61
+ signature,
62
+ },
63
+ ],
64
+ });
65
+ }
66
+
67
+ public static fromSerialized(json: GeneralJSONSerialized) {
68
+ if (!json.signatures[0]) {
69
+ throw new SDJWTException('Invalid JSON');
70
+ }
71
+ const disclosures = json.signatures[0].header?.disclosures ?? [];
72
+ const kb_jwt = json.signatures[0].header?.kb_jwt;
73
+ return new GeneralJSON({
74
+ payload: json.payload,
75
+ disclosures,
76
+ kb_jwt,
77
+ signatures: json.signatures.map((s) => {
78
+ return {
79
+ protected: s.protected,
80
+ signature: s.signature,
81
+ kid: s.header?.kid,
82
+ };
83
+ }),
84
+ });
85
+ }
86
+
87
+ public toJson() {
88
+ return {
89
+ payload: this.payload,
90
+ signatures: this.signatures.map((s, i) => {
91
+ if (i !== 0) {
92
+ // If present, disclosures and kb_jwt, MUST be included in the first unprotected header and
93
+ // MUST NOT be present in any following unprotected headers.
94
+ return {
95
+ header: {
96
+ kid: s.kid,
97
+ },
98
+ protected: s.protected,
99
+ signature: s.signature,
100
+ };
101
+ }
102
+ return {
103
+ header: {
104
+ disclosures: this.disclosures,
105
+ kid: s.kid,
106
+ kb_jwt: this.kb_jwt,
107
+ },
108
+ protected: s.protected,
109
+ signature: s.signature,
110
+ };
111
+ }),
112
+ };
113
+ }
114
+
115
+ public toEncoded(index: number) {
116
+ if (index < 0 || index >= this.signatures.length) {
117
+ throw new SDJWTException('Index out of bounds');
118
+ }
119
+
120
+ const { protected: protectedHeader, signature } = this.signatures[index];
121
+ const disclosures = this.disclosures.join(SD_SEPARATOR);
122
+ const kb_jwt = this.kb_jwt ?? '';
123
+ const jwt = `${protectedHeader}.${this.payload}.${signature}`;
124
+ return [jwt, disclosures, kb_jwt].join(SD_SEPARATOR);
125
+ }
126
+
127
+ public async addSignature(
128
+ protectedHeader: Record<string, unknown>,
129
+ signer: Signer,
130
+ kid?: string,
131
+ ) {
132
+ const header = base64urlEncode(JSON.stringify(protectedHeader));
133
+ const signature = await signer(`${header}.${this.payload}`);
134
+ this.signatures.push({
135
+ protected: header,
136
+ signature,
137
+ kid,
138
+ });
139
+ }
140
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,9 @@
1
- import { SDJWTException, uint8ArrayToBase64Url } from '@sd-jwt/utils';
1
+ import {
2
+ base64urlDecode,
3
+ base64urlEncode,
4
+ SDJWTException,
5
+ uint8ArrayToBase64Url,
6
+ } from '@sd-jwt/utils';
2
7
  import { Jwt } from './jwt';
3
8
  import { KBJwt } from './kbjwt';
4
9
  import { SDJwt, pack } from './sdjwt';
@@ -10,14 +15,19 @@ import {
10
15
  type PresentationFrame,
11
16
  type SDJWTCompact,
12
17
  type SDJWTConfig,
18
+ type JwtPayload,
19
+ type Signer,
13
20
  } from '@sd-jwt/types';
14
21
  import { getSDAlgAndPayload } from '@sd-jwt/decode';
15
- import type { JwtPayload } from '@sd-jwt/types';
22
+ import { FlattenJSON } from './flattenJSON';
23
+ import { GeneralJSON } from './generalJSON';
16
24
 
17
25
  export * from './sdjwt';
18
26
  export * from './kbjwt';
19
27
  export * from './jwt';
20
28
  export * from './decoy';
29
+ export * from './flattenJSON';
30
+ export * from './generalJSON';
21
31
 
22
32
  export type SdJwtPayload = Record<string, unknown>;
23
33
 
@@ -25,7 +35,7 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
25
35
  //header type
26
36
  protected type?: string;
27
37
 
28
- public static DEFAULT_hashAlg = 'sha-256';
38
+ public static readonly DEFAULT_hashAlg = 'sha-256';
29
39
 
30
40
  protected userConfig: SDJWTConfig = {};
31
41
 
@@ -310,4 +320,341 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
310
320
  const sdjwt = await SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher);
311
321
  return sdjwt.getClaims(this.userConfig.hasher);
312
322
  }
323
+
324
+ public toFlattenJSON(endcodedSDJwt: SDJWTCompact) {
325
+ return FlattenJSON.fromEncode(endcodedSDJwt);
326
+ }
327
+
328
+ public toGeneralJSON(endcodedSDJwt: SDJWTCompact) {
329
+ return GeneralJSON.fromEncode(endcodedSDJwt);
330
+ }
331
+ }
332
+
333
+ export class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
334
+ //header type
335
+ protected type?: string;
336
+
337
+ public static readonly DEFAULT_hashAlg = 'sha-256';
338
+
339
+ protected userConfig: SDJWTConfig = {};
340
+
341
+ constructor(userConfig?: SDJWTConfig) {
342
+ if (userConfig) {
343
+ this.userConfig = userConfig;
344
+ }
345
+ }
346
+
347
+ private async createKBJwt(
348
+ options: KBOptions,
349
+ sdHash: string,
350
+ ): Promise<KBJwt> {
351
+ if (!this.userConfig.kbSigner) {
352
+ throw new SDJWTException('Key Binding Signer not found');
353
+ }
354
+ if (!this.userConfig.kbSignAlg) {
355
+ throw new SDJWTException('Key Binding sign algorithm not specified');
356
+ }
357
+
358
+ const { payload } = options;
359
+ const kbJwt = new KBJwt({
360
+ header: {
361
+ typ: KB_JWT_TYP,
362
+ alg: this.userConfig.kbSignAlg,
363
+ },
364
+ payload: { ...payload, sd_hash: sdHash },
365
+ });
366
+
367
+ await kbJwt.sign(this.userConfig.kbSigner);
368
+ return kbJwt;
369
+ }
370
+
371
+ private encodeObj(obj: Record<string, unknown>): string {
372
+ return base64urlEncode(JSON.stringify(obj));
373
+ }
374
+
375
+ public async issue<Payload extends ExtendedPayload>(
376
+ payload: Payload,
377
+ disclosureFrame: DisclosureFrame<Payload> | undefined,
378
+ options: {
379
+ sigs: Array<{
380
+ signer: Signer;
381
+ alg: string;
382
+ kid: string;
383
+ header?: Record<string, unknown>;
384
+ }>; // multiple signers for the credential
385
+ },
386
+ ): Promise<GeneralJSON> {
387
+ if (!this.userConfig.hasher) {
388
+ throw new SDJWTException('Hasher not found');
389
+ }
390
+
391
+ if (!this.userConfig.saltGenerator) {
392
+ throw new SDJWTException('SaltGenerator not found');
393
+ }
394
+
395
+ if (disclosureFrame) {
396
+ this.validateReservedFields<Payload>(disclosureFrame);
397
+ }
398
+
399
+ const hasher = this.userConfig.hasher;
400
+ const hashAlg = this.userConfig.hashAlg ?? SDJwtInstance.DEFAULT_hashAlg;
401
+
402
+ const { packedClaims, disclosures } = await pack(
403
+ payload,
404
+ disclosureFrame,
405
+ { hasher, alg: hashAlg },
406
+ this.userConfig.saltGenerator,
407
+ );
408
+
409
+ const encodedDisclosures = disclosures.map((d) => d.encode());
410
+ const encodedSDJwtPayload = this.encodeObj({
411
+ ...packedClaims,
412
+ _sd_alg: disclosureFrame ? hashAlg : undefined,
413
+ });
414
+
415
+ const signatures = await Promise.all(
416
+ options.sigs.map(async (s) => {
417
+ const { signer, alg, kid, header } = s;
418
+ const protectedHeader = { typ: this.type, alg, kid, ...header };
419
+ const encodedProtectedHeader = this.encodeObj(protectedHeader);
420
+ const signature = await signer(
421
+ `${encodedProtectedHeader}.${encodedSDJwtPayload}`,
422
+ );
423
+
424
+ return {
425
+ protected: encodedProtectedHeader,
426
+ kid,
427
+ signature,
428
+ };
429
+ }),
430
+ );
431
+
432
+ const generalJson = new GeneralJSON({
433
+ payload: encodedSDJwtPayload,
434
+ disclosures: encodedDisclosures,
435
+ signatures,
436
+ });
437
+
438
+ return generalJson;
439
+ }
440
+
441
+ /**
442
+ * Validates if the disclosureFrame contains any reserved fields. If so it will throw an error.
443
+ * @param disclosureFrame
444
+ * @returns
445
+ */
446
+ protected validateReservedFields<T extends ExtendedPayload>(
447
+ disclosureFrame: DisclosureFrame<T>,
448
+ ) {
449
+ return;
450
+ }
451
+
452
+ public async present<T extends Record<string, unknown>>(
453
+ generalJSON: GeneralJSON,
454
+ presentationFrame?: PresentationFrame<T>,
455
+ options?: {
456
+ kb?: KBOptions;
457
+ },
458
+ ): Promise<GeneralJSON> {
459
+ if (!this.userConfig.hasher) {
460
+ throw new SDJWTException('Hasher not found');
461
+ }
462
+ const hasher = this.userConfig.hasher;
463
+ const encodedSDJwt = generalJSON.toEncoded(0);
464
+ const sdjwt = await SDJwt.fromEncode(encodedSDJwt, hasher);
465
+
466
+ if (!sdjwt.jwt?.payload) throw new SDJWTException('Payload not found');
467
+ const disclosures = await sdjwt.getPresentDisclosures(
468
+ presentationFrame,
469
+ hasher,
470
+ );
471
+ const encodedDisclosures = disclosures.map((d) => d.encode());
472
+ const presentedGeneralJSON = new GeneralJSON({
473
+ payload: generalJSON.payload,
474
+ disclosures: encodedDisclosures,
475
+ signatures: generalJSON.signatures,
476
+ });
477
+
478
+ if (!options?.kb) {
479
+ return presentedGeneralJSON;
480
+ }
481
+
482
+ const presentSdJwtWithoutKb = await sdjwt.present(
483
+ presentationFrame,
484
+ hasher,
485
+ );
486
+
487
+ const sdHashStr = await this.calculateSDHash(
488
+ presentSdJwtWithoutKb,
489
+ sdjwt,
490
+ hasher,
491
+ );
492
+
493
+ const kbJwt = await this.createKBJwt(options.kb, sdHashStr);
494
+ const encodedKbJwt = kbJwt.encodeJwt();
495
+ presentedGeneralJSON.kb_jwt = encodedKbJwt;
496
+ return presentedGeneralJSON;
497
+ }
498
+
499
+ // This function is for verifying the SD JWT
500
+ // If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT
501
+ // If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it
502
+ public async verify(
503
+ generalJSON: GeneralJSON,
504
+ requiredClaimKeys?: string[],
505
+ requireKeyBindings?: boolean,
506
+ ) {
507
+ if (!this.userConfig.hasher) {
508
+ throw new SDJWTException('Hasher not found');
509
+ }
510
+ const hasher = this.userConfig.hasher;
511
+
512
+ const { payload, headers } = await this.validate(generalJSON);
513
+
514
+ const encodedSDJwt = generalJSON.toEncoded(0);
515
+ const sdjwt = await SDJwt.fromEncode(encodedSDJwt, hasher);
516
+ if (!sdjwt.jwt || !sdjwt.jwt.payload) {
517
+ throw new SDJWTException('Invalid SD JWT');
518
+ }
519
+
520
+ if (requiredClaimKeys) {
521
+ const keys = await sdjwt.keys(hasher);
522
+ const missingKeys = requiredClaimKeys.filter((k) => !keys.includes(k));
523
+ if (missingKeys.length > 0) {
524
+ throw new SDJWTException(
525
+ `Missing required claim keys: ${missingKeys.join(', ')}`,
526
+ );
527
+ }
528
+ }
529
+
530
+ if (!requireKeyBindings) {
531
+ return { payload, headers };
532
+ }
533
+
534
+ if (!sdjwt.kbJwt) {
535
+ throw new SDJWTException('Key Binding JWT not exist');
536
+ }
537
+ if (!this.userConfig.kbVerifier) {
538
+ throw new SDJWTException('Key Binding Verifier not found');
539
+ }
540
+ const kb = await sdjwt.kbJwt.verifyKB({
541
+ verifier: this.userConfig.kbVerifier,
542
+ payload: payload as JwtPayload,
543
+ });
544
+ if (!kb) {
545
+ throw new Error('signature is not valid');
546
+ }
547
+ const sdHashfromKb = kb.payload.sd_hash;
548
+ const sdjwtWithoutKb = new SDJwt({
549
+ jwt: sdjwt.jwt,
550
+ disclosures: sdjwt.disclosures,
551
+ });
552
+
553
+ const presentSdJwtWithoutKb = sdjwtWithoutKb.encodeSDJwt();
554
+ const sdHashStr = await this.calculateSDHash(
555
+ presentSdJwtWithoutKb,
556
+ sdjwt,
557
+ hasher,
558
+ );
559
+
560
+ if (sdHashStr !== sdHashfromKb) {
561
+ throw new SDJWTException('Invalid sd_hash in Key Binding JWT');
562
+ }
563
+
564
+ return { payload, headers, kb };
565
+ }
566
+
567
+ private async calculateSDHash(
568
+ presentSdJwtWithoutKb: string,
569
+ sdjwt: SDJwt,
570
+ hasher: Hasher,
571
+ ) {
572
+ if (!sdjwt.jwt || !sdjwt.jwt.payload) {
573
+ throw new SDJWTException('Invalid SD JWT');
574
+ }
575
+ const { _sd_alg } = getSDAlgAndPayload(sdjwt.jwt.payload);
576
+ const sdHash = await hasher(presentSdJwtWithoutKb, _sd_alg);
577
+ const sdHashStr = uint8ArrayToBase64Url(sdHash);
578
+ return sdHashStr;
579
+ }
580
+
581
+ // This function is for validating the SD JWT
582
+ // Just checking signature and return its the claims
583
+ public async validate(generalJSON: GeneralJSON) {
584
+ if (!this.userConfig.hasher) {
585
+ throw new SDJWTException('Hasher not found');
586
+ }
587
+ if (!this.userConfig.verifier) {
588
+ throw new SDJWTException('Verifier not found');
589
+ }
590
+ const hasher = this.userConfig.hasher;
591
+ const verifier = this.userConfig.verifier;
592
+
593
+ const { payload, signatures } = generalJSON;
594
+
595
+ const results = await Promise.all(
596
+ signatures.map(async (s) => {
597
+ const { protected: encodedHeader, signature } = s;
598
+ const verified = await verifier(
599
+ `${encodedHeader}.${payload}`,
600
+ signature,
601
+ );
602
+ const header = JSON.parse(base64urlDecode(encodedHeader));
603
+ return { verified, header };
604
+ }),
605
+ );
606
+
607
+ const verified = results.every((r) => r.verified);
608
+ if (!verified) {
609
+ throw new SDJWTException('Signature is not valid');
610
+ }
611
+
612
+ const encodedSDJwt = generalJSON.toEncoded(0);
613
+ const sdjwt = await SDJwt.fromEncode(encodedSDJwt, hasher);
614
+ if (!sdjwt.jwt) {
615
+ throw new SDJWTException('Invalid SD JWT');
616
+ }
617
+
618
+ const claims = await sdjwt.getClaims(hasher);
619
+ return { payload: claims, headers: results.map((r) => r.header) };
620
+ }
621
+
622
+ public config(newConfig: SDJWTConfig) {
623
+ this.userConfig = { ...this.userConfig, ...newConfig };
624
+ }
625
+
626
+ public encode(sdJwt: GeneralJSON, index: number): SDJWTCompact {
627
+ return sdJwt.toEncoded(index);
628
+ }
629
+
630
+ public decode(endcodedSDJwt: SDJWTCompact) {
631
+ return GeneralJSON.fromEncode(endcodedSDJwt);
632
+ }
633
+
634
+ public async keys(generalSdjwt: GeneralJSON) {
635
+ if (!this.userConfig.hasher) {
636
+ throw new SDJWTException('Hasher not found');
637
+ }
638
+ const endcodedSDJwt = generalSdjwt.toEncoded(0);
639
+ const sdjwt = await SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher);
640
+ return sdjwt.keys(this.userConfig.hasher);
641
+ }
642
+
643
+ public async presentableKeys(generalSdjwt: GeneralJSON) {
644
+ if (!this.userConfig.hasher) {
645
+ throw new SDJWTException('Hasher not found');
646
+ }
647
+ const endcodedSDJwt = generalSdjwt.toEncoded(0);
648
+ const sdjwt = await SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher);
649
+ return sdjwt.presentableKeys(this.userConfig.hasher);
650
+ }
651
+
652
+ public async getClaims(generalSdjwt: GeneralJSON) {
653
+ if (!this.userConfig.hasher) {
654
+ throw new SDJWTException('Hasher not found');
655
+ }
656
+ const endcodedSDJwt = generalSdjwt.toEncoded(0);
657
+ const sdjwt = await SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher);
658
+ return sdjwt.getClaims(this.userConfig.hasher);
659
+ }
313
660
  }
package/src/sdjwt.ts CHANGED
@@ -120,6 +120,19 @@ export class SDJwt<
120
120
  presentFrame: PresentationFrame<T> | undefined,
121
121
  hasher: Hasher,
122
122
  ): Promise<SDJWTCompact> {
123
+ const disclosures = await this.getPresentDisclosures(presentFrame, hasher);
124
+ const presentSDJwt = new SDJwt({
125
+ jwt: this.jwt,
126
+ disclosures,
127
+ kbJwt: this.kbJwt,
128
+ });
129
+ return presentSDJwt.encodeSDJwt();
130
+ }
131
+
132
+ public async getPresentDisclosures<T extends Record<string, unknown>>(
133
+ presentFrame: PresentationFrame<T> | undefined,
134
+ hasher: Hasher,
135
+ ): Promise<Disclosure<unknown>[]> {
123
136
  if (!this.jwt?.payload || !this.disclosures) {
124
137
  throw new SDJWTException('Invalid sd-jwt: jwt or disclosures is missing');
125
138
  }
@@ -138,12 +151,7 @@ export class SDJwt<
138
151
  const disclosures = keys
139
152
  .map((k) => hashmap[disclosureKeymap[k]])
140
153
  .filter((d) => d !== undefined);
141
- const presentSDJwt = new SDJwt({
142
- jwt: this.jwt,
143
- disclosures,
144
- kbJwt: this.kbJwt,
145
- });
146
- return presentSDJwt.encodeSDJwt();
154
+ return disclosures;
147
155
  }
148
156
 
149
157
  public encodeSDJwt(): SDJWTCompact {
@@ -0,0 +1,56 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { FlattenJSON } from '..';
3
+
4
+ describe('FlattenJSON', () => {
5
+ test('fromEncode', () => {
6
+ const compact =
7
+ 'eyJ0eXAiOiJzZCtqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpZCI6IjEyMzQiLCJfc2QiOlsiYkRUUnZtNS1Zbi1IRzdjcXBWUjVPVlJJWHNTYUJrNTdKZ2lPcV9qMVZJNCIsImV0M1VmUnlsd1ZyZlhkUEt6Zzc5aGNqRDFJdHpvUTlvQm9YUkd0TW9zRmsiLCJ6V2ZaTlMxOUF0YlJTVGJvN3NKUm4wQlpRdldSZGNob0M3VVphYkZyalk4Il0sIl9zZF9hbGciOiJzaGEtMjU2In0.n27NCtnuwytlBYtUNjgkesDP_7gN7bhaLhWNL4SWT6MaHsOjZ2ZMp987GgQRL6ZkLbJ7Cd3hlePHS84GBXPuvg~WyI1ZWI4Yzg2MjM0MDJjZjJlIiwiZmlyc3RuYW1lIiwiSm9obiJd~WyJjNWMzMWY2ZWYzNTg4MWJjIiwibGFzdG5hbWUiLCJEb2UiXQ~WyJmYTlkYTUzZWJjOTk3OThlIiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE3MTAwNjk3MjIsImF1ZCI6ImRpZDpleGFtcGxlOjEyMyIsIm5vbmNlIjoiazh2ZGYwbmQ2Iiwic2RfaGFzaCI6Il8tTmJWSzNmczl3VzNHaDNOUktSNEt1NmZDMUwzN0R2MFFfalBXd0ppRkUifQ.pqw2OB5IA5ya9Mxf60hE3nr2gsJEIoIlnuCa4qIisijHbwg3WzTDFmW2SuNvK_ORN0WU6RoGbJx5uYZh8k4EbA';
8
+ const flattenJSON = FlattenJSON.fromEncode(compact);
9
+ expect(flattenJSON).toBeDefined();
10
+
11
+ const result = {
12
+ payload:
13
+ 'eyJpZCI6IjEyMzQiLCJfc2QiOlsiYkRUUnZtNS1Zbi1IRzdjcXBWUjVPVlJJWHNTYUJrNTdKZ2lPcV9qMVZJNCIsImV0M1VmUnlsd1ZyZlhkUEt6Zzc5aGNqRDFJdHpvUTlvQm9YUkd0TW9zRmsiLCJ6V2ZaTlMxOUF0YlJTVGJvN3NKUm4wQlpRdldSZGNob0M3VVphYkZyalk4Il0sIl9zZF9hbGciOiJzaGEtMjU2In0',
14
+ signature:
15
+ 'n27NCtnuwytlBYtUNjgkesDP_7gN7bhaLhWNL4SWT6MaHsOjZ2ZMp987GgQRL6ZkLbJ7Cd3hlePHS84GBXPuvg',
16
+ protected: 'eyJ0eXAiOiJzZCtqd3QiLCJhbGciOiJFUzI1NiJ9',
17
+ header: {
18
+ disclosures: [
19
+ 'WyI1ZWI4Yzg2MjM0MDJjZjJlIiwiZmlyc3RuYW1lIiwiSm9obiJd',
20
+ 'WyJjNWMzMWY2ZWYzNTg4MWJjIiwibGFzdG5hbWUiLCJEb2UiXQ',
21
+ 'WyJmYTlkYTUzZWJjOTk3OThlIiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ',
22
+ ],
23
+ kb_jwt:
24
+ 'eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE3MTAwNjk3MjIsImF1ZCI6ImRpZDpleGFtcGxlOjEyMyIsIm5vbmNlIjoiazh2ZGYwbmQ2Iiwic2RfaGFzaCI6Il8tTmJWSzNmczl3VzNHaDNOUktSNEt1NmZDMUwzN0R2MFFfalBXd0ppRkUifQ.pqw2OB5IA5ya9Mxf60hE3nr2gsJEIoIlnuCa4qIisijHbwg3WzTDFmW2SuNvK_ORN0WU6RoGbJx5uYZh8k4EbA',
25
+ },
26
+ };
27
+
28
+ expect(flattenJSON.toJson()).toEqual(result);
29
+ });
30
+
31
+ test('fromSerialized', () => {
32
+ const flattenJSON = {
33
+ payload:
34
+ 'eyJpZCI6IjEyMzQiLCJfc2QiOlsiYkRUUnZtNS1Zbi1IRzdjcXBWUjVPVlJJWHNTYUJrNTdKZ2lPcV9qMVZJNCIsImV0M1VmUnlsd1ZyZlhkUEt6Zzc5aGNqRDFJdHpvUTlvQm9YUkd0TW9zRmsiLCJ6V2ZaTlMxOUF0YlJTVGJvN3NKUm4wQlpRdldSZGNob0M3VVphYkZyalk4Il0sIl9zZF9hbGciOiJzaGEtMjU2In0',
35
+ signature:
36
+ 'n27NCtnuwytlBYtUNjgkesDP_7gN7bhaLhWNL4SWT6MaHsOjZ2ZMp987GgQRL6ZkLbJ7Cd3hlePHS84GBXPuvg',
37
+ protected: 'eyJ0eXAiOiJzZCtqd3QiLCJhbGciOiJFUzI1NiJ9',
38
+ header: {
39
+ disclosures: [
40
+ 'WyI1ZWI4Yzg2MjM0MDJjZjJlIiwiZmlyc3RuYW1lIiwiSm9obiJd',
41
+ 'WyJjNWMzMWY2ZWYzNTg4MWJjIiwibGFzdG5hbWUiLCJEb2UiXQ',
42
+ 'WyJmYTlkYTUzZWJjOTk3OThlIiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ',
43
+ ],
44
+ kb_jwt:
45
+ 'eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE3MTAwNjk3MjIsImF1ZCI6ImRpZDpleGFtcGxlOjEyMyIsIm5vbmNlIjoiazh2ZGYwbmQ2Iiwic2RfaGFzaCI6Il8tTmJWSzNmczl3VzNHaDNOUktSNEt1NmZDMUwzN0R2MFFfalBXd0ppRkUifQ.pqw2OB5IA5ya9Mxf60hE3nr2gsJEIoIlnuCa4qIisijHbwg3WzTDFmW2SuNvK_ORN0WU6RoGbJx5uYZh8k4EbA',
46
+ },
47
+ };
48
+
49
+ const result = FlattenJSON.fromSerialized(flattenJSON);
50
+ expect(result).toBeDefined();
51
+ const compact =
52
+ 'eyJ0eXAiOiJzZCtqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpZCI6IjEyMzQiLCJfc2QiOlsiYkRUUnZtNS1Zbi1IRzdjcXBWUjVPVlJJWHNTYUJrNTdKZ2lPcV9qMVZJNCIsImV0M1VmUnlsd1ZyZlhkUEt6Zzc5aGNqRDFJdHpvUTlvQm9YUkd0TW9zRmsiLCJ6V2ZaTlMxOUF0YlJTVGJvN3NKUm4wQlpRdldSZGNob0M3VVphYkZyalk4Il0sIl9zZF9hbGciOiJzaGEtMjU2In0.n27NCtnuwytlBYtUNjgkesDP_7gN7bhaLhWNL4SWT6MaHsOjZ2ZMp987GgQRL6ZkLbJ7Cd3hlePHS84GBXPuvg~WyI1ZWI4Yzg2MjM0MDJjZjJlIiwiZmlyc3RuYW1lIiwiSm9obiJd~WyJjNWMzMWY2ZWYzNTg4MWJjIiwibGFzdG5hbWUiLCJEb2UiXQ~WyJmYTlkYTUzZWJjOTk3OThlIiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE3MTAwNjk3MjIsImF1ZCI6ImRpZDpleGFtcGxlOjEyMyIsIm5vbmNlIjoiazh2ZGYwbmQ2Iiwic2RfaGFzaCI6Il8tTmJWSzNmczl3VzNHaDNOUktSNEt1NmZDMUwzN0R2MFFfalBXd0ppRkUifQ.pqw2OB5IA5ya9Mxf60hE3nr2gsJEIoIlnuCa4qIisijHbwg3WzTDFmW2SuNvK_ORN0WU6RoGbJx5uYZh8k4EbA';
53
+
54
+ expect(result.toEncoded()).toEqual(compact);
55
+ });
56
+ });