@sd-jwt/sd-jwt-vc 0.1.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 +17 -0
- package/LICENSE +201 -0
- package/README.md +173 -0
- package/dist/index.d.mts +1106 -0
- package/dist/index.d.ts +1106 -0
- package/dist/index.js +773 -0
- package/dist/index.mjs +734 -0
- package/package.json +67 -0
- package/src/index.ts +7 -0
- package/src/sd-jwt-vc-config.ts +25 -0
- package/src/sd-jwt-vc-instance.ts +679 -0
- package/src/sd-jwt-vc-payload.ts +23 -0
- package/src/sd-jwt-vc-status-reference.ts +9 -0
- package/src/sd-jwt-vc-type-metadata-format.ts +271 -0
- package/src/sd-jwt-vc-vct.ts +6 -0
- package/src/test/index.spec.ts +180 -0
- package/src/test/vct.spec.ts +684 -0
- package/src/verification-result.ts +16 -0
- package/test/app-e2e.spec.ts +286 -0
- package/test/array_data_types.json +31 -0
- package/test/array_full_sd.json +21 -0
- package/test/array_in_sd.json +13 -0
- package/test/array_nested_in_plain.json +29 -0
- package/test/array_none_disclosed.json +17 -0
- package/test/array_of_nulls.json +15 -0
- package/test/array_of_objects.json +65 -0
- package/test/array_of_scalars.json +19 -0
- package/test/array_recursive_sd.json +35 -0
- package/test/array_recursive_sd_some_disclosed.json +63 -0
- package/test/complex.json +43 -0
- package/test/header_mod.json +48 -0
- package/test/json_serialization.json +48 -0
- package/test/key_binding.json +48 -0
- package/test/no_sd.json +36 -0
- package/test/object_data_types.json +62 -0
- package/test/recursions.json +117 -0
- package/tsconfig.json +7 -0
- package/vitest.config.mts +4 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const BackgroundImageSchema = z.looseObject({
|
|
4
|
+
/** REQUIRED. A URI pointing to the background image. */
|
|
5
|
+
uri: z.string(),
|
|
6
|
+
/** OPTIONAL. An "integrity metadata" string as described in Section 7. */
|
|
7
|
+
'uri#integrity': z.string().optional(),
|
|
8
|
+
});
|
|
9
|
+
export type BackgroundImage = z.infer<typeof BackgroundImageSchema>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Logo metadata used in rendering a credential.
|
|
13
|
+
*/
|
|
14
|
+
export const LogoSchema = z.looseObject({
|
|
15
|
+
/** REQUIRED. A URI pointing to the logo image. */
|
|
16
|
+
uri: z.string(),
|
|
17
|
+
/** OPTIONAL. An "integrity metadata" string as described in Section 7. */
|
|
18
|
+
'uri#integrity': z.string().optional(),
|
|
19
|
+
/** OPTIONAL. A string containing alternative text for the logo image. */
|
|
20
|
+
alt_text: z.string().optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type Logo = z.infer<typeof LogoSchema>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The simple rendering method is intended for applications that do not support SVG.
|
|
27
|
+
*/
|
|
28
|
+
export const SimpleRenderingSchema = z.looseObject({
|
|
29
|
+
/** OPTIONAL. Logo metadata to display for the credential. */
|
|
30
|
+
logo: LogoSchema.optional(),
|
|
31
|
+
/** OPTIONAL. RGB color value for the credential background (e.g., "#FFFFFF"). */
|
|
32
|
+
background_color: z.string().optional(),
|
|
33
|
+
/** OPTIONAL. RGB color value for the credential text (e.g., "#000000"). */
|
|
34
|
+
text_color: z.string().optional(),
|
|
35
|
+
/** OPTIONAL. An object containing information about the background image to be displayed for the type. */
|
|
36
|
+
background_image: BackgroundImageSchema.optional(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export type SimpleRendering = z.infer<typeof SimpleRenderingSchema>;
|
|
40
|
+
|
|
41
|
+
/** Enum of valid values for rendering orientation. */
|
|
42
|
+
export const OrientationSchema = z.enum(['portrait', 'landscape']);
|
|
43
|
+
|
|
44
|
+
/** Enum of valid values for rendering color schemes. */
|
|
45
|
+
export const ColorSchemeSchema = z.enum(['light', 'dark']);
|
|
46
|
+
|
|
47
|
+
/** Enum of valid values for rendering contrast. */
|
|
48
|
+
export const ContrastSchema = z.enum(['normal', 'high']);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Properties that describe the display preferences for an SVG template rendering.
|
|
52
|
+
*/
|
|
53
|
+
export const SvgTemplatePropertiesSchema = z.looseObject({
|
|
54
|
+
/** OPTIONAL. Orientation optimized for the template. */
|
|
55
|
+
orientation: OrientationSchema.optional(),
|
|
56
|
+
/** OPTIONAL. Color scheme optimized for the template. */
|
|
57
|
+
color_scheme: ColorSchemeSchema.optional(),
|
|
58
|
+
/** OPTIONAL. Contrast level optimized for the template. */
|
|
59
|
+
contrast: ContrastSchema.optional(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export type SvgTemplateProperties = z.infer<typeof SvgTemplatePropertiesSchema>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* SVG rendering metadata containing URI and optional integrity and properties.
|
|
66
|
+
*/
|
|
67
|
+
export const SvgTemplateRenderingSchema = z.looseObject({
|
|
68
|
+
/** REQUIRED. A URI pointing to the SVG template. */
|
|
69
|
+
uri: z.string(),
|
|
70
|
+
/** OPTIONAL. An "integrity metadata" string as described in Section 7. */
|
|
71
|
+
'uri#integrity': z.string().optional(),
|
|
72
|
+
/** REQUIRED if more than one SVG template is present. */
|
|
73
|
+
properties: SvgTemplatePropertiesSchema.optional(),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export type SvgTemplateRendering = z.infer<typeof SvgTemplateRenderingSchema>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Rendering metadata, either simple or SVG-based, for a credential.
|
|
80
|
+
*/
|
|
81
|
+
export const RenderingSchema = z
|
|
82
|
+
.looseObject({
|
|
83
|
+
/** OPTIONAL. Simple rendering metadata. */
|
|
84
|
+
simple: SimpleRenderingSchema.optional(),
|
|
85
|
+
/** OPTIONAL. Array of SVG template rendering objects. */
|
|
86
|
+
svg_templates: z.array(SvgTemplateRenderingSchema).optional(),
|
|
87
|
+
/**
|
|
88
|
+
* OPTIONAL. Array of SVG template rendering objects.
|
|
89
|
+
* @deprecated use `svg_templates` (plural) instead.
|
|
90
|
+
*/
|
|
91
|
+
svg_template: z.array(SvgTemplateRenderingSchema).optional(),
|
|
92
|
+
})
|
|
93
|
+
.transform(({ svg_template, svg_templates, ...rest }) => ({
|
|
94
|
+
...rest,
|
|
95
|
+
svg_templates: svg_templates ?? svg_template,
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
export type Rendering = z.infer<typeof RenderingSchema>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Display metadata associated with a credential type.
|
|
102
|
+
*/
|
|
103
|
+
export const DisplaySchema = z
|
|
104
|
+
.looseObject({
|
|
105
|
+
/**
|
|
106
|
+
* Language tag according to RFC 5646 (e.g., "en", "de").
|
|
107
|
+
* @deprecated - use `locale` instead
|
|
108
|
+
*/
|
|
109
|
+
lang: z.string().optional(),
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* REQUIRED (preferred). Language tag according to RFC 5646.
|
|
113
|
+
* Alias for `lang` - either `lang` or `locale` must be provided.
|
|
114
|
+
*/
|
|
115
|
+
locale: z.string().optional(),
|
|
116
|
+
|
|
117
|
+
/** REQUIRED. Human-readable name for the credential type. */
|
|
118
|
+
name: z.string(),
|
|
119
|
+
/** OPTIONAL. Description of the credential type for end users. */
|
|
120
|
+
description: z.string().optional(),
|
|
121
|
+
/** OPTIONAL. Rendering information (simple or SVG) for the credential. */
|
|
122
|
+
rendering: RenderingSchema.optional(),
|
|
123
|
+
})
|
|
124
|
+
.transform(({ lang, locale, ...rest }) => ({
|
|
125
|
+
...rest,
|
|
126
|
+
locale: locale ?? lang,
|
|
127
|
+
}))
|
|
128
|
+
.refine(({ locale }) => locale !== undefined, {
|
|
129
|
+
message:
|
|
130
|
+
'Either locale (preferred) or lang (spec name, deprecated) MUST be defined on claim display entry.',
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export type Display = z.infer<typeof DisplaySchema>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Claim path within the credential's JSON structure.
|
|
137
|
+
* Example: ["address", "street_address"]
|
|
138
|
+
*/
|
|
139
|
+
export const ClaimPathSchema = z.array(z.string().nullable());
|
|
140
|
+
|
|
141
|
+
export type ClaimPath = z.infer<typeof ClaimPathSchema>;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Display metadata for a specific claim.
|
|
145
|
+
*/
|
|
146
|
+
export const ClaimDisplaySchema = z
|
|
147
|
+
.looseObject({
|
|
148
|
+
/**
|
|
149
|
+
* Language tag according to RFC 5646.
|
|
150
|
+
* @deprecated - use `locale` instead
|
|
151
|
+
*/
|
|
152
|
+
lang: z.string().optional(),
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* REQUIRED (preferred). Language tag according to RFC 5646.
|
|
156
|
+
* Alias for `lang` - either `lang` or `locale` must be provided.
|
|
157
|
+
*/
|
|
158
|
+
locale: z.string().optional(),
|
|
159
|
+
|
|
160
|
+
/** REQUIRED. Human-readable label for the claim. */
|
|
161
|
+
label: z.string().optional(),
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Human-readable label for the claim.
|
|
165
|
+
* @deprecated - use `label` instead
|
|
166
|
+
*/
|
|
167
|
+
name: z.string().optional(),
|
|
168
|
+
|
|
169
|
+
/** OPTIONAL. Description of the claim for end users. */
|
|
170
|
+
description: z.string().optional(),
|
|
171
|
+
})
|
|
172
|
+
.transform(({ lang, name, label, locale, ...rest }) => ({
|
|
173
|
+
...rest,
|
|
174
|
+
locale: locale ?? lang,
|
|
175
|
+
label: label ?? name,
|
|
176
|
+
}))
|
|
177
|
+
.refine(({ locale }) => locale !== undefined, {
|
|
178
|
+
message:
|
|
179
|
+
'Either locale (preferred) or lang (deprecated) MUST be defined on claim display entry.',
|
|
180
|
+
})
|
|
181
|
+
.refine(({ label }) => label !== undefined, {
|
|
182
|
+
message:
|
|
183
|
+
'Either label (preferred) or name (deprecated) MUST be defined on claim display entry.',
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
export type ClaimDisplay = z.infer<typeof ClaimDisplaySchema>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Indicates whether a claim is selectively disclosable.
|
|
190
|
+
*/
|
|
191
|
+
export const ClaimSelectiveDisclosureSchema = z.enum([
|
|
192
|
+
'always',
|
|
193
|
+
'allowed',
|
|
194
|
+
'never',
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
export type ClaimSelectiveDisclosure = z.infer<
|
|
198
|
+
typeof ClaimSelectiveDisclosureSchema
|
|
199
|
+
>;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Metadata for individual claims in the credential type.
|
|
203
|
+
*/
|
|
204
|
+
export const ClaimSchema = z.looseObject({
|
|
205
|
+
/**
|
|
206
|
+
* REQUIRED. Array of one or more paths to the claim in the credential subject.
|
|
207
|
+
* Each path is an array of strings (or null for array elements).
|
|
208
|
+
*/
|
|
209
|
+
path: ClaimPathSchema,
|
|
210
|
+
/** OPTIONAL. Display metadata in multiple languages. */
|
|
211
|
+
display: z.array(ClaimDisplaySchema).optional(),
|
|
212
|
+
/** OPTIONAL. Controls whether the claim must, may, or must not be selectively disclosed. */
|
|
213
|
+
sd: ClaimSelectiveDisclosureSchema.optional(),
|
|
214
|
+
/**
|
|
215
|
+
* OPTIONAL. A boolean indicating whether the claim must be present in the Unsecured Payload
|
|
216
|
+
* of the SD-JWT VC. Default is false if not specified.
|
|
217
|
+
*/
|
|
218
|
+
mandatory: z.boolean().optional(),
|
|
219
|
+
/**
|
|
220
|
+
* OPTIONAL. Unique string identifier for referencing the claim in an SVG template.
|
|
221
|
+
* Must consist of alphanumeric characters or underscores and must not start with a digit.
|
|
222
|
+
*/
|
|
223
|
+
svg_id: z.string().optional(),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
export type Claim = z.infer<typeof ClaimSchema>;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Type metadata for a specific Verifiable Credential (VC) type.
|
|
230
|
+
* Reference: https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-13.html#name-type-metadata-format
|
|
231
|
+
*/
|
|
232
|
+
export const TypeMetadataFormatSchema = z.looseObject({
|
|
233
|
+
/** REQUIRED. A URI uniquely identifying the credential type. */
|
|
234
|
+
vct: z.string(),
|
|
235
|
+
/** OPTIONAL. Human-readable name for developers. */
|
|
236
|
+
name: z.string().optional(),
|
|
237
|
+
/** OPTIONAL. Human-readable description for developers. */
|
|
238
|
+
description: z.string().optional(),
|
|
239
|
+
/** OPTIONAL. URI of another type that this one extends. */
|
|
240
|
+
extends: z.string().optional(),
|
|
241
|
+
/** OPTIONAL. Integrity metadata for the 'extends' field. */
|
|
242
|
+
'extends#integrity': z.string().optional(),
|
|
243
|
+
/** OPTIONAL. Array of localized display metadata for the type. */
|
|
244
|
+
display: z.array(DisplaySchema).optional(),
|
|
245
|
+
/** OPTIONAL. Array of claim metadata. */
|
|
246
|
+
claims: z.array(ClaimSchema).optional(),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The resolved type metadata. If you just want to use the type metadata, you should use `typeMetadata`.
|
|
251
|
+
* In case additional processing is needed (e.g. for extensions in type metadata), you can use the `typeMetadataChain`
|
|
252
|
+
*/
|
|
253
|
+
export type ResolvedTypeMetadata = {
|
|
254
|
+
/**
|
|
255
|
+
* The merged type metadata based on the resolved `vct` document and all `extends` values.
|
|
256
|
+
*/
|
|
257
|
+
mergedTypeMetadata: TypeMetadataFormat;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The original type metadata documents, ordered from the extending type to the last extended type.
|
|
261
|
+
*/
|
|
262
|
+
typeMetadataChain: [TypeMetadataFormat, ...TypeMetadataFormat[]];
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The vct values present in the type metadata chain. This can be used for matching against e.g.
|
|
266
|
+
* DCQL queries which can query an underlying type.
|
|
267
|
+
*/
|
|
268
|
+
vctValues: [string, ...string[]];
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export type TypeMetadataFormat = z.infer<typeof TypeMetadataFormatSchema>;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import Crypto from 'node:crypto';
|
|
2
|
+
import { hasher as digest, generateSalt } from '@owf/crypto';
|
|
3
|
+
import {
|
|
4
|
+
createHeaderAndPayload,
|
|
5
|
+
StatusList,
|
|
6
|
+
type StatusListJWTHeaderParameters,
|
|
7
|
+
} from '@owf/token-status-list';
|
|
8
|
+
import type {
|
|
9
|
+
DisclosureFrame,
|
|
10
|
+
JwtPayload,
|
|
11
|
+
Signer,
|
|
12
|
+
Verifier,
|
|
13
|
+
} from '@sd-jwt/core';
|
|
14
|
+
import { SignJWT } from 'jose';
|
|
15
|
+
import { describe, expect, test } from 'vitest';
|
|
16
|
+
import { SDJwtVcInstance } from '..';
|
|
17
|
+
import type { SdJwtVcPayload } from '../sd-jwt-vc-payload';
|
|
18
|
+
|
|
19
|
+
const iss = 'ExampleIssuer';
|
|
20
|
+
const vct = 'ExampleCredentialType';
|
|
21
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
22
|
+
|
|
23
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
24
|
+
|
|
25
|
+
//create a separate keypair for the status list
|
|
26
|
+
const { privateKey: statusListPrivateKey, publicKey: statusListPublicKey } =
|
|
27
|
+
Crypto.generateKeyPairSync('ed25519');
|
|
28
|
+
|
|
29
|
+
//TODO: to simulate a hosted status list, use the same appraoch as in vct.spec.ts
|
|
30
|
+
|
|
31
|
+
const createSignerVerifier = () => {
|
|
32
|
+
const signer: Signer = async (data: string) => {
|
|
33
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
34
|
+
return Buffer.from(sig).toString('base64url');
|
|
35
|
+
};
|
|
36
|
+
const verifier: Verifier = async (data: string, sig: string) => {
|
|
37
|
+
return Crypto.verify(
|
|
38
|
+
null,
|
|
39
|
+
Buffer.from(data),
|
|
40
|
+
publicKey,
|
|
41
|
+
Buffer.from(sig, 'base64url'),
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
return { signer, verifier };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const generateStatusList = async (): Promise<string> => {
|
|
48
|
+
const statusList = new StatusList([0, 1, 0, 0, 0, 0, 1, 1], 1);
|
|
49
|
+
const payload: JwtPayload = {
|
|
50
|
+
iss: 'https://example.com',
|
|
51
|
+
sub: 'https://example.com/status/1',
|
|
52
|
+
iat: Math.floor(Date.now() / 1000),
|
|
53
|
+
};
|
|
54
|
+
const header: StatusListJWTHeaderParameters = {
|
|
55
|
+
alg: 'EdDSA',
|
|
56
|
+
typ: 'statuslist+jwt',
|
|
57
|
+
};
|
|
58
|
+
const values = createHeaderAndPayload(statusList, payload, header);
|
|
59
|
+
return new SignJWT(values.payload)
|
|
60
|
+
.setProtectedHeader(values.header)
|
|
61
|
+
.sign(statusListPrivateKey);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const statusListJWT = await generateStatusList();
|
|
65
|
+
|
|
66
|
+
describe('App', () => {
|
|
67
|
+
test('Example', async () => {
|
|
68
|
+
const { signer, verifier } = createSignerVerifier();
|
|
69
|
+
const sdjwt = new SDJwtVcInstance({
|
|
70
|
+
signer,
|
|
71
|
+
signAlg: 'EdDSA',
|
|
72
|
+
verifier,
|
|
73
|
+
hasher: digest,
|
|
74
|
+
hashAlg: 'sha-256',
|
|
75
|
+
saltGenerator: generateSalt,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const claims = {
|
|
79
|
+
firstname: 'John',
|
|
80
|
+
};
|
|
81
|
+
const disclosureFrame = {
|
|
82
|
+
_sd: ['firstname', 'iss'],
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const expectedPayload: SdJwtVcPayload = { iat, iss, vct, ...claims };
|
|
86
|
+
const encodedSdjwt = sdjwt.issue(
|
|
87
|
+
expectedPayload,
|
|
88
|
+
disclosureFrame as unknown as DisclosureFrame<SdJwtVcPayload>,
|
|
89
|
+
);
|
|
90
|
+
await expect(encodedSdjwt).rejects.toThrowError();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('Revocation', () => {
|
|
95
|
+
const { signer, verifier } = createSignerVerifier();
|
|
96
|
+
const sdjwt = new SDJwtVcInstance({
|
|
97
|
+
signer,
|
|
98
|
+
signAlg: 'EdDSA',
|
|
99
|
+
verifier,
|
|
100
|
+
hasher: digest,
|
|
101
|
+
hashAlg: 'sha-256',
|
|
102
|
+
saltGenerator: generateSalt,
|
|
103
|
+
statusListFetcher(_uri: string) {
|
|
104
|
+
// we emulate fetching the status list from the uri. Validation of the JWT is not done here in the test but should be done in the implementation.
|
|
105
|
+
return Promise.resolve(statusListJWT);
|
|
106
|
+
},
|
|
107
|
+
// statusValidator(status: number) {
|
|
108
|
+
// // we are only accepting status 0
|
|
109
|
+
// if (status === 0) return Promise.resolve();
|
|
110
|
+
// throw new Error('Status is not valid');
|
|
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
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('Test with a non revoked credential', async () => {
|
|
124
|
+
const claims = {
|
|
125
|
+
firstname: 'John',
|
|
126
|
+
status: {
|
|
127
|
+
status_list: {
|
|
128
|
+
uri: 'https://example.com/status-list',
|
|
129
|
+
idx: 0,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
const expectedPayload: SdJwtVcPayload = { iat, iss, vct, ...claims };
|
|
134
|
+
const encodedSdjwt = await sdjwt.issue(expectedPayload);
|
|
135
|
+
const result = await sdjwt.verify(encodedSdjwt);
|
|
136
|
+
expect(result).toBeDefined();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('Test with a revoked credential', async () => {
|
|
140
|
+
const claims = {
|
|
141
|
+
firstname: 'John',
|
|
142
|
+
status: {
|
|
143
|
+
status_list: {
|
|
144
|
+
uri: 'https://example.com/status-list',
|
|
145
|
+
idx: 1,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
const expectedPayload: SdJwtVcPayload = { iat, iss, vct, ...claims };
|
|
150
|
+
const encodedSdjwt = await sdjwt.issue(expectedPayload);
|
|
151
|
+
const result = sdjwt.verify(encodedSdjwt);
|
|
152
|
+
await expect(result).rejects.toThrowError('Status is not valid');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('Test with a revoked credential but status verification disabled', async () => {
|
|
156
|
+
const claims = {
|
|
157
|
+
firstname: 'John',
|
|
158
|
+
status: {
|
|
159
|
+
status_list: {
|
|
160
|
+
uri: 'https://example.com/status-list',
|
|
161
|
+
idx: 1,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
const expectedPayload: SdJwtVcPayload = { iat, iss, vct, ...claims };
|
|
166
|
+
const encodedSdjwt = await sdjwt.issue(expectedPayload);
|
|
167
|
+
const result = await sdjwt.verify(encodedSdjwt, {
|
|
168
|
+
disableStatusVerification: true,
|
|
169
|
+
});
|
|
170
|
+
expect(result).toBeDefined();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('test to fetch the statuslist', async () => {
|
|
174
|
+
//TODO: not implemented yet since we need to either mock the fetcher or use a real fetcher
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('test with an expired status list', async () => {
|
|
178
|
+
//TODO: needs to be implemented
|
|
179
|
+
});
|
|
180
|
+
});
|