@sd-jwt/sd-jwt-vc 0.14.2-next.0 → 0.15.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [0.15.0](https://github.com/openwallet-foundation-labs/sd-jwt-js/compare/v0.14.1...v0.15.0) (2025-08-20)
7
+
8
+
9
+ ### Features
10
+
11
+ * Allows to pass a custom function for the status list JWT validation ([#306](https://github.com/openwallet-foundation-labs/sd-jwt-js/issues/306)) ([25e546e](https://github.com/openwallet-foundation-labs/sd-jwt-js/commit/25e546e1536178f8ee41b0e1b3836656fd6f48ab))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [0.14.1](https://github.com/openwallet-foundation-labs/sd-jwt-js/compare/v0.14.0...v0.14.1) (2025-08-02)
7
18
 
8
19
 
package/README.md CHANGED
@@ -87,6 +87,8 @@ Check out more details in our [documentation](https://github.com/openwallet-foun
87
87
 
88
88
  To add revocation capabilities, you can use the `@sd-jwt/jwt-status-list` library to create a JWT Status List and include it in the SD-JWT-VC.
89
89
 
90
+ You can pass a dedicated `statusVerifier` function in the configuration to verify the signature of the payload of the JWT of the statuslist. If no function is provided, it will fallback to the verifier that is also used for the sd-jwt-vc.
91
+
90
92
  ### Type Metadata
91
93
 
92
94
  By setting the `loadTypeMetadataFormat` to `true` like this:
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { SDJWTConfig, kbPayload, kbHeader, DisclosureFrame } from '@sd-jwt/types';
1
+ import { SDJWTConfig, Verifier, kbPayload, kbHeader, DisclosureFrame } from '@sd-jwt/types';
2
2
  import { SdJwtPayload, SDJwtInstance, VerifierOptions } from '@sd-jwt/core';
3
3
 
4
4
  /**
@@ -156,6 +156,7 @@ type SDJWTVCConfig = SDJWTConfig & {
156
156
  statusListFetcher?: StatusListFetcher;
157
157
  statusValidator?: StatusValidator;
158
158
  vctFetcher?: VcTFetcher;
159
+ statusVerifier?: Verifier;
159
160
  loadTypeMetadataFormat?: boolean;
160
161
  timeout?: number;
161
162
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { SDJWTConfig, kbPayload, kbHeader, DisclosureFrame } from '@sd-jwt/types';
1
+ import { SDJWTConfig, Verifier, kbPayload, kbHeader, DisclosureFrame } from '@sd-jwt/types';
2
2
  import { SdJwtPayload, SDJwtInstance, VerifierOptions } from '@sd-jwt/core';
3
3
 
4
4
  /**
@@ -156,6 +156,7 @@ type SDJWTVCConfig = SDJWTConfig & {
156
156
  statusListFetcher?: StatusListFetcher;
157
157
  statusValidator?: StatusValidator;
158
158
  vctFetcher?: VcTFetcher;
159
+ statusVerifier?: Verifier;
159
160
  loadTypeMetadataFormat?: boolean;
160
161
  timeout?: number;
161
162
  };
package/dist/index.js CHANGED
@@ -325,7 +325,7 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends import_core.SDJwtInstance {
325
325
  */
326
326
  verifyStatus(result, options) {
327
327
  return __async(this, null, function* () {
328
- var _a, _b, _c, _d;
328
+ var _a, _b, _c, _d, _e;
329
329
  if (result.payload.status) {
330
330
  if (result.payload.status.status_list) {
331
331
  const fetcher = (_a = this.userConfig.statusListFetcher) != null ? _a : this.statusListFetcher.bind(this);
@@ -333,16 +333,19 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends import_core.SDJwtInstance {
333
333
  result.payload.status.status_list.uri
334
334
  );
335
335
  const slJWT = import_core.Jwt.fromEncode(statusListJWT);
336
- yield slJWT.verify(this.userConfig.verifier, options);
337
- const currentDate = (_b = options == null ? void 0 : options.currentDate) != null ? _b : Math.floor(Date.now() / 1e3);
338
- if (((_c = slJWT.payload) == null ? void 0 : _c.exp) && slJWT.payload.exp < currentDate) {
336
+ yield slJWT.verify(
337
+ (_b = this.userConfig.statusVerifier) != null ? _b : this.userConfig.verifier,
338
+ options
339
+ );
340
+ const currentDate = (_c = options == null ? void 0 : options.currentDate) != null ? _c : Math.floor(Date.now() / 1e3);
341
+ if (((_d = slJWT.payload) == null ? void 0 : _d.exp) && slJWT.payload.exp < currentDate) {
339
342
  throw new import_utils.SDJWTException("Status list is expired");
340
343
  }
341
344
  const statusList = (0, import_jwt_status_list.getListFromStatusListJWT)(statusListJWT);
342
345
  const status = statusList.getStatus(
343
346
  result.payload.status.status_list.idx
344
347
  );
345
- const statusValidator = (_d = this.userConfig.statusValidator) != null ? _d : this.statusValidator.bind(this);
348
+ const statusValidator = (_e = this.userConfig.statusValidator) != null ? _e : this.statusValidator.bind(this);
346
349
  yield statusValidator(status);
347
350
  }
348
351
  }
package/dist/index.mjs CHANGED
@@ -293,7 +293,7 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends SDJwtInstance {
293
293
  */
294
294
  verifyStatus(result, options) {
295
295
  return __async(this, null, function* () {
296
- var _a, _b, _c, _d;
296
+ var _a, _b, _c, _d, _e;
297
297
  if (result.payload.status) {
298
298
  if (result.payload.status.status_list) {
299
299
  const fetcher = (_a = this.userConfig.statusListFetcher) != null ? _a : this.statusListFetcher.bind(this);
@@ -301,16 +301,19 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends SDJwtInstance {
301
301
  result.payload.status.status_list.uri
302
302
  );
303
303
  const slJWT = Jwt.fromEncode(statusListJWT);
304
- yield slJWT.verify(this.userConfig.verifier, options);
305
- const currentDate = (_b = options == null ? void 0 : options.currentDate) != null ? _b : Math.floor(Date.now() / 1e3);
306
- if (((_c = slJWT.payload) == null ? void 0 : _c.exp) && slJWT.payload.exp < currentDate) {
304
+ yield slJWT.verify(
305
+ (_b = this.userConfig.statusVerifier) != null ? _b : this.userConfig.verifier,
306
+ options
307
+ );
308
+ const currentDate = (_c = options == null ? void 0 : options.currentDate) != null ? _c : Math.floor(Date.now() / 1e3);
309
+ if (((_d = slJWT.payload) == null ? void 0 : _d.exp) && slJWT.payload.exp < currentDate) {
307
310
  throw new SDJWTException("Status list is expired");
308
311
  }
309
312
  const statusList = getListFromStatusListJWT(statusListJWT);
310
313
  const status = statusList.getStatus(
311
314
  result.payload.status.status_list.idx
312
315
  );
313
- const statusValidator = (_d = this.userConfig.statusValidator) != null ? _d : this.statusValidator.bind(this);
316
+ const statusValidator = (_e = this.userConfig.statusValidator) != null ? _e : this.statusValidator.bind(this);
314
317
  yield statusValidator(status);
315
318
  }
316
319
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sd-jwt/sd-jwt-vc",
3
- "version": "0.14.2-next.0+1c55f0e",
3
+ "version": "0.15.0",
4
4
  "description": "sd-jwt draft 7 implementation in typescript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -39,15 +39,15 @@
39
39
  },
40
40
  "license": "Apache-2.0",
41
41
  "dependencies": {
42
- "@sd-jwt/core": "0.14.2-next.0+1c55f0e",
43
- "@sd-jwt/jwt-status-list": "0.14.2-next.0+1c55f0e",
44
- "@sd-jwt/utils": "0.14.2-next.0+1c55f0e",
42
+ "@sd-jwt/core": "0.15.0",
43
+ "@sd-jwt/jwt-status-list": "0.15.0",
44
+ "@sd-jwt/utils": "0.15.0",
45
45
  "ajv": "^8.17.1",
46
46
  "ajv-formats": "^3.0.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@sd-jwt/crypto-nodejs": "0.14.2-next.0+1c55f0e",
50
- "@sd-jwt/types": "0.14.2-next.0+1c55f0e",
49
+ "@sd-jwt/crypto-nodejs": "0.15.0",
50
+ "@sd-jwt/types": "0.15.0",
51
51
  "jose": "^5.2.2",
52
52
  "msw": "^2.3.5"
53
53
  },
@@ -67,5 +67,5 @@
67
67
  "esm"
68
68
  ]
69
69
  },
70
- "gitHead": "1c55f0ecb7a308b4115565a1f5abc57efab6b80f"
70
+ "gitHead": "b8733bc33817900b2bfe946bd10e3ff8673bb5fc"
71
71
  }
@@ -1,4 +1,4 @@
1
- import type { SDJWTConfig } from '@sd-jwt/types';
1
+ import type { SDJWTConfig, Verifier } from '@sd-jwt/types';
2
2
  import type { VcTFetcher } from './sd-jwt-vc-vct';
3
3
 
4
4
  export type StatusListFetcher = (uri: string) => Promise<string>;
@@ -14,6 +14,8 @@ export type SDJWTVCConfig = SDJWTConfig & {
14
14
  statusValidator?: StatusValidator;
15
15
  // a function that fetches the type metadata format from the uri. If not provided, the library will assume that the response is a TypeMetadataFormat. Caching has to be implemented in this function. If the integrity value is passed, it to be validated according to https://www.w3.org/TR/SRI/
16
16
  vctFetcher?: VcTFetcher;
17
+ // a function that verifies the status of the JWT. If not provided, the library will assume that the status is valid if it is 0.
18
+ statusVerifier?: Verifier;
17
19
  // if set to true, it will load the metadata format based on the vct value. If not provided, it will default to false.
18
20
  loadTypeMetadataFormat?: boolean;
19
21
  // timeout value in milliseconds when to abort the fetch request. If not provided, it will default to 10000.
@@ -358,7 +358,11 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
358
358
  StatusListJWTPayload
359
359
  >(statusListJWT);
360
360
  // check if the status list has a valid signature. The presence of the verifier is checked in the parent class.
361
- await slJWT.verify(this.userConfig.verifier as Verifier, options);
361
+ await slJWT.verify(
362
+ this.userConfig.statusVerifier ??
363
+ (this.userConfig.verifier as Verifier),
364
+ options,
365
+ );
362
366
 
363
367
  const currentDate =
364
368
  options?.currentDate ?? Math.floor(Date.now() / 1000);
@@ -22,6 +22,10 @@ const iat = Math.floor(Date.now() / 1000);
22
22
 
23
23
  const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
24
24
 
25
+ //create a separate keypair for the status list
26
+ const { privateKey: statusListPrivateKey, publicKey: statusListPublicKey } =
27
+ Crypto.generateKeyPairSync('ed25519');
28
+
25
29
  //TODO: to simulate a hosted status list, use the same appraoch as in vct.spec.ts
26
30
 
27
31
  const createSignerVerifier = () => {
@@ -54,7 +58,7 @@ const generateStatusList = async (): Promise<string> => {
54
58
  const values = createHeaderAndPayload(statusList, payload, header);
55
59
  return new SignJWT(values.payload)
56
60
  .setProtectedHeader(values.header)
57
- .sign(privateKey);
61
+ .sign(statusListPrivateKey);
58
62
  };
59
63
 
60
64
  const statusListJWT = await generateStatusList();
@@ -105,6 +109,15 @@ describe('Revocation', () => {
105
109
  // if (status === 0) return Promise.resolve();
106
110
  // throw new Error('Status is not valid');
107
111
  // },
112
+ statusVerifier: async (data: string, sig: string) => {
113
+ //we could also look into the data to extract the public key from the x5c when provided
114
+ return Crypto.verify(
115
+ null,
116
+ Buffer.from(data),
117
+ statusListPublicKey,
118
+ Buffer.from(sig, 'base64url'),
119
+ );
120
+ },
108
121
  });
109
122
 
110
123
  test('Test with a non revcoked credential', async () => {