@sd-jwt/sd-jwt-vc 0.7.2-next.6 → 0.7.2-next.7

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/README.md CHANGED
@@ -84,8 +84,28 @@ const verified = await sdjwt.verify(presentation);
84
84
  Check out more details in our [documentation](https://github.com/openwallet-foundation-labs/sd-jwt-js/tree/main/docs) or [examples](https://github.com/openwallet-foundation-labs/sd-jwt-js/tree/main/examples)
85
85
 
86
86
  ### Revocation
87
+
87
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.
88
89
 
90
+ ### Type Metadata
91
+
92
+ By setting the `loadTypeMetadataFormat` to `true` like this:
93
+
94
+ ```typescript
95
+ const sdjwt = new SDJwtVcInstance({
96
+ signer,
97
+ signAlg: 'EdDSA',
98
+ verifier,
99
+ hasher: digest,
100
+ hashAlg: 'SHA-256',
101
+ saltGenerator: generateSalt,
102
+ loadTypeMetadataFormat: true,
103
+ });
104
+ ```
105
+
106
+ The library will load load the type metadata format based on the `vct` value according to the [SD-JWT-VC specification](https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-04.html#name-type-metadata) and validate this schema.
107
+
108
+ Since at this point the display is not yet implemented, the library will only validate the schema and return the type metadata format. In the future the values of the type metadata can be fetched via a function call.
89
109
 
90
110
  ### Dependencies
91
111
 
package/dist/index.d.mts CHANGED
@@ -1,13 +1,32 @@
1
- import * as _sd_jwt_types from '@sd-jwt/types';
2
- import { SDJWTConfig, DisclosureFrame } from '@sd-jwt/types';
1
+ import { SDJWTConfig, kbPayload, kbHeader, DisclosureFrame } from '@sd-jwt/types';
3
2
  import { SdJwtPayload, SDJwtInstance } from '@sd-jwt/core';
4
3
 
4
+ /**
5
+ * https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-04.html#name-type-metadata-format
6
+ */
7
+ type TypeMetadataFormat = {
8
+ vct: string;
9
+ name?: string;
10
+ description?: string;
11
+ extends?: string;
12
+ 'extends#Integrity'?: string;
13
+ schema?: object;
14
+ schema_uri?: string;
15
+ 'schema_uri#Integrity'?: string;
16
+ };
17
+
18
+ type VcTFetcher = (uri: string, integrity?: string) => Promise<TypeMetadataFormat>;
19
+
20
+ type StatusListFetcher = (uri: string) => Promise<string>;
21
+ type StatusValidator = (status: number) => Promise<void>;
5
22
  /**
6
23
  * Configuration for SD-JWT-VC
7
24
  */
8
25
  type SDJWTVCConfig = SDJWTConfig & {
9
- statusListFetcher?: (uri: string) => Promise<string>;
10
- statusValidator?: (status: number) => Promise<void>;
26
+ statusListFetcher?: StatusListFetcher;
27
+ statusValidator?: StatusValidator;
28
+ vctFetcher?: VcTFetcher;
29
+ loadTypeMetadataFormat?: boolean;
11
30
  };
12
31
 
13
32
  interface SDJWTVCStatusReference {
@@ -23,11 +42,22 @@ interface SdJwtVcPayload extends SdJwtPayload {
23
42
  exp?: number;
24
43
  cnf?: unknown;
25
44
  vct: string;
45
+ 'vct#Integrity'?: string;
26
46
  status?: SDJWTVCStatusReference;
27
47
  sub?: string;
28
48
  iat?: number;
29
49
  }
30
50
 
51
+ type VerificationResult = {
52
+ payload: SdJwtVcPayload;
53
+ header: Record<string, unknown> | undefined;
54
+ kb: {
55
+ payload: kbPayload;
56
+ header: kbHeader;
57
+ } | undefined;
58
+ typeMetadataFormat?: TypeMetadataFormat;
59
+ };
60
+
31
61
  declare class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
32
62
  /**
33
63
  * The type of the SD-JWT-VC set in the header.typ field.
@@ -53,16 +83,44 @@ declare class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
53
83
  */
54
84
  private statusValidator;
55
85
  /**
56
- * Verifies the SD-JWT-VC.
86
+ * Verifies the SD-JWT-VC. It will validate the signature, the keybindings when required, the status, and the VCT.
87
+ */
88
+ verify(encodedSDJwt: string, requiredClaimKeys?: string[], requireKeyBindings?: boolean): Promise<VerificationResult>;
89
+ /**
90
+ * Default function to fetch the VCT from the uri. We assume that the vct is a URL that is used to fetch the VCT.
91
+ * @param uri
92
+ * @returns
93
+ */
94
+ private vctFetcher;
95
+ /**
96
+ * Validates the integrity of the response if the integrity is passed. If the integrity does not match, an error is thrown.
97
+ * @param integrity
98
+ * @param response
99
+ */
100
+ private validateIntegrity;
101
+ /**
102
+ * Fetches the content from the url with a timeout of 10 seconds.
103
+ * @param url
104
+ * @returns
105
+ */
106
+ private fetch;
107
+ /**
108
+ * Loads the schema either from the object or as fallback from the uri.
109
+ * @param typeMetadataFormat
110
+ * @returns
111
+ */
112
+ private loadSchema;
113
+ /**
114
+ * Verifies the VCT of the SD-JWT-VC. Returns the type metadata format. If the schema does not match, an error is thrown. If it matches, it will return the type metadata format.
115
+ * @param result
116
+ * @returns
117
+ */
118
+ private verifyVct;
119
+ /**
120
+ * Verifies the status of the SD-JWT-VC.
121
+ * @param result
57
122
  */
58
- verify(encodedSDJwt: string, requiredClaimKeys?: string[], requireKeyBindings?: boolean): Promise<{
59
- payload: SdJwtVcPayload;
60
- header: Record<string, unknown> | undefined;
61
- kb: {
62
- payload: _sd_jwt_types.kbPayload;
63
- header: _sd_jwt_types.kbHeader;
64
- } | undefined;
65
- }>;
123
+ private verifyStatus;
66
124
  }
67
125
 
68
- export { type SDJWTVCConfig, type SDJWTVCStatusReference, SDJwtVcInstance, type SdJwtVcPayload };
126
+ export { type SDJWTVCConfig, type SDJWTVCStatusReference, SDJwtVcInstance, type SdJwtVcPayload, type StatusListFetcher, type StatusValidator };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,32 @@
1
- import * as _sd_jwt_types from '@sd-jwt/types';
2
- import { SDJWTConfig, DisclosureFrame } from '@sd-jwt/types';
1
+ import { SDJWTConfig, kbPayload, kbHeader, DisclosureFrame } from '@sd-jwt/types';
3
2
  import { SdJwtPayload, SDJwtInstance } from '@sd-jwt/core';
4
3
 
4
+ /**
5
+ * https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-04.html#name-type-metadata-format
6
+ */
7
+ type TypeMetadataFormat = {
8
+ vct: string;
9
+ name?: string;
10
+ description?: string;
11
+ extends?: string;
12
+ 'extends#Integrity'?: string;
13
+ schema?: object;
14
+ schema_uri?: string;
15
+ 'schema_uri#Integrity'?: string;
16
+ };
17
+
18
+ type VcTFetcher = (uri: string, integrity?: string) => Promise<TypeMetadataFormat>;
19
+
20
+ type StatusListFetcher = (uri: string) => Promise<string>;
21
+ type StatusValidator = (status: number) => Promise<void>;
5
22
  /**
6
23
  * Configuration for SD-JWT-VC
7
24
  */
8
25
  type SDJWTVCConfig = SDJWTConfig & {
9
- statusListFetcher?: (uri: string) => Promise<string>;
10
- statusValidator?: (status: number) => Promise<void>;
26
+ statusListFetcher?: StatusListFetcher;
27
+ statusValidator?: StatusValidator;
28
+ vctFetcher?: VcTFetcher;
29
+ loadTypeMetadataFormat?: boolean;
11
30
  };
12
31
 
13
32
  interface SDJWTVCStatusReference {
@@ -23,11 +42,22 @@ interface SdJwtVcPayload extends SdJwtPayload {
23
42
  exp?: number;
24
43
  cnf?: unknown;
25
44
  vct: string;
45
+ 'vct#Integrity'?: string;
26
46
  status?: SDJWTVCStatusReference;
27
47
  sub?: string;
28
48
  iat?: number;
29
49
  }
30
50
 
51
+ type VerificationResult = {
52
+ payload: SdJwtVcPayload;
53
+ header: Record<string, unknown> | undefined;
54
+ kb: {
55
+ payload: kbPayload;
56
+ header: kbHeader;
57
+ } | undefined;
58
+ typeMetadataFormat?: TypeMetadataFormat;
59
+ };
60
+
31
61
  declare class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
32
62
  /**
33
63
  * The type of the SD-JWT-VC set in the header.typ field.
@@ -53,16 +83,44 @@ declare class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
53
83
  */
54
84
  private statusValidator;
55
85
  /**
56
- * Verifies the SD-JWT-VC.
86
+ * Verifies the SD-JWT-VC. It will validate the signature, the keybindings when required, the status, and the VCT.
87
+ */
88
+ verify(encodedSDJwt: string, requiredClaimKeys?: string[], requireKeyBindings?: boolean): Promise<VerificationResult>;
89
+ /**
90
+ * Default function to fetch the VCT from the uri. We assume that the vct is a URL that is used to fetch the VCT.
91
+ * @param uri
92
+ * @returns
93
+ */
94
+ private vctFetcher;
95
+ /**
96
+ * Validates the integrity of the response if the integrity is passed. If the integrity does not match, an error is thrown.
97
+ * @param integrity
98
+ * @param response
99
+ */
100
+ private validateIntegrity;
101
+ /**
102
+ * Fetches the content from the url with a timeout of 10 seconds.
103
+ * @param url
104
+ * @returns
105
+ */
106
+ private fetch;
107
+ /**
108
+ * Loads the schema either from the object or as fallback from the uri.
109
+ * @param typeMetadataFormat
110
+ * @returns
111
+ */
112
+ private loadSchema;
113
+ /**
114
+ * Verifies the VCT of the SD-JWT-VC. Returns the type metadata format. If the schema does not match, an error is thrown. If it matches, it will return the type metadata format.
115
+ * @param result
116
+ * @returns
117
+ */
118
+ private verifyVct;
119
+ /**
120
+ * Verifies the status of the SD-JWT-VC.
121
+ * @param result
57
122
  */
58
- verify(encodedSDJwt: string, requiredClaimKeys?: string[], requireKeyBindings?: boolean): Promise<{
59
- payload: SdJwtVcPayload;
60
- header: Record<string, unknown> | undefined;
61
- kb: {
62
- payload: _sd_jwt_types.kbPayload;
63
- header: _sd_jwt_types.kbHeader;
64
- } | undefined;
65
- }>;
123
+ private verifyStatus;
66
124
  }
67
125
 
68
- export { type SDJWTVCConfig, type SDJWTVCStatusReference, SDJwtVcInstance, type SdJwtVcPayload };
126
+ export { type SDJWTVCConfig, type SDJWTVCStatusReference, SDJwtVcInstance, type SdJwtVcPayload, type StatusListFetcher, type StatusValidator };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -17,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
17
18
  }
18
19
  return to;
19
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
20
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
30
  var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj);
