@sd-jwt/core 0.20.1 → 0.21.0-alpha-20260916100413
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.
- package/LICENSE +2 -2
- package/README.md +82 -80
- package/dist/index.cjs +1526 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +696 -0
- package/dist/index.d.mts +537 -527
- package/dist/index.mjs +1422 -1998
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -49
- package/CHANGELOG.md +0 -39
- package/dist/index.d.ts +0 -686
- package/dist/index.js +0 -2100
- package/src/decode/decode.ts +0 -407
- package/src/decode/index.ts +0 -1
- package/src/decoy.ts +0 -15
- package/src/flattenJSON.ts +0 -99
- package/src/generalJSON.ts +0 -157
- package/src/index.ts +0 -1000
- package/src/jwt.ts +0 -286
- package/src/kbjwt.ts +0 -101
- package/src/present/index.ts +0 -1
- package/src/present/present.ts +0 -216
- package/src/sdjwt.ts +0 -384
- package/src/test/decode/decode.spec.ts +0 -197
- package/src/test/decoy.spec.ts +0 -30
- package/src/test/flattenJSON.spec.ts +0 -122
- package/src/test/generalJSON.spec.ts +0 -198
- package/src/test/index.spec.ts +0 -986
- package/src/test/jwt.spec.ts +0 -301
- package/src/test/kbjwt.spec.ts +0 -555
- package/src/test/pass.spec.ts +0 -7
- package/src/test/present/present.spec.ts +0 -305
- package/src/test/sdjwt.spec.ts +0 -384
- package/src/test/types/type.spec.ts +0 -88
- package/src/test/utils/base64url.spec.ts +0 -33
- package/src/test/utils/disclosure.spec.ts +0 -170
- package/src/test/utils/error.spec.ts +0 -15
- package/src/types/index.ts +0 -2
- package/src/types/type.ts +0 -270
- package/src/types/verification-error.ts +0 -55
- package/src/utils/base64url.ts +0 -6
- package/src/utils/disclosure.ts +0 -121
- package/src/utils/error.ts +0 -25
- package/src/utils/index.ts +0 -3
- package/src/utils/strict-json.ts +0 -17
- package/test/app-e2e.spec.ts +0 -292
- package/test/array_data_types.json +0 -31
- package/test/array_full_sd.json +0 -21
- package/test/array_in_sd.json +0 -13
- package/test/array_nested_in_plain.json +0 -29
- package/test/array_none_disclosed.json +0 -17
- package/test/array_of_nulls.json +0 -15
- package/test/array_of_objects.json +0 -65
- package/test/array_of_scalars.json +0 -19
- package/test/array_recursive_sd.json +0 -35
- package/test/array_recursive_sd_some_disclosed.json +0 -63
- package/test/complex.json +0 -43
- package/test/header_mod.json +0 -48
- package/test/json_serialization.json +0 -48
- package/test/key_binding.json +0 -48
- package/test/no_sd.json +0 -36
- package/test/object_data_types.json +0 -62
- package/test/recursions.json +0 -117
- package/test/rfc9901-audit-fixes.spec.ts +0 -252
- package/test/rfc9901-validation.spec.ts +0 -150
- package/test/tsconfig.json +0 -4
- package/tsconfig.json +0 -7
- package/vitest.config.mts +0 -4
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import Crypto from 'node:crypto';
|
|
2
|
-
import { hasher as digest, generateSalt } from '@owf/crypto';
|
|
3
|
-
import { describe, expect, test } from 'vitest';
|
|
4
|
-
import { unpackObj } from '../src/decode';
|
|
5
|
-
import { GeneralJSON, Jwt, KBJwt, SDJwtInstance } from '../src/index';
|
|
6
|
-
import { selectDisclosures, transformPresentationFrame } from '../src/present';
|
|
7
|
-
import type { Signer, Verifier } from '../src/types';
|
|
8
|
-
import { Disclosure } from '../src/utils';
|
|
9
|
-
|
|
10
|
-
const createSignerVerifier = () => {
|
|
11
|
-
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
12
|
-
const signer: Signer = async (data: string) => {
|
|
13
|
-
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
14
|
-
return Buffer.from(sig).toString('base64url');
|
|
15
|
-
};
|
|
16
|
-
const verifier: Verifier = async (data: string, sig: string) => {
|
|
17
|
-
return Crypto.verify(
|
|
18
|
-
null,
|
|
19
|
-
Buffer.from(data),
|
|
20
|
-
publicKey,
|
|
21
|
-
Buffer.from(sig, 'base64url'),
|
|
22
|
-
);
|
|
23
|
-
};
|
|
24
|
-
return { signer, verifier };
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
describe('RFC 9901 audit fixes', () => {
|
|
28
|
-
test('rejects malformed disclosure containers', () => {
|
|
29
|
-
const objectDisclosure = Disclosure.fromArray(['salt', 'name', 'Alice'], {
|
|
30
|
-
digest: 'object-digest',
|
|
31
|
-
encoded: 'object-disclosure',
|
|
32
|
-
});
|
|
33
|
-
const arrayDisclosure = Disclosure.fromArray(['salt', 'Alice'], {
|
|
34
|
-
digest: 'array-digest',
|
|
35
|
-
encoded: 'array-disclosure',
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
expect(() => unpackObj({ _sd: 'object-digest' }, {})).toThrow(
|
|
39
|
-
'Invalid _sd claim: expected array of strings',
|
|
40
|
-
);
|
|
41
|
-
expect(() =>
|
|
42
|
-
unpackObj({ items: [{ '...': 'array-digest', extra: true }] }, {}),
|
|
43
|
-
).toThrow('Invalid array disclosure placeholder');
|
|
44
|
-
expect(() =>
|
|
45
|
-
unpackObj({ _sd: ['array-digest'] }, { 'array-digest': arrayDisclosure }),
|
|
46
|
-
).toThrow('Array disclosure cannot be used as an object property');
|
|
47
|
-
expect(() =>
|
|
48
|
-
unpackObj(
|
|
49
|
-
{ items: [{ '...': 'object-digest' }] },
|
|
50
|
-
{ 'object-digest': objectDisclosure },
|
|
51
|
-
),
|
|
52
|
-
).toThrow('Object-property disclosure cannot be used as an array element');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('rejects disclosed claim name collisions at the same object level', () => {
|
|
56
|
-
const first = Disclosure.fromArray(['salt-1', 'name', 'Alice'], {
|
|
57
|
-
digest: 'digest-1',
|
|
58
|
-
encoded: 'disclosure-1',
|
|
59
|
-
});
|
|
60
|
-
const second = Disclosure.fromArray(['salt-2', 'name', 'Mallory'], {
|
|
61
|
-
digest: 'digest-2',
|
|
62
|
-
encoded: 'disclosure-2',
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
expect(() =>
|
|
66
|
-
unpackObj(
|
|
67
|
-
{ _sd: ['digest-1', 'digest-2'] },
|
|
68
|
-
{ 'digest-1': first, 'digest-2': second },
|
|
69
|
-
),
|
|
70
|
-
).toThrow('Disclosed claim name "name" conflicts with another disclosure');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('validates JWT claims after disclosure processing', async () => {
|
|
74
|
-
const { signer, verifier } = createSignerVerifier();
|
|
75
|
-
const sdjwt = new SDJwtInstance({
|
|
76
|
-
signer,
|
|
77
|
-
signAlg: 'EdDSA',
|
|
78
|
-
verifier,
|
|
79
|
-
hasher: digest,
|
|
80
|
-
saltGenerator: generateSalt,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const disclosure = new Disclosure(['salt', 'exp', 1]);
|
|
84
|
-
const disclosureDigest = await disclosure.digest({
|
|
85
|
-
hasher: digest,
|
|
86
|
-
alg: 'sha-256',
|
|
87
|
-
});
|
|
88
|
-
const header = Buffer.from(JSON.stringify({ alg: 'EdDSA' })).toString(
|
|
89
|
-
'base64url',
|
|
90
|
-
);
|
|
91
|
-
const payload = Buffer.from(
|
|
92
|
-
JSON.stringify({ _sd: [disclosureDigest], _sd_alg: 'sha-256' }),
|
|
93
|
-
).toString('base64url');
|
|
94
|
-
const unsignedJwt = `${header}.${payload}`;
|
|
95
|
-
const signature = await signer(unsignedJwt);
|
|
96
|
-
|
|
97
|
-
await expect(
|
|
98
|
-
sdjwt.validate(`${unsignedJwt}.${signature}~${disclosure.encode()}~`, {
|
|
99
|
-
currentDate: 1,
|
|
100
|
-
}),
|
|
101
|
-
).rejects.toThrow('Verify Error: JWT is expired');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test('rejects unsafe JWT algorithm and invalid validity claims', async () => {
|
|
105
|
-
const jwt = new Jwt({
|
|
106
|
-
header: { alg: 'none' },
|
|
107
|
-
payload: { exp: 100 },
|
|
108
|
-
signature: 'signature',
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
await expect(jwt.verify(() => true)).rejects.toThrow(
|
|
112
|
-
'Verify Error: alg "none" is not allowed',
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
const { signer, verifier } = createSignerVerifier();
|
|
116
|
-
const signed = new Jwt({
|
|
117
|
-
header: { alg: 'EdDSA' },
|
|
118
|
-
payload: { exp: '100' },
|
|
119
|
-
});
|
|
120
|
-
await signed.sign(signer);
|
|
121
|
-
|
|
122
|
-
await expect(signed.verify(verifier)).rejects.toThrow(
|
|
123
|
-
'Verify Error: JWT exp must be a number',
|
|
124
|
-
);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test('validates expected issuer audience', async () => {
|
|
128
|
-
const { signer, verifier } = createSignerVerifier();
|
|
129
|
-
const jwt = new Jwt({
|
|
130
|
-
header: { alg: 'EdDSA' },
|
|
131
|
-
payload: { aud: 'verifier-a' },
|
|
132
|
-
});
|
|
133
|
-
await jwt.sign(signer);
|
|
134
|
-
|
|
135
|
-
await expect(
|
|
136
|
-
jwt.verify(verifier, { expectedAudience: 'verifier-b' }),
|
|
137
|
-
).rejects.toThrow('Verify Error: Invalid audience');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('validates key binding audience, age, and claim types', async () => {
|
|
141
|
-
const { signer } = createSignerVerifier();
|
|
142
|
-
const kbJwt = new KBJwt({
|
|
143
|
-
header: { typ: 'kb+jwt', alg: 'EdDSA' },
|
|
144
|
-
payload: { iat: 900, aud: 'verifier-a', nonce: 'nonce', sd_hash: 'hash' },
|
|
145
|
-
});
|
|
146
|
-
await kbJwt.sign(signer);
|
|
147
|
-
|
|
148
|
-
await expect(
|
|
149
|
-
kbJwt.verifyKB({
|
|
150
|
-
verifier: () => true,
|
|
151
|
-
payload: {},
|
|
152
|
-
nonce: 'nonce',
|
|
153
|
-
options: { expectedKeyBindingAudience: 'verifier-b' },
|
|
154
|
-
}),
|
|
155
|
-
).rejects.toThrow('Verify Error: Invalid Key Binding audience');
|
|
156
|
-
|
|
157
|
-
await expect(
|
|
158
|
-
kbJwt.verifyKB({
|
|
159
|
-
verifier: () => true,
|
|
160
|
-
payload: {},
|
|
161
|
-
nonce: 'nonce',
|
|
162
|
-
options: { currentDate: 1000, keyBindingMaxAgeSeconds: 50 },
|
|
163
|
-
}),
|
|
164
|
-
).rejects.toThrow('Verify Error: Key Binding JWT is too old');
|
|
165
|
-
|
|
166
|
-
(kbJwt.payload as Record<string, unknown>).iat = '900';
|
|
167
|
-
await expect(
|
|
168
|
-
kbJwt.verifyKB({ verifier: () => true, payload: {}, nonce: 'nonce' }),
|
|
169
|
-
).rejects.toThrow('Invalid Key Binding Jwt');
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
test('rejects insecure disclosure hash algorithms by default', () => {
|
|
173
|
-
const { signer, verifier } = createSignerVerifier();
|
|
174
|
-
|
|
175
|
-
expect(
|
|
176
|
-
() =>
|
|
177
|
-
new SDJwtInstance({
|
|
178
|
-
signer,
|
|
179
|
-
signAlg: 'EdDSA',
|
|
180
|
-
verifier,
|
|
181
|
-
hasher: digest,
|
|
182
|
-
saltGenerator: generateSalt,
|
|
183
|
-
hashAlg: 'sha-256-32',
|
|
184
|
-
}),
|
|
185
|
-
).toThrow('Disallowed hash algorithm: sha-256-32');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test('rejects duplicate salts during issuance', async () => {
|
|
189
|
-
const { signer, verifier } = createSignerVerifier();
|
|
190
|
-
const sdjwt = new SDJwtInstance({
|
|
191
|
-
signer,
|
|
192
|
-
signAlg: 'EdDSA',
|
|
193
|
-
verifier,
|
|
194
|
-
hasher: digest,
|
|
195
|
-
saltGenerator: () => 'same-salt',
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
await expect(
|
|
199
|
-
sdjwt.issue({ first: 'a', second: 'b' }, { _sd: ['first', 'second'] }),
|
|
200
|
-
).rejects.toThrow('Duplicate disclosure salt detected');
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test('rejects disclosures and kb_jwt outside the first General JSON header', () => {
|
|
204
|
-
expect(() =>
|
|
205
|
-
GeneralJSON.fromSerialized({
|
|
206
|
-
payload: 'payload',
|
|
207
|
-
signatures: [
|
|
208
|
-
{ protected: 'header-1', signature: 'sig-1', header: {} },
|
|
209
|
-
{
|
|
210
|
-
protected: 'header-2',
|
|
211
|
-
signature: 'sig-2',
|
|
212
|
-
header: { disclosures: [] },
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
}),
|
|
216
|
-
).toThrow(
|
|
217
|
-
'disclosures and kb_jwt MUST only appear in the first unprotected header',
|
|
218
|
-
);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
test('escapes presentation paths with literal dots in claim names', () => {
|
|
222
|
-
expect(transformPresentationFrame({ 'a.b': true })).toEqual(['a~1b']);
|
|
223
|
-
expect(transformPresentationFrame({ a: { b: true } })).toEqual([
|
|
224
|
-
'a',
|
|
225
|
-
'a.b',
|
|
226
|
-
]);
|
|
227
|
-
|
|
228
|
-
const selected = selectDisclosures(
|
|
229
|
-
{ _sd: ['top'], a: { _sd: ['nested'] } },
|
|
230
|
-
[
|
|
231
|
-
{
|
|
232
|
-
digest: 'top',
|
|
233
|
-
encoded: 'top-disclosure',
|
|
234
|
-
salt: 'salt-1',
|
|
235
|
-
key: 'a.b',
|
|
236
|
-
value: 'top-level',
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
digest: 'nested',
|
|
240
|
-
encoded: 'nested-disclosure',
|
|
241
|
-
salt: 'salt-2',
|
|
242
|
-
key: 'b',
|
|
243
|
-
value: 'nested',
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
{ a: { b: true } },
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
expect(selected).toHaveLength(1);
|
|
250
|
-
expect(selected[0].digest).toBe('nested');
|
|
251
|
-
});
|
|
252
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest';
|
|
2
|
-
import { unpackObj } from '../src/decode';
|
|
3
|
-
import { Disclosure } from '../src/utils';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Tests for RFC 9901 validation checks added to unpackObj / unpackObjInternal.
|
|
7
|
-
*
|
|
8
|
-
* Section 7.1 step 4: Duplicate digest rejection
|
|
9
|
-
* Section 7.1 step 5: Unreferenced disclosure rejection
|
|
10
|
-
* Section 7.1 step 3c.ii.3: Claim name collision rejection
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const makeDisclosure = (
|
|
14
|
-
digest: string,
|
|
15
|
-
key: string | undefined,
|
|
16
|
-
value: unknown,
|
|
17
|
-
) =>
|
|
18
|
-
Disclosure.fromArray(key ? ['salt', key, value] : ['salt', value], {
|
|
19
|
-
digest,
|
|
20
|
-
encoded: `encoded-${digest}`,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('RFC 9901 validation', () => {
|
|
24
|
-
// ──────────────────────────────────────────
|
|
25
|
-
// 7.1 step 4 — Duplicate digest in _sd array
|
|
26
|
-
// ──────────────────────────────────────────
|
|
27
|
-
test('rejects duplicate digest in _sd array', () => {
|
|
28
|
-
const digest = 'abc123';
|
|
29
|
-
const payload = { _sd: [digest, digest] };
|
|
30
|
-
const map: Record<string, Disclosure> = {
|
|
31
|
-
[digest]: makeDisclosure(digest, 'foo', 'bar'),
|
|
32
|
-
};
|
|
33
|
-
expect(() => unpackObj(payload, map)).toThrow(
|
|
34
|
-
'Duplicate digest found in SD-JWT payload',
|
|
35
|
-
);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// ──────────────────────────────────────────
|
|
39
|
-
// 7.1 step 4 — Duplicate digest across nested _sd
|
|
40
|
-
// ──────────────────────────────────────────
|
|
41
|
-
test('rejects duplicate digest across nested _sd arrays', () => {
|
|
42
|
-
const digest = 'dup1';
|
|
43
|
-
const payload = {
|
|
44
|
-
_sd: [digest],
|
|
45
|
-
nested: {
|
|
46
|
-
_sd: [digest],
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
const map: Record<string, Disclosure> = {
|
|
50
|
-
[digest]: makeDisclosure(digest, 'x', 'y'),
|
|
51
|
-
};
|
|
52
|
-
expect(() => unpackObj(payload, map)).toThrow(
|
|
53
|
-
'Duplicate digest found in SD-JWT payload',
|
|
54
|
-
);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// ──────────────────────────────────────────
|
|
58
|
-
// 7.1 step 4 — Duplicate digest in array items
|
|
59
|
-
// ──────────────────────────────────────────
|
|
60
|
-
test('rejects duplicate digest in array element disclosures', () => {
|
|
61
|
-
const digest = 'arrdup';
|
|
62
|
-
const payload = {
|
|
63
|
-
arr: [{ '...': digest }, { '...': digest }],
|
|
64
|
-
};
|
|
65
|
-
const map: Record<string, Disclosure> = {
|
|
66
|
-
[digest]: makeDisclosure(digest, undefined, 'val'),
|
|
67
|
-
};
|
|
68
|
-
expect(() => unpackObj(payload, map)).toThrow(
|
|
69
|
-
'Duplicate digest found in SD-JWT payload',
|
|
70
|
-
);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// ──────────────────────────────────────────
|
|
74
|
-
// 7.1 step 5 — Unreferenced disclosure
|
|
75
|
-
// ──────────────────────────────────────────
|
|
76
|
-
test('rejects unreferenced disclosure', () => {
|
|
77
|
-
const usedDigest = 'used1';
|
|
78
|
-
const unusedDigest = 'unused1';
|
|
79
|
-
const payload = { _sd: [usedDigest] };
|
|
80
|
-
const map: Record<string, Disclosure> = {
|
|
81
|
-
[usedDigest]: makeDisclosure(usedDigest, 'a', 1),
|
|
82
|
-
[unusedDigest]: makeDisclosure(unusedDigest, 'b', 2),
|
|
83
|
-
};
|
|
84
|
-
expect(() => unpackObj(payload, map)).toThrow(
|
|
85
|
-
'Unreferenced disclosure(s) detected in SD-JWT',
|
|
86
|
-
);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('rejects when no digests exist in payload but disclosures provided', () => {
|
|
90
|
-
const payload = { plain: 'value' };
|
|
91
|
-
const map: Record<string, Disclosure> = {
|
|
92
|
-
orphan: makeDisclosure('orphan', 'k', 'v'),
|
|
93
|
-
};
|
|
94
|
-
expect(() => unpackObj(payload, map)).toThrow(
|
|
95
|
-
'Unreferenced disclosure(s) detected in SD-JWT',
|
|
96
|
-
);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// ──────────────────────────────────────────
|
|
100
|
-
// 7.1 step 3c.ii.3 — Claim name collision
|
|
101
|
-
// ──────────────────────────────────────────
|
|
102
|
-
test('rejects disclosed claim name that conflicts with plaintext key', () => {
|
|
103
|
-
const digest = 'col1';
|
|
104
|
-
// The payload has both a plaintext "name" and a disclosure that would add "name"
|
|
105
|
-
const payload = { name: 'Alice', _sd: [digest] };
|
|
106
|
-
const map: Record<string, Disclosure> = {
|
|
107
|
-
[digest]: makeDisclosure(digest, 'name', 'Mallory'),
|
|
108
|
-
};
|
|
109
|
-
expect(() => unpackObj(payload, map)).toThrow(
|
|
110
|
-
'Disclosed claim name "name" conflicts with existing payload key',
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// ──────────────────────────────────────────
|
|
115
|
-
// Positive: valid SD-JWT unpacks without error
|
|
116
|
-
// ──────────────────────────────────────────
|
|
117
|
-
test('unpacks valid SD-JWT without errors', () => {
|
|
118
|
-
const d1 = 'digest1';
|
|
119
|
-
const d2 = 'digest2';
|
|
120
|
-
const payload = { _sd: [d1, d2], plain: 'hello' };
|
|
121
|
-
const map: Record<string, Disclosure> = {
|
|
122
|
-
[d1]: makeDisclosure(d1, 'foo', 'bar'),
|
|
123
|
-
[d2]: makeDisclosure(d2, 'baz', 42),
|
|
124
|
-
};
|
|
125
|
-
const { unpackedObj, disclosureKeymap } = unpackObj(payload, map);
|
|
126
|
-
expect(unpackedObj).toEqual({ plain: 'hello', foo: 'bar', baz: 42 });
|
|
127
|
-
expect(disclosureKeymap).toEqual({ foo: d1, baz: d2 });
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('unpacks valid SD-JWT with array disclosures', () => {
|
|
131
|
-
const d1 = 'arrdig1';
|
|
132
|
-
const d2 = 'arrdig2';
|
|
133
|
-
const payload = {
|
|
134
|
-
arr: [{ '...': d1 }, 'plainItem', { '...': d2 }],
|
|
135
|
-
};
|
|
136
|
-
const map: Record<string, Disclosure> = {
|
|
137
|
-
[d1]: makeDisclosure(d1, undefined, 'secret1'),
|
|
138
|
-
[d2]: makeDisclosure(d2, undefined, 'secret2'),
|
|
139
|
-
};
|
|
140
|
-
const { unpackedObj } = unpackObj(payload, map);
|
|
141
|
-
expect(unpackedObj).toEqual({ arr: ['secret1', 'plainItem', 'secret2'] });
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test('allows empty disclosure map with no _sd in payload', () => {
|
|
145
|
-
const payload = { plain: 'value' };
|
|
146
|
-
const map: Record<string, Disclosure> = {};
|
|
147
|
-
const { unpackedObj } = unpackObj(payload, map);
|
|
148
|
-
expect(unpackedObj).toEqual({ plain: 'value' });
|
|
149
|
-
});
|
|
150
|
-
});
|
package/test/tsconfig.json
DELETED
package/tsconfig.json
DELETED
package/vitest.config.mts
DELETED