22
31
  var __async = (__this, __arguments, generator) => {
@@ -51,6 +60,8 @@ module.exports = __toCommonJS(src_exports);
51
60
  var import_core = require("@sd-jwt/core");
52
61
  var import_utils = require("@sd-jwt/utils");
53
62
  var import_jwt_status_list = require("@sd-jwt/jwt-status-list");
63
+ var import_ajv = __toESM(require("ajv"));
64
+ var import_ajv_formats = __toESM(require("ajv-formats"));
54
65
  var SDJwtVcInstance = class _SDJwtVcInstance extends import_core.SDJwtInstance {
55
66
  constructor(userConfig) {
56
67
  super(userConfig);
@@ -118,11 +129,10 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends import_core.SDJwtInstance {
118
129
  });
119
130
  }
120
131
  /**
121
- * Verifies the SD-JWT-VC.
132
+ * Verifies the SD-JWT-VC. It will validate the signature, the keybindings when required, the status, and the VCT.
122
133
  */
123
134
  verify(encodedSDJwt, requiredClaimKeys, requireKeyBindings) {
124
135
  return __async(this, null, function* () {
125
- var _a, _b, _c;
126
136
  const result = yield __superGet(_SDJwtVcInstance.prototype, this, "verify").call(this, encodedSDJwt, requiredClaimKeys, requireKeyBindings).then((res) => {
127
137
  return {
128
138
  payload: res.payload,
@@ -130,9 +140,145 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends import_core.SDJwtInstance {
130
140
  kb: res.kb
131
141
  };
132
142
  });
143
+ yield this.verifyStatus(result);
144
+ if (this.userConfig.loadTypeMetadataFormat) {
145
+ yield this.verifyVct(result);
146
+ }
147
+ return result;
148
+ });
149
+ }
150
+ /**
151
+ * Default function to fetch the VCT from the uri. We assume that the vct is a URL that is used to fetch the VCT.
152
+ * @param uri
153
+ * @returns
154
+ */
155
+ vctFetcher(uri, integrity) {
156
+ return __async(this, null, function* () {
157
+ const elements = uri.split("/");
158
+ elements.splice(3, 0, ".well-known/vct");
159
+ const url = elements.join("/");
160
+ return this.fetch(url, integrity);
161
+ });
162
+ }
163
+ /**
164
+ * Validates the integrity of the response if the integrity is passed. If the integrity does not match, an error is thrown.
165
+ * @param integrity
166
+ * @param response
167
+ */
168
+ validateIntegrity(response, url, integrity) {
169
+ return __async(this, null, function* () {
170
+ if (integrity) {
171
+ const arrayBuffer = yield response.arrayBuffer();
172
+ const alg = integrity.split("-")[0];
173
+ const hashBuffer = yield this.userConfig.hasher(
174
+ arrayBuffer,
175
+ alg
176
+ );
177
+ const integrityHash = integrity.split("-")[1];
178
+ const hash = Array.from(new Uint8Array(hashBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
179
+ if (hash !== integrityHash) {
180
+ throw new Error(
181
+ `Integrity check for ${url} failed: is ${hash}, but expected ${integrityHash}`
182
+ );
183
+ }
184
+ }
185
+ });
186
+ }
187
+ /**
188
+ * Fetches the content from the url with a timeout of 10 seconds.
189
+ * @param url
190
+ * @returns
191
+ */
192
+ fetch(url, integrity) {
193
+ return __async(this, null, function* () {
194
+ const controller = new AbortController();
195
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
196
+ try {
197
+ const response = yield fetch(url, {
198
+ signal: controller.signal
199
+ });
200
+ if (!response.ok) {
201
+ throw new Error(yield response.text());
202
+ }
203
+ yield this.validateIntegrity(response.clone(), url, integrity);
204
+ return response.json();
205
+ } finally {
206
+ clearTimeout(timeoutId);
207
+ }
208
+ });
209
+ }
210
+ /**
211
+ * Loads the schema either from the object or as fallback from the uri.
212
+ * @param typeMetadataFormat
213
+ * @returns
214
+ */
215
+ loadSchema(typeMetadataFormat) {
216
+ return __async(this, null, function* () {
217
+ if (typeMetadataFormat.schema)
218
+ return typeMetadataFormat.schema;
219
+ if (typeMetadataFormat.schema_uri) {
220
+ const schema = yield this.fetch(
221
+ typeMetadataFormat.schema_uri,
222
+ typeMetadataFormat["schema_uri#Integrity"]
223
+ );
224
+ return schema;
225
+ }
226
+ throw new Error("No schema or schema_uri found");
227
+ });
228
+ }
229
+ /**
230
+ * Verifies the VCT of the SD-JWT-VC. Returns the type metadata format. If the schema does not match, an error is thrown. If it matches, it will return the type metadata format.
231
+ * @param result
232
+ * @returns
233
+ */
234
+ verifyVct(result) {
235
+ return __async(this, null, function* () {
236
+ var _a;
237
+ const fetcher = (_a = this.userConfig.vctFetcher) != null ? _a : this.vctFetcher.bind(this);
238
+ const typeMetadataFormat = yield fetcher(
239
+ result.payload.vct,
240
+ result.payload["vct#Integrity"]
241
+ );
242
+ if (typeMetadataFormat.extends) {
243
+ }
244
+ const schema = yield this.loadSchema(typeMetadataFormat);
245
+ const loadedSchemas = /* @__PURE__ */ new Set();
246
+ const ajv = new import_ajv.default({
247
+ loadSchema: (uri) => __async(this, null, function* () {
248
+ if (loadedSchemas.has(uri)) {
249
+ return {};
250
+ }
251
+ const response = yield fetch(uri);
252
+ if (!response.ok) {
253
+ throw new Error(
254
+ `Error fetching schema: ${response.status} ${yield response.text()}`
255
+ );
256
+ }
257
+ loadedSchemas.add(uri);
258
+ return response.json();
259
+ })
260
+ });
261
+ (0, import_ajv_formats.default)(ajv);
262
+ const validate = yield ajv.compileAsync(schema);
263
+ const valid = validate(result.payload);
264
+ if (!valid) {
265
+ throw new import_utils.SDJWTException(
266
+ `Payload does not match the schema: ${JSON.stringify(validate.errors)}`
267
+ );
268
+ }
269
+ return typeMetadataFormat;
270
+ });
271
+ }
272
+ /**
273
+ * Verifies the status of the SD-JWT-VC.
274
+ * @param result
275
+ */
276
+ verifyStatus(result) {
277
+ return __async(this, null, function* () {
278
+ var _a, _b, _c;
133
279
  if (result.payload.status) {
134
280
  if (result.payload.status.status_list) {
135
- const fetcher = (_a = this.userConfig.statusListFetcher) != null ? _a : this.statusListFetcher;
281
+ const fetcher = (_a = this.userConfig.statusListFetcher) != null ? _a : this.statusListFetcher.bind(this);
136
282
  const statusListJWT = yield fetcher(
137
283
  result.payload.status.status_list.uri
138
284
  );
@@ -145,11 +291,10 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends import_core.SDJwtInstance {
145
291
  const status = statusList.getStatus(
146
292
  result.payload.status.status_list.idx
147
293
  );
148
- const statusValidator = (_c = this.userConfig.statusValidator) != null ? _c : this.statusValidator;
294
+ const statusValidator = (_c = this.userConfig.statusValidator) != null ? _c : this.statusValidator.bind(this);
149
295
  yield statusValidator(status);
150
296
  }
151
297
  }
152
- return result;
153
298
  });
154
299
  }
155
300
  };
package/dist/index.mjs CHANGED
@@ -28,6 +28,8 @@ import { SDJWTException } from "@sd-jwt/utils";
28
28
  import {
29
29
  getListFromStatusListJWT
30
30
  } from "@sd-jwt/jwt-status-list";
31
+ import Ajv from "ajv";
32
+ import addFormats from "ajv-formats";
31
33
  var SDJwtVcInstance = class _SDJwtVcInstance extends SDJwtInstance {
32
34
  constructor(userConfig) {
33
35
  super(userConfig);
@@ -95,11 +97,10 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends SDJwtInstance {
95
97
  });
96
98
  }
97
99
  /**
98
- * Verifies the SD-JWT-VC.
100
+ * Verifies the SD-JWT-VC. It will validate the signature, the keybindings when required, the status, and the VCT.
99
101
  */
100
102
  verify(encodedSDJwt, requiredClaimKeys, requireKeyBindings) {
101
103
  return __async(this, null, function* () {
102
- var _a, _b, _c;
103
104
  const result = yield __superGet(_SDJwtVcInstance.prototype, this, "verify").call(this, encodedSDJwt, requiredClaimKeys, requireKeyBindings).then((res) => {
104
105
  return {
105
106
  payload: res.payload,
@@ -107,9 +108,145 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends SDJwtInstance {
107
108
  kb: res.kb
108
109
  };
109
110
  });
111
+ yield this.verifyStatus(result);
112
+ if (this.userConfig.loadTypeMetadataFormat) {
113
+ yield this.verifyVct(result);
114
+ }
115
+ return result;
116
+ });
117
+ }
118
+ /**
119
+ * Default function to fetch the VCT from the uri. We assume that the vct is a URL that is used to fetch the VCT.
120
+ * @param uri
121
+ * @returns
122
+ */
123
+ vctFetcher(uri, integrity) {
124
+ return __async(this, null, function* () {
125
+ const elements = uri.split("/");
126
+ elements.splice(3, 0, ".well-known/vct");
127
+ const url = elements.join("/");
128
+ return this.fetch(url, integrity);
129
+ });
130
+ }
131
+ /**
132
+ * Validates the integrity of the response if the integrity is passed. If the integrity does not match, an error is thrown.
133
+ * @param integrity
134
+ * @param response
135
+ */
136
+ validateIntegrity(response, url, integrity) {
137
+ return __async(this, null, function* () {
138
+ if (integrity) {
139
+ const arrayBuffer = yield response.arrayBuffer();
140
+ const alg = integrity.split("-")[0];
141
+ const hashBuffer = yield this.userConfig.hasher(
142
+ arrayBuffer,
143
+ alg
144
+ );
145
+ const integrityHash = integrity.split("-")[1];
146
+ const hash = Array.from(new Uint8Array(hashBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
147
+ if (hash !== integrityHash) {
148
+ throw new Error(
149
+ `Integrity check for ${url} failed: is ${hash}, but expected ${integrityHash}`
150
+ );
151
+ }
152
+ }
153
+ });
154
+ }
155
+ /**
156
+ * Fetches the content from the url with a timeout of 10 seconds.
157
+ * @param url
158
+ * @returns
159
+ */
160
+ fetch(url, integrity) {
161
+ return __async(this, null, function* () {
162
+ const controller = new AbortController();
163
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
164
+ try {
165
+ const response = yield fetch(url, {
166
+ signal: controller.signal
167
+ });
168
+ if (!response.ok) {
169
+ throw new Error(yield response.text());
170
+ }
171
+ yield this.validateIntegrity(response.clone(), url, integrity);
172
+ return response.json();
173
+ } finally {
174
+ clearTimeout(timeoutId);
175
+ }
176
+ });
177
+ }
178
+ /**
179
+ * Loads the schema either from the object or as fallback from the uri.
180
+ * @param typeMetadataFormat
181
+ * @returns
182
+ */
183
+ loadSchema(typeMetadataFormat) {
184
+ return __async(this, null, function* () {
185
+ if (typeMetadataFormat.schema)
186
+ return typeMetadataFormat.schema;
187
+ if (typeMetadataFormat.schema_uri) {
188
+ const schema = yield this.fetch(
189
+ typeMetadataFormat.schema_uri,
190
+ typeMetadataFormat["schema_uri#Integrity"]
191
+ );
192
+ return schema;
193
+ }
194
+ throw new Error("No schema or schema_uri found");
195
+ });
196
+ }
197
+ /**
198
+ * Verifies the VCT of the SD-JWT-VC. Returns the type metadata format. If the schema does not match, an error is thrown. If it matches, it will return the type metadata format.
199
+ * @param result
200
+ * @returns
201
+ */
202
+ verifyVct(result) {
203
+ return __async(this, null, function* () {
204
+ var _a;
205
+ const fetcher = (_a = this.userConfig.vctFetcher) != null ? _a : this.vctFetcher.bind(this);
206
+ const typeMetadataFormat = yield fetcher(
207
+ result.payload.vct,
208
+ result.payload["vct#Integrity"]
209
+ );
210
+ if (typeMetadataFormat.extends) {
211
+ }
212
+ const schema = yield this.loadSchema(typeMetadataFormat);
213
+ const loadedSchemas = /* @__PURE__ */ new Set();
214
+ const ajv = new Ajv({
215
+ loadSchema: (uri) => __async(this, null, function* () {
216
+ if (loadedSchemas.has(uri)) {
217
+ return {};
218
+ }
219
+ const response = yield fetch(uri);
220
+ if (!response.ok) {
221
+ throw new Error(
222
+ `Error fetching schema: ${response.status} ${yield response.text()}`
223
+ );
224
+ }
225
+ loadedSchemas.add(uri);
226
+ return response.json();
227
+ })
228
+ });
229
+ addFormats(ajv);
230
+ const validate = yield ajv.compileAsync(schema);
231
+ const valid = validate(result.payload);
232
+ if (!valid) {
233
+ throw new SDJWTException(
234
+ `Payload does not match the schema: ${JSON.stringify(validate.errors)}`
235
+ );
236
+ }
237
+ return typeMetadataFormat;
238
+ });
239
+ }
240
+ /**
241
+ * Verifies the status of the SD-JWT-VC.
242
+ * @param result
243
+ */
244
+ verifyStatus(result) {
245
+ return __async(this, null, function* () {
246
+ var _a, _b, _c;
110
247
  if (result.payload.status) {
111
248
  if (result.payload.status.status_list) {
112
- const fetcher = (_a = this.userConfig.statusListFetcher) != null ? _a : this.statusListFetcher;
249
+ const fetcher = (_a = this.userConfig.statusListFetcher) != null ? _a : this.statusListFetcher.bind(this);
113
250
  const statusListJWT = yield fetcher(
114
251
  result.payload.status.status_list.uri
115
252
  );
@@ -122,11 +259,10 @@ var SDJwtVcInstance = class _SDJwtVcInstance extends SDJwtInstance {
122
259
  const status = statusList.getStatus(
123
260
  result.payload.status.status_list.idx
124
261
  );
125
- const statusValidator = (_c = this.userConfig.statusValidator) != null ? _c : this.statusValidator;
262
+ const statusValidator = (_c = this.userConfig.statusValidator) != null ? _c : this.statusValidator.bind(this);
126
263
  yield statusValidator(status);
127
264
  }
128
265
  }
129
- return result;
130
266
  });
131
267
  }
132
268
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sd-jwt/sd-jwt-vc",
3
- "version": "0.7.2-next.6+c3f4580",
3
+ "version": "0.7.2-next.7+96e76a9",
4
4
  "description": "sd-jwt draft 7 implementation in typescript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -15,7 +15,7 @@
15
15
  "build": "rm -rf **/dist && tsup",
16
16
  "lint": "biome lint ./src",
17
17
  "test": "pnpm run test:node && pnpm run test:browser && pnpm run test:e2e && pnpm run test:cov",
18
- "test:node": "vitest run ./src/test/*.spec.ts && vitest run ./src/test/*.spec.ts --environment jsdom",
18
+ "test:node": "vitest run ./src/test/*.spec.ts",
19
19
  "test:browser": "vitest run ./src/test/*.spec.ts --environment jsdom",
20
20
  "test:e2e": "vitest run ./test/*e2e.spec.ts --environment node",
21
21
  "test:cov": "vitest run --coverage"
@@ -39,14 +39,17 @@
39
39
  },
40
40
  "license": "Apache-2.0",
41
41
  "dependencies": {
42
- "@sd-jwt/core": "0.7.2-next.6+c3f4580",
43
- "@sd-jwt/jwt-status-list": "0.7.2-next.6+c3f4580",
44
- "@sd-jwt/utils": "0.7.2-next.6+c3f4580"
42
+ "@sd-jwt/core": "0.7.2-next.7+96e76a9",
43
+ "@sd-jwt/jwt-status-list": "0.7.2-next.7+96e76a9",
44
+ "@sd-jwt/utils": "0.7.2-next.7+96e76a9",
45
+ "ajv": "^8.17.1",
46
+ "ajv-formats": "^3.0.1"
45
47
  },
46
48
  "devDependencies": {
47
- "@sd-jwt/crypto-nodejs": "0.7.2-next.6+c3f4580",
48
- "@sd-jwt/types": "0.7.2-next.6+c3f4580",
49
- "jose": "^5.2.2"
49
+ "@sd-jwt/crypto-nodejs": "0.7.2-next.7+96e76a9",
50
+ "@sd-jwt/types": "0.7.2-next.7+96e76a9",
51
+ "jose": "^5.2.2",
52
+ "msw": "^2.3.5"
50
53
  },
51
54
  "publishConfig": {
52
55
  "access": "public"
@@ -64,5 +67,5 @@
64
67
  "esm"
65
68
  ]
66
69
  },
67
- "gitHead": "c3f4580c6e93856b5eaaad98d12984a7be932639"
70
+ "gitHead": "96e76a9d553bff34274b5ad243d0154cd220061b"
68
71
  }
@@ -1,11 +1,19 @@
1
1
  import type { SDJWTConfig } from '@sd-jwt/types';
2
+ import type { VcTFetcher } from './sd-jwt-vc-vct';
3
+
4
+ export type StatusListFetcher = (uri: string) => Promise<string>;
5
+ export type StatusValidator = (status: number) => Promise<void>;
2
6
 
3
7
  /**
4
8
  * Configuration for SD-JWT-VC
5
9
  */
6
10
  export type SDJWTVCConfig = SDJWTConfig & {
7
11
  // A function that fetches the status list from the uri. If not provided, the library will assume that the response is a compact JWT.
8
- statusListFetcher?: (uri: string) => Promise<string>;
12
+ statusListFetcher?: StatusListFetcher;
9
13
  // validte the status and decide if the status is valid or not. If not provided, the code will continue if it is 0, otherwise it will throw an error.
10
- statusValidator?: (status: number) => Promise<void>;
14
+ statusValidator?: StatusValidator;
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
+ vctFetcher?: VcTFetcher;
17
+ // if set to true, it will load the metadata format based on the vct value. If not provided, it will default to false.
18
+ loadTypeMetadataFormat?: boolean;
11
19
  };
@@ -1,13 +1,23 @@
1
1
  import { Jwt, SDJwtInstance } from '@sd-jwt/core';
2
- import type { DisclosureFrame, Verifier } from '@sd-jwt/types';
2
+ import type { DisclosureFrame, Hasher, Verifier } from '@sd-jwt/types';
3
3
  import { SDJWTException } from '@sd-jwt/utils';
4
4
  import type { SdJwtVcPayload } from './sd-jwt-vc-payload';
5
- import type { SDJWTVCConfig } from './sd-jwt-vc-config';
5
+ import type {
6
+ SDJWTVCConfig,
7
+ StatusListFetcher,
8
+ StatusValidator,
9
+ } from './sd-jwt-vc-config';
6
10
  import {
7
11
  type StatusListJWTPayload,
8
12
  getListFromStatusListJWT,
13
+ type StatusListJWTHeaderParameters,
9
14
  } from '@sd-jwt/jwt-status-list';
10
- import type { StatusListJWTHeaderParameters } from '@sd-jwt/jwt-status-list';
15
+ import type { TypeMetadataFormat } from './sd-jwt-vc-type-metadata-format';
16
+ import Ajv, { type SchemaObject } from 'ajv';
17
+ import type { VerificationResult } from './verification-result';
18
+ import addFormats from 'ajv-formats';
19
+ import type { VcTFetcher } from './sd-jwt-vc-vct';
20
+
11
21
  export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
12
22
  /**
13
23
  * The type of the SD-JWT-VC set in the header.typ field.
@@ -95,7 +105,7 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
95
105
  }
96
106
 
97
107
  /**
98
- * Verifies the SD-JWT-VC.
108
+ * Verifies the SD-JWT-VC. It will validate the signature, the keybindings when required, the status, and the VCT.
99
109
  */
100
110
  async verify(
101
111
  encodedSDJwt: string,
@@ -103,7 +113,7 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
103
113
  requireKeyBindings?: boolean,
104
114
  ) {
105
115
  // Call the parent class's verify method
106
- const result = await super
116
+ const result: VerificationResult = await super
107
117
  .verify(encodedSDJwt, requiredClaimKeys, requireKeyBindings)
108
118
  .then((res) => {
109
119
  return {
@@ -113,12 +123,167 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
113
123
  };
114
124
  });
115
125
 
126
+ await this.verifyStatus(result);
127
+ if (this.userConfig.loadTypeMetadataFormat) {
128
+ await this.verifyVct(result);
129
+ }
130
+ return result;
131
+ }
132
+
133
+ /**
134
+ * Default function to fetch the VCT from the uri. We assume that the vct is a URL that is used to fetch the VCT.
135
+ * @param uri
136
+ * @returns
137
+ */
138
+ private async vctFetcher(
139
+ uri: string,
140
+ integrity?: string,
141
+ ): Promise<TypeMetadataFormat> {
142
+ // modify the uri based on https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-04.html#section-6.3.1
143
+ const elements = uri.split('/');
144
+ //insert a new element on the thrid position, but not replace it
145
+ elements.splice(3, 0, '.well-known/vct');
146
+ const url = elements.join('/');
147
+ return this.fetch<TypeMetadataFormat>(url, integrity);
148
+ }
149
+
150
+ /**
151
+ * Validates the integrity of the response if the integrity is passed. If the integrity does not match, an error is thrown.
152
+ * @param integrity
153
+ * @param response
154
+ */
155
+ private async validateIntegrity(
156
+ response: Response,
157
+ url: string,
158
+ integrity?: string,
159
+ ) {
160
+ if (integrity) {
161
+ // validate the integrity of the response according to https://www.w3.org/TR/SRI/
162
+ const arrayBuffer = await response.arrayBuffer();
163
+ const alg = integrity.split('-')[0];
164
+ //TODO: error handling when a hasher is passed that is not supporting the required algorithm acording to the spec
165
+ const hashBuffer = await (this.userConfig.hasher as Hasher)(
166
+ arrayBuffer,
167
+ alg,
168
+ );
169
+ const integrityHash = integrity.split('-')[1];
170
+ const hash = Array.from(new Uint8Array(hashBuffer))
171
+ .map((byte) => byte.toString(16).padStart(2, '0'))
172
+ .join('');
173
+ if (hash !== integrityHash) {
174
+ throw new Error(
175
+ `Integrity check for ${url} failed: is ${hash}, but expected ${integrityHash}`,
176
+ );
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Fetches the content from the url with a timeout of 10 seconds.
183
+ * @param url
184
+ * @returns
185
+ */
186
+ private async fetch<T>(url: string, integrity?: string) {
187
+ const controller = new AbortController();
188
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
189
+ try {
190
+ const response = await fetch(url, {
191
+ signal: controller.signal,
192
+ });
193
+ if (!response.ok) {
194
+ throw new Error(await response.text());
195
+ }
196
+ await this.validateIntegrity(response.clone(), url, integrity);
197
+ return response.json() as Promise<T>;
198
+ } finally {
199
+ clearTimeout(timeoutId);
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Loads the schema either from the object or as fallback from the uri.
205
+ * @param typeMetadataFormat
206
+ * @returns
207
+ */
208
+ private async loadSchema(typeMetadataFormat: TypeMetadataFormat) {
209
+ //if schema is present, return it
210
+ if (typeMetadataFormat.schema) return typeMetadataFormat.schema;
211
+ if (typeMetadataFormat.schema_uri) {
212
+ const schema = await this.fetch<SchemaObject>(
213
+ typeMetadataFormat.schema_uri,
214
+ typeMetadataFormat['schema_uri#Integrity'],
215
+ );
216
+ return schema;
217
+ }
218
+ throw new Error('No schema or schema_uri found');
219
+ }
220
+
221
+ /**
222
+ * Verifies the VCT of the SD-JWT-VC. Returns the type metadata format. If the schema does not match, an error is thrown. If it matches, it will return the type metadata format.
223
+ * @param result
224
+ * @returns
225
+ */
226
+ private async verifyVct(
227
+ result: VerificationResult,
228
+ ): Promise<TypeMetadataFormat | undefined> {
229
+ const fetcher: VcTFetcher =
230
+ this.userConfig.vctFetcher ?? this.vctFetcher.bind(this);
231
+ const typeMetadataFormat = await fetcher(
232
+ result.payload.vct,
233
+ result.payload['vct#Integrity'],
234
+ );
235
+
236
+ if (typeMetadataFormat.extends) {
237
+ // implement based on https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-04.html#name-extending-type-metadata
238
+ //TODO: needs to be implemented. Unclear at this point which values will overwrite the values from the extended type metadata format
239
+ }
240
+
241
+ //init the json schema validator, load referenced schemas on demand
242
+ const schema = await this.loadSchema(typeMetadataFormat);
243
+ const loadedSchemas = new Set<string>();
244
+ // init the json schema validator
245
+ const ajv = new Ajv({
246
+ loadSchema: async (uri: string) => {
247
+ if (loadedSchemas.has(uri)) {
248
+ return {};
249
+ }
250
+ const response = await fetch(uri);
251
+ if (!response.ok) {
252
+ throw new Error(
253
+ `Error fetching schema: ${
254
+ response.status
255
+ } ${await response.text()}`,
256
+ );
257
+ }
258
+ loadedSchemas.add(uri);
259
+ return response.json();
260
+ },
261
+ });
262
+ addFormats(ajv);
263
+ const validate = await ajv.compileAsync(schema);
264
+ const valid = validate(result.payload);
265
+
266
+ if (!valid) {
267
+ throw new SDJWTException(
268
+ `Payload does not match the schema: ${JSON.stringify(validate.errors)}`,
269
+ );
270
+ }
271
+
272
+ return typeMetadataFormat;
273
+ }
274
+
275
+ /**
276
+ * Verifies the status of the SD-JWT-VC.
277
+ * @param result
278
+ */
279
+ private async verifyStatus(result: VerificationResult): Promise<void> {
116
280
  if (result.payload.status) {
117
281
  //checks if a status field is present in the payload based on https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-02.html
118
282
  if (result.payload.status.status_list) {
119
283
  // fetch the status list from the uri
120
- const fetcher =
121
- this.userConfig.statusListFetcher ?? this.statusListFetcher;
284
+ const fetcher: StatusListFetcher =
285
+ this.userConfig.statusListFetcher ??
286
+ this.statusListFetcher.bind(this);
122
287
  // fetch the status list from the uri
123
288
  const statusListJWT = await fetcher(
124
289
  result.payload.status.status_list.uri,
@@ -146,12 +311,10 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
146
311
  );
147
312
 
148
313
  // validate the status
149
- const statusValidator =
150
- this.userConfig.statusValidator ?? this.statusValidator;
314
+ const statusValidator: StatusValidator =
315
+ this.userConfig.statusValidator ?? this.statusValidator.bind(this);
151
316
  await statusValidator(status);
152
317
  }
153
318
  }
154
-
155
- return result;
156
319
  }
157
320
  }
@@ -12,6 +12,8 @@ export interface SdJwtVcPayload extends SdJwtPayload {
12
12
  cnf?: unknown;
13
13
  // REQUIRED. The type of the Verifiable Credential, e.g., https://credentials.example.com/identity_credential, as defined in Section 3.2.2.1.1.
14
14
  vct: string;
15
+ // OPTIONAL. If passed, the loaded type metadata format has to be validated according to https://www.w3.org/TR/SRI/
16
+ 'vct#Integrity'?: string;
15
17
  // OPTIONAL. The information on how to read the status of the Verifiable Credential. See [https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-02.html] for more information.
16
18
  status?: SDJWTVCStatusReference;
17
19
  // OPTIONAL. The identifier of the Subject of the Verifiable Credential. The Issuer MAY use it to provide the Subject identifier known by the Issuer. There is no requirement for a binding to exist between sub and cnf claims.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-04.html#name-type-metadata-format
3
+ */
4
+ export type TypeMetadataFormat = {
5
+ vct: string; // REQUIRED. A URI that uniquely identifies the type. This URI MUST be dereferenceable to a JSON document that describes the type.
6
+ name?: string; // OPTIONAL. A human-readable name for the type, intended for developers reading the JSON document.
7
+ description?: string; // OPTIONAL. A human-readable description for the type, intended for developers reading the JSON document.
8
+ extends?: string; // OPTIONAL. A URI of another type that this type extends, as described in Section 6.4.
9
+ 'extends#Integrity'?: string; // OPTIONAL. Validating the ingegrity of the extends field
10
+ schema?: object; // OPTIONAL. An embedded JSON Schema document describing the structure of the Verifiable Credential as described in Section 6.5.1. schema MUST NOT be used if schema_uri is present.
11
+ schema_uri?: string; // OPTIONAL. A URL pointing to a JSON Schema document describing the structure of the Verifiable Credential as described in Section 6.5.1. schema_uri MUST NOT be used if schema is present.
12
+ 'schema_uri#Integrity'?: string; // OPTIONAL. Validating the integrity of the schema_uri field.
13
+ };
@@ -0,0 +1,6 @@
1
+ import type { TypeMetadataFormat } from './sd-jwt-vc-type-metadata-format';
2
+
3
+ export type VcTFetcher = (
4
+ uri: string,
5
+ integrity?: string,
6
+ ) => Promise<TypeMetadataFormat>;
@@ -17,10 +17,13 @@ import {
17
17
  import { SignJWT } from 'jose';
18
18
 
19
19
  const iss = 'ExampleIssuer';
20
- const vct = 'https://example.com/schema/1';
20
+ const vct = 'ExampleCredentialType';
21
21
  const iat = new Date().getTime() / 1000;
22
22
 
23
23
  const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
24
+
25
+ //TODO: to simulate a hosted status list, use the same appraoch as in vct.spec.ts
26
+
24
27
  const createSignerVerifier = () => {
25
28
  const signer: Signer = async (data: string) => {
26
29
  const sig = Crypto.sign(null, Buffer.from(data), privateKey);
@@ -0,0 +1,128 @@
1
+ import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
2
+ import type { DisclosureFrame, Signer, Verifier } from '@sd-jwt/types';
3
+ import { describe, test, beforeAll, afterAll } from 'vitest';
4
+ import { SDJwtVcInstance } from '..';
5
+ import type { SdJwtVcPayload } from '../sd-jwt-vc-payload';
6
+ import Crypto from 'node:crypto';
7
+ import { setupServer } from 'msw/node';
8
+ import { HttpResponse, http } from 'msw';
9
+ import { afterEach } from 'node:test';
10
+ import type { TypeMetadataFormat } from '../sd-jwt-vc-type-metadata-format';
11
+
12
+ const restHandlers = [
13
+ http.get('http://example.com/schema/example', () => {
14
+ const res = {
15
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
16
+ type: 'object',
17
+ properties: {
18
+ vct: {
19
+ type: 'string',
20
+ },
21
+ iss: {
22
+ type: 'string',
23
+ },
24
+ nbf: {
25
+ type: 'number',
26
+ },
27
+ exp: {
28
+ type: 'number',
29
+ },
30
+ cnf: {
31
+ type: 'object',
32
+ },
33
+ status: {
34
+ type: 'object',
35
+ },
36
+ firstName: {
37
+ type: 'string',
38
+ },
39
+ },
40
+ required: ['iss', 'vct'],
41
+ };
42
+ return HttpResponse.json(res);
43
+ }),
44
+ http.get('http://exmaple.com/.well-known/vct/example', () => {
45
+ const res: TypeMetadataFormat = {
46
+ vct: 'http://example.com/example',
47
+ name: 'ExampleCredentialType',
48
+ description: 'An example credential type',
49
+ schema_uri: 'http://example.com/schema/example',
50
+ //this value could be generated on demand to make it easier when changing the values
51
+ 'schema_uri#Integrity':
52
+ 'sha256-48a61b283ded3b55e8d9a9b063327641dc4c53f76bd5daa96c23f232822167ae',
53
+ };
54
+ return HttpResponse.json(res);
55
+ }),
56
+ ];
57
+
58
+ //this value could be generated on demand to make it easier when changing the values
59
+ const vctIntegrity =
60
+ 'sha256-96bed58130a44af05ae8970aa9caa0bf0135cd15afe721ea29f553394692acef';
61
+
62
+ const server = setupServer(...restHandlers);
63
+
64
+ const iss = 'ExampleIssuer';
65
+ const vct = 'http://exmaple.com/example';
66
+ const iat = new Date().getTime() / 1000;
67
+
68
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
69
+
70
+ const createSignerVerifier = () => {
71
+ const signer: Signer = async (data: string) => {
72
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
73
+ return Buffer.from(sig).toString('base64url');
74
+ };
75
+ const verifier: Verifier = async (data: string, sig: string) => {
76
+ return Crypto.verify(
77
+ null,
78
+ Buffer.from(data),
79
+ publicKey,
80
+ Buffer.from(sig, 'base64url'),
81
+ );
82
+ };
83
+ return { signer, verifier };
84
+ };
85
+
86
+ describe('App', () => {
87
+ beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
88
+
89
+ afterAll(() => server.close());
90
+
91
+ afterEach(() => server.resetHandlers());
92
+
93
+ test('VCT Validation', async () => {
94
+ const { signer, verifier } = createSignerVerifier();
95
+ const sdjwt = new SDJwtVcInstance({
96
+ signer,
97
+ signAlg: 'EdDSA',
98
+ verifier,
99
+ hasher: digest,
100
+ hashAlg: 'SHA-256',
101
+ saltGenerator: generateSalt,
102
+ loadTypeMetadataFormat: true,
103
+ });
104
+
105
+ const claims = {
106
+ firstname: 'John',
107
+ };
108
+ const disclosureFrame = {
109
+ _sd: ['firstname'],
110
+ };
111
+
112
+ const expectedPayload: SdJwtVcPayload = {
113
+ iat,
114
+ iss,
115
+ vct,
116
+ 'vct#Integrity': vctIntegrity,
117
+ ...claims,
118
+ };
119
+ const encodedSdjwt = await sdjwt.issue(
120
+ expectedPayload,
121
+ disclosureFrame as unknown as DisclosureFrame<SdJwtVcPayload>,
122
+ );
123
+
124
+ await sdjwt.verify(encodedSdjwt);
125
+ });
126
+
127
+ //TODO: we need tests with an embedded schema, extended and maybe also to test the errors when schema information is not available or the integrity is not valid
128
+ });
@@ -0,0 +1,15 @@
1
+ import type { kbPayload, kbHeader } from '@sd-jwt/types';
2
+ import type { SdJwtVcPayload } from './sd-jwt-vc-payload';
3
+ import type { TypeMetadataFormat } from './sd-jwt-vc-type-metadata-format';
4
+
5
+ export type VerificationResult = {
6
+ payload: SdJwtVcPayload;
7
+ header: Record<string, unknown> | undefined;
8
+ kb:
9
+ | {
10
+ payload: kbPayload;
11
+ header: kbHeader;
12
+ }
13
+ | undefined;
14
+ typeMetadataFormat?: TypeMetadataFormat;
15
+ };
@@ -1,5 +1,5 @@
1
1
  import Crypto from 'node:crypto';
2
- import { SDJwtVcInstance, SdJwtVcPayload } from '../src/index';
2
+ import { SDJwtVcInstance } from '../src/index';
3
3
  import type {
4
4
  DisclosureFrame,
5
5
  PresentationFrame,
@@ -29,7 +29,7 @@ const createSignerVerifier = () => {
29
29
  };
30
30
 
31
31
  const iss = 'ExampleIssuer';
32
- const vct = 'https://example.com/schema/1';
32
+ const vct = 'ExampleCredentials';
33
33
  const iat = new Date().getTime() / 1000;
34
34
 
35
35
  describe('App', () => {