@openid4vc/openid4vci 0.3.0-alpha-20250224151429
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 +9 -0
- package/dist/index.d.mts +18898 -0
- package/dist/index.d.ts +18898 -0
- package/dist/index.js +2130 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2124 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2124 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { getGlobalConfig as getGlobalConfig2, setGlobalConfig } from "@openid4vc/utils";
|
|
3
|
+
|
|
4
|
+
// src/credential-request/credential-request-configurations.ts
|
|
5
|
+
import { arrayEqualsIgnoreOrder } from "@openid4vc/utils";
|
|
6
|
+
|
|
7
|
+
// src/metadata/credential-issuer/credential-issuer-metadata.ts
|
|
8
|
+
import { Oauth2Error, fetchWellKnownMetadata } from "@openid4vc/oauth2";
|
|
9
|
+
import { joinUriParts } from "@openid4vc/utils";
|
|
10
|
+
|
|
11
|
+
// src/metadata/credential-issuer/z-credential-issuer-metadata.ts
|
|
12
|
+
import { zCompactJwt } from "@openid4vc/oauth2";
|
|
13
|
+
import { zHttpsUrl } from "@openid4vc/utils";
|
|
14
|
+
import z9 from "zod";
|
|
15
|
+
|
|
16
|
+
// src/formats/credential/mso-mdoc/z-mso-mdoc.ts
|
|
17
|
+
import z3 from "zod";
|
|
18
|
+
|
|
19
|
+
// src/metadata/credential-issuer/z-credential-configuration-supported-common.ts
|
|
20
|
+
import z2 from "zod";
|
|
21
|
+
|
|
22
|
+
// src/key-attestation/z-key-attestation.ts
|
|
23
|
+
import { zJwk, zJwtHeader, zJwtPayload } from "@openid4vc/oauth2";
|
|
24
|
+
import { zInteger } from "@openid4vc/utils";
|
|
25
|
+
import z from "zod";
|
|
26
|
+
var zKeyAttestationJwtHeader = z.object({
|
|
27
|
+
...zJwtHeader.shape,
|
|
28
|
+
typ: z.literal("keyattestation+jwt")
|
|
29
|
+
}).passthrough().refine(({ kid, jwk }) => jwk === void 0 || kid === void 0, {
|
|
30
|
+
message: `Both 'jwk' and 'kid' are defined. Only one is allowed`
|
|
31
|
+
}).refine(({ trust_chain, kid }) => !trust_chain || !kid, {
|
|
32
|
+
message: `When 'trust_chain' is provided, 'kid' is required`
|
|
33
|
+
});
|
|
34
|
+
var zIso18045 = z.enum(["iso_18045_high", "iso_18045_moderate", "iso_18045_enhanced-basic", "iso_18045_basic"]);
|
|
35
|
+
var zIso18045OrStringArray = z.array(z.union([zIso18045, z.string()]));
|
|
36
|
+
var zKeyAttestationJwtPayload = z.object({
|
|
37
|
+
...zJwtPayload.shape,
|
|
38
|
+
iat: zInteger,
|
|
39
|
+
attested_keys: z.array(zJwk),
|
|
40
|
+
key_storage: z.optional(zIso18045OrStringArray),
|
|
41
|
+
user_authentication: z.optional(zIso18045OrStringArray),
|
|
42
|
+
certification: z.optional(z.string())
|
|
43
|
+
}).passthrough();
|
|
44
|
+
var zKeyAttestationJwtPayloadForUse = (use) => z.object({
|
|
45
|
+
...zKeyAttestationJwtPayload.shape,
|
|
46
|
+
// REQUIRED when used as proof_type.attesation directly
|
|
47
|
+
nonce: use === "proof_type.attestation" ? z.string({
|
|
48
|
+
message: `Nonce must be defined when key attestation is used as 'proof_type.attestation' directly`
|
|
49
|
+
}) : z.optional(z.string()),
|
|
50
|
+
// REQUIRED when used within header of proof_type.jwt
|
|
51
|
+
exp: use === "proof_type.jwt" ? zInteger : z.optional(zInteger)
|
|
52
|
+
}).passthrough();
|
|
53
|
+
|
|
54
|
+
// src/metadata/credential-issuer/z-credential-configuration-supported-common.ts
|
|
55
|
+
var zCredentialConfigurationSupportedClaims = z2.object({
|
|
56
|
+
mandatory: z2.boolean().optional(),
|
|
57
|
+
value_type: z2.string().optional(),
|
|
58
|
+
display: z2.object({
|
|
59
|
+
name: z2.string().optional(),
|
|
60
|
+
locale: z2.string().optional()
|
|
61
|
+
}).passthrough().optional()
|
|
62
|
+
}).passthrough();
|
|
63
|
+
var zCredentialConfigurationSupportedCommon = z2.object({
|
|
64
|
+
format: z2.string(),
|
|
65
|
+
scope: z2.string().optional(),
|
|
66
|
+
cryptographic_binding_methods_supported: z2.array(z2.string()).optional(),
|
|
67
|
+
credential_signing_alg_values_supported: z2.array(z2.string()).optional(),
|
|
68
|
+
proof_types_supported: z2.record(
|
|
69
|
+
z2.union([z2.literal("jwt"), z2.literal("attestation"), z2.string()]),
|
|
70
|
+
z2.object({
|
|
71
|
+
proof_signing_alg_values_supported: z2.array(z2.string()),
|
|
72
|
+
key_attestations_required: z2.object({
|
|
73
|
+
key_storage: zIso18045OrStringArray.optional(),
|
|
74
|
+
user_authentication: zIso18045OrStringArray.optional()
|
|
75
|
+
}).passthrough().optional()
|
|
76
|
+
})
|
|
77
|
+
).optional(),
|
|
78
|
+
display: z2.array(
|
|
79
|
+
z2.object({
|
|
80
|
+
name: z2.string(),
|
|
81
|
+
locale: z2.string().optional(),
|
|
82
|
+
logo: z2.object({
|
|
83
|
+
// FIXME: make required again, but need to support draft 11 first
|
|
84
|
+
uri: z2.string().optional(),
|
|
85
|
+
alt_text: z2.string().optional()
|
|
86
|
+
}).passthrough().optional(),
|
|
87
|
+
description: z2.string().optional(),
|
|
88
|
+
background_color: z2.string().optional(),
|
|
89
|
+
background_image: z2.object({
|
|
90
|
+
// TODO: should be required, but paradym's metadata is wrong here.
|
|
91
|
+
uri: z2.string().optional()
|
|
92
|
+
}).passthrough().optional(),
|
|
93
|
+
text_color: z2.string().optional()
|
|
94
|
+
}).passthrough()
|
|
95
|
+
).optional()
|
|
96
|
+
}).passthrough();
|
|
97
|
+
|
|
98
|
+
// src/formats/credential/mso-mdoc/z-mso-mdoc.ts
|
|
99
|
+
var zMsoMdocFormatIdentifier = z3.literal("mso_mdoc");
|
|
100
|
+
var zMsoMdocCredentialIssuerMetadata = z3.object({
|
|
101
|
+
format: zMsoMdocFormatIdentifier,
|
|
102
|
+
doctype: z3.string(),
|
|
103
|
+
claims: z3.optional(zCredentialConfigurationSupportedClaims),
|
|
104
|
+
order: z3.optional(z3.array(z3.string()))
|
|
105
|
+
});
|
|
106
|
+
var zMsoMdocCredentialRequestFormat = z3.object({
|
|
107
|
+
format: zMsoMdocFormatIdentifier,
|
|
108
|
+
doctype: z3.string(),
|
|
109
|
+
claims: z3.optional(zCredentialConfigurationSupportedClaims)
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// src/formats/credential/sd-jwt-vc/z-sd-jwt-vc.ts
|
|
113
|
+
import z4 from "zod";
|
|
114
|
+
var zSdJwtVcFormatIdentifier = z4.literal("vc+sd-jwt");
|
|
115
|
+
var zSdJwtVcCredentialIssuerMetadata = z4.object({
|
|
116
|
+
vct: z4.string(),
|
|
117
|
+
format: zSdJwtVcFormatIdentifier,
|
|
118
|
+
claims: z4.optional(zCredentialConfigurationSupportedClaims),
|
|
119
|
+
order: z4.optional(z4.array(z4.string()))
|
|
120
|
+
});
|
|
121
|
+
var zSdJwtVcCredentialRequestFormat = z4.object({
|
|
122
|
+
format: zSdJwtVcFormatIdentifier,
|
|
123
|
+
vct: z4.string(),
|
|
124
|
+
claims: z4.optional(zCredentialConfigurationSupportedClaims)
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// src/formats/credential/w3c-vc/z-w3c-ldp-vc.ts
|
|
128
|
+
import z6 from "zod";
|
|
129
|
+
|
|
130
|
+
// src/formats/credential/w3c-vc/z-w3c-vc-common.ts
|
|
131
|
+
import z5 from "zod";
|
|
132
|
+
var zCredentialSubjectLeafType = z5.object({
|
|
133
|
+
mandatory: z5.boolean().optional(),
|
|
134
|
+
value_type: z5.string().optional(),
|
|
135
|
+
display: z5.array(
|
|
136
|
+
z5.object({
|
|
137
|
+
name: z5.string().optional(),
|
|
138
|
+
locale: z5.string().optional()
|
|
139
|
+
}).passthrough()
|
|
140
|
+
).optional()
|
|
141
|
+
}).passthrough();
|
|
142
|
+
var zClaimValueSchema = z5.union([z5.array(z5.any()), z5.record(z5.string(), z5.any()), zCredentialSubjectLeafType]);
|
|
143
|
+
var zW3cVcCredentialSubject = z5.record(z5.string(), zClaimValueSchema);
|
|
144
|
+
var zW3cVcJsonLdCredentialDefinition = z5.object({
|
|
145
|
+
"@context": z5.array(z5.string()),
|
|
146
|
+
type: z5.array(z5.string()),
|
|
147
|
+
credentialSubject: zW3cVcCredentialSubject.optional()
|
|
148
|
+
}).passthrough();
|
|
149
|
+
|
|
150
|
+
// src/formats/credential/w3c-vc/z-w3c-ldp-vc.ts
|
|
151
|
+
var zLdpVcFormatIdentifier = z6.literal("ldp_vc");
|
|
152
|
+
var zLdpVcCredentialIssuerMetadata = z6.object({
|
|
153
|
+
format: zLdpVcFormatIdentifier,
|
|
154
|
+
credential_definition: zW3cVcJsonLdCredentialDefinition,
|
|
155
|
+
order: z6.array(z6.string()).optional()
|
|
156
|
+
});
|
|
157
|
+
var zLdpVcCredentialIssuerMetadataDraft11 = z6.object({
|
|
158
|
+
order: z6.array(z6.string()).optional(),
|
|
159
|
+
format: zLdpVcFormatIdentifier,
|
|
160
|
+
// Credential definition was spread on top level instead of a separatey property in v11
|
|
161
|
+
// As well as using types instead of type
|
|
162
|
+
"@context": z6.array(z6.string()),
|
|
163
|
+
types: z6.array(z6.string()),
|
|
164
|
+
credentialSubject: zW3cVcCredentialSubject.optional()
|
|
165
|
+
}).passthrough();
|
|
166
|
+
var zLdpVcCredentialIssuerMetadataDraft11To14 = zLdpVcCredentialIssuerMetadataDraft11.transform(
|
|
167
|
+
({ "@context": context, types, credentialSubject, ...rest }) => ({
|
|
168
|
+
...rest,
|
|
169
|
+
credential_definition: {
|
|
170
|
+
"@context": context,
|
|
171
|
+
type: types,
|
|
172
|
+
// Prevent weird typing issue with optional vs undefined
|
|
173
|
+
...credentialSubject ? { credentialSubject } : {}
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
var zLdpVcCredentialIssuerMetadataDraft14To11 = zLdpVcCredentialIssuerMetadata.passthrough().transform(({ credential_definition: { type, ...credentialDefinition }, ...rest }) => ({
|
|
178
|
+
...rest,
|
|
179
|
+
...credentialDefinition,
|
|
180
|
+
types: type
|
|
181
|
+
})).and(zLdpVcCredentialIssuerMetadataDraft11);
|
|
182
|
+
var zLdpVcCredentialRequestFormat = z6.object({
|
|
183
|
+
format: zLdpVcFormatIdentifier,
|
|
184
|
+
credential_definition: zW3cVcJsonLdCredentialDefinition
|
|
185
|
+
});
|
|
186
|
+
var zLdpVcCredentialRequestDraft11 = z6.object({
|
|
187
|
+
format: zLdpVcFormatIdentifier,
|
|
188
|
+
credential_definition: z6.object({
|
|
189
|
+
"@context": z6.array(z6.string()),
|
|
190
|
+
// credential_definition was using types instead of type in v11
|
|
191
|
+
types: z6.array(z6.string()),
|
|
192
|
+
credentialSubject: zW3cVcCredentialSubject.optional()
|
|
193
|
+
})
|
|
194
|
+
}).passthrough();
|
|
195
|
+
var zLdpVcCredentialRequestDraft11To14 = zLdpVcCredentialRequestDraft11.transform(
|
|
196
|
+
({ credential_definition: { types, ...restCredentialDefinition }, ...rest }) => ({
|
|
197
|
+
...rest,
|
|
198
|
+
credential_definition: {
|
|
199
|
+
...restCredentialDefinition,
|
|
200
|
+
type: types
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
var zLdpVcCredentialRequestDraft14To11 = zLdpVcCredentialRequestFormat.passthrough().transform(({ credential_definition: { type, ...restCredentialDefinition }, ...rest }) => ({
|
|
205
|
+
...rest,
|
|
206
|
+
credential_definition: {
|
|
207
|
+
...restCredentialDefinition,
|
|
208
|
+
types: type
|
|
209
|
+
}
|
|
210
|
+
})).and(zLdpVcCredentialRequestDraft11);
|
|
211
|
+
|
|
212
|
+
// src/formats/credential/w3c-vc/z-w3c-jwt-vc-json-ld.ts
|
|
213
|
+
import z7 from "zod";
|
|
214
|
+
var zJwtVcJsonLdFormatIdentifier = z7.literal("jwt_vc_json-ld");
|
|
215
|
+
var zJwtVcJsonLdCredentialIssuerMetadata = z7.object({
|
|
216
|
+
format: zJwtVcJsonLdFormatIdentifier,
|
|
217
|
+
credential_definition: zW3cVcJsonLdCredentialDefinition,
|
|
218
|
+
order: z7.optional(z7.array(z7.string()))
|
|
219
|
+
});
|
|
220
|
+
var zJwtVcJsonLdCredentialIssuerMetadataDraft11 = z7.object({
|
|
221
|
+
order: z7.array(z7.string()).optional(),
|
|
222
|
+
format: zJwtVcJsonLdFormatIdentifier,
|
|
223
|
+
// Credential definition was spread on top level instead of a separatey property in v11
|
|
224
|
+
// As well as using types instead of type
|
|
225
|
+
"@context": z7.array(z7.string()),
|
|
226
|
+
types: z7.array(z7.string()),
|
|
227
|
+
credentialSubject: zW3cVcCredentialSubject.optional()
|
|
228
|
+
}).passthrough();
|
|
229
|
+
var zJwtVcJsonLdCredentialIssuerMetadataDraft11To14 = zJwtVcJsonLdCredentialIssuerMetadataDraft11.transform(
|
|
230
|
+
({ "@context": context, types, credentialSubject, ...rest }) => ({
|
|
231
|
+
...rest,
|
|
232
|
+
credential_definition: {
|
|
233
|
+
"@context": context,
|
|
234
|
+
type: types,
|
|
235
|
+
// Prevent weird typing issue with optional vs undefined
|
|
236
|
+
...credentialSubject ? { credentialSubject } : {}
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
var zJwtVcJsonLdCredentialIssuerMetadataDraft14To11 = zJwtVcJsonLdCredentialIssuerMetadata.passthrough().transform(({ credential_definition: { type, ...credentialDefinition }, ...rest }) => ({
|
|
241
|
+
...rest,
|
|
242
|
+
...credentialDefinition,
|
|
243
|
+
types: type
|
|
244
|
+
})).and(zJwtVcJsonLdCredentialIssuerMetadataDraft11);
|
|
245
|
+
var zJwtVcJsonLdCredentialRequestFormat = z7.object({
|
|
246
|
+
format: zJwtVcJsonLdFormatIdentifier,
|
|
247
|
+
credential_definition: zW3cVcJsonLdCredentialDefinition
|
|
248
|
+
});
|
|
249
|
+
var zJwtVcJsonLdCredentialRequestDraft11 = z7.object({
|
|
250
|
+
format: zJwtVcJsonLdFormatIdentifier,
|
|
251
|
+
credential_definition: z7.object({
|
|
252
|
+
"@context": z7.array(z7.string()),
|
|
253
|
+
// credential_definition was using types instead of type in v11
|
|
254
|
+
types: z7.array(z7.string()),
|
|
255
|
+
credentialSubject: z7.optional(zW3cVcCredentialSubject)
|
|
256
|
+
}).passthrough()
|
|
257
|
+
}).passthrough();
|
|
258
|
+
var zJwtVcJsonLdCredentialRequestDraft11To14 = zJwtVcJsonLdCredentialRequestDraft11.transform(
|
|
259
|
+
({ credential_definition: { types, ...restCredentialDefinition }, ...rest }) => ({
|
|
260
|
+
...rest,
|
|
261
|
+
credential_definition: {
|
|
262
|
+
...restCredentialDefinition,
|
|
263
|
+
type: types
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
);
|
|
267
|
+
var zJwtVcJsonLdCredentialRequestDraft14To11 = zJwtVcJsonLdCredentialRequestFormat.passthrough().transform(({ credential_definition: { type, ...restCredentialDefinition }, ...rest }) => ({
|
|
268
|
+
...rest,
|
|
269
|
+
credential_definition: {
|
|
270
|
+
...restCredentialDefinition,
|
|
271
|
+
types: type
|
|
272
|
+
}
|
|
273
|
+
})).and(zJwtVcJsonLdCredentialRequestDraft11);
|
|
274
|
+
|
|
275
|
+
// src/formats/credential/w3c-vc/z-w3c-jwt-vc-json.ts
|
|
276
|
+
import z8 from "zod";
|
|
277
|
+
var zJwtVcJsonFormatIdentifier = z8.literal("jwt_vc_json");
|
|
278
|
+
var zJwtVcJsonCredentialDefinition = z8.object({
|
|
279
|
+
type: z8.array(z8.string()),
|
|
280
|
+
credentialSubject: zW3cVcCredentialSubject.optional()
|
|
281
|
+
}).passthrough();
|
|
282
|
+
var zJwtVcJsonCredentialIssuerMetadata = z8.object({
|
|
283
|
+
format: zJwtVcJsonFormatIdentifier,
|
|
284
|
+
credential_definition: zJwtVcJsonCredentialDefinition,
|
|
285
|
+
order: z8.array(z8.string()).optional()
|
|
286
|
+
});
|
|
287
|
+
var zJwtVcJsonCredentialIssuerMetadataDraft11 = z8.object({
|
|
288
|
+
format: zJwtVcJsonFormatIdentifier,
|
|
289
|
+
order: z8.array(z8.string()).optional(),
|
|
290
|
+
// Credential definition was spread on top level instead of a separatey property in v11
|
|
291
|
+
// As well as using types instead of type
|
|
292
|
+
types: z8.array(z8.string()),
|
|
293
|
+
credentialSubject: zW3cVcCredentialSubject.optional()
|
|
294
|
+
}).passthrough();
|
|
295
|
+
var zJwtVcJsonCredentialIssuerMetadataDraft11To14 = zJwtVcJsonCredentialIssuerMetadataDraft11.transform(
|
|
296
|
+
({ types, credentialSubject, ...rest }) => ({
|
|
297
|
+
...rest,
|
|
298
|
+
credential_definition: {
|
|
299
|
+
type: types,
|
|
300
|
+
// Prevent weird typing issue with optional vs undefined
|
|
301
|
+
...credentialSubject ? { credentialSubject } : {}
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
var zJwtVcJsonCredentialIssuerMetadataDraft14To11 = zJwtVcJsonCredentialIssuerMetadata.passthrough().transform(({ credential_definition: { type, ...credentialDefinition }, ...rest }) => ({
|
|
306
|
+
...rest,
|
|
307
|
+
types: type,
|
|
308
|
+
...credentialDefinition
|
|
309
|
+
})).and(zJwtVcJsonCredentialIssuerMetadataDraft11);
|
|
310
|
+
var zJwtVcJsonCredentialRequestFormat = z8.object({
|
|
311
|
+
format: zJwtVcJsonFormatIdentifier,
|
|
312
|
+
credential_definition: zJwtVcJsonCredentialDefinition
|
|
313
|
+
});
|
|
314
|
+
var zJwtVcJsonCredentialRequestDraft11 = z8.object({
|
|
315
|
+
format: zJwtVcJsonFormatIdentifier,
|
|
316
|
+
// Credential definition was spread on top level instead of a separatey property in v11
|
|
317
|
+
// As well as using types instead of type
|
|
318
|
+
types: z8.array(z8.string()),
|
|
319
|
+
credentialSubject: z8.optional(zW3cVcCredentialSubject)
|
|
320
|
+
}).passthrough();
|
|
321
|
+
var zJwtVcJsonCredentialRequestDraft11To14 = zJwtVcJsonCredentialRequestDraft11.transform(
|
|
322
|
+
({ types, credentialSubject, ...rest }) => {
|
|
323
|
+
return {
|
|
324
|
+
...rest,
|
|
325
|
+
credential_definition: {
|
|
326
|
+
type: types,
|
|
327
|
+
// Prevent weird typing issue with optional vs undefined
|
|
328
|
+
...credentialSubject ? { credentialSubject } : {}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
var zJwtVcJsonCredentialRequestDraft14To11 = zJwtVcJsonCredentialRequestFormat.passthrough().transform(({ credential_definition: { type, ...credentialDefinition }, ...rest }) => ({
|
|
334
|
+
...rest,
|
|
335
|
+
types: type,
|
|
336
|
+
...credentialDefinition
|
|
337
|
+
})).and(zJwtVcJsonCredentialRequestDraft11);
|
|
338
|
+
|
|
339
|
+
// src/version.ts
|
|
340
|
+
var Openid4vciDraftVersion = /* @__PURE__ */ ((Openid4vciDraftVersion2) => {
|
|
341
|
+
Openid4vciDraftVersion2["Draft14"] = "Draft14";
|
|
342
|
+
Openid4vciDraftVersion2["Draft11"] = "Draft11";
|
|
343
|
+
return Openid4vciDraftVersion2;
|
|
344
|
+
})(Openid4vciDraftVersion || {});
|
|
345
|
+
|
|
346
|
+
// src/metadata/credential-issuer/z-credential-issuer-metadata.ts
|
|
347
|
+
var allCredentialIssuerMetadataFormats = [
|
|
348
|
+
zSdJwtVcCredentialIssuerMetadata,
|
|
349
|
+
zMsoMdocCredentialIssuerMetadata,
|
|
350
|
+
zJwtVcJsonLdCredentialIssuerMetadata,
|
|
351
|
+
zLdpVcCredentialIssuerMetadata,
|
|
352
|
+
zJwtVcJsonCredentialIssuerMetadata
|
|
353
|
+
];
|
|
354
|
+
var allCredentialIssuerMetadataFormatIdentifiers = allCredentialIssuerMetadataFormats.map(
|
|
355
|
+
(format) => format.shape.format.value
|
|
356
|
+
);
|
|
357
|
+
var zCredentialConfigurationSupportedWithFormats = zCredentialConfigurationSupportedCommon.transform(
|
|
358
|
+
(data, ctx) => {
|
|
359
|
+
if (!allCredentialIssuerMetadataFormatIdentifiers.includes(data.format)) return data;
|
|
360
|
+
const result = z9.object({}).passthrough().and(z9.discriminatedUnion("format", allCredentialIssuerMetadataFormats)).safeParse(data);
|
|
361
|
+
if (result.success) {
|
|
362
|
+
return result.data;
|
|
363
|
+
}
|
|
364
|
+
for (const issue of result.error.issues) {
|
|
365
|
+
ctx.addIssue(issue);
|
|
366
|
+
}
|
|
367
|
+
return z9.NEVER;
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
var zCredentialIssuerMetadataDisplayEntry = z9.object({
|
|
371
|
+
name: z9.string().optional(),
|
|
372
|
+
locale: z9.string().optional(),
|
|
373
|
+
logo: z9.object({
|
|
374
|
+
// FIXME: make required again, but need to support draft 11 first
|
|
375
|
+
uri: z9.string().optional(),
|
|
376
|
+
alt_text: z9.string().optional()
|
|
377
|
+
}).passthrough().optional()
|
|
378
|
+
}).passthrough();
|
|
379
|
+
var zCredentialIssuerMetadataDraft14 = z9.object({
|
|
380
|
+
credential_issuer: zHttpsUrl,
|
|
381
|
+
authorization_servers: z9.array(zHttpsUrl).optional(),
|
|
382
|
+
credential_endpoint: zHttpsUrl,
|
|
383
|
+
deferred_credential_endpoint: zHttpsUrl.optional(),
|
|
384
|
+
notification_endpoint: zHttpsUrl.optional(),
|
|
385
|
+
// Added after draft 14, but needed for proper
|
|
386
|
+
nonce_endpoint: zHttpsUrl.optional(),
|
|
387
|
+
credential_response_encryption: z9.object({
|
|
388
|
+
alg_values_supported: z9.array(z9.string()),
|
|
389
|
+
enc_values_supported: z9.array(z9.string()),
|
|
390
|
+
encryption_required: z9.boolean()
|
|
391
|
+
}).passthrough().optional(),
|
|
392
|
+
batch_credential_issuance: z9.object({
|
|
393
|
+
batch_size: z9.number().positive()
|
|
394
|
+
}).passthrough().optional(),
|
|
395
|
+
signed_metadata: zCompactJwt.optional(),
|
|
396
|
+
display: z9.array(zCredentialIssuerMetadataDisplayEntry).optional(),
|
|
397
|
+
credential_configurations_supported: z9.record(z9.string(), zCredentialConfigurationSupportedWithFormats)
|
|
398
|
+
}).passthrough();
|
|
399
|
+
var zCredentialConfigurationSupportedDraft11To14 = z9.object({
|
|
400
|
+
id: z9.string().optional(),
|
|
401
|
+
format: z9.string(),
|
|
402
|
+
cryptographic_suites_supported: z9.array(z9.string()).optional(),
|
|
403
|
+
display: z9.array(
|
|
404
|
+
z9.object({
|
|
405
|
+
logo: z9.object({
|
|
406
|
+
url: z9.string().url().optional()
|
|
407
|
+
}).passthrough().optional(),
|
|
408
|
+
background_image: z9.object({
|
|
409
|
+
url: z9.string().url().optional()
|
|
410
|
+
}).passthrough().optional()
|
|
411
|
+
}).passthrough()
|
|
412
|
+
).optional()
|
|
413
|
+
}).passthrough().transform(({ cryptographic_suites_supported, display, id, ...rest }) => ({
|
|
414
|
+
...rest,
|
|
415
|
+
...cryptographic_suites_supported ? { credential_signing_alg_values_supported: cryptographic_suites_supported } : {},
|
|
416
|
+
...display ? {
|
|
417
|
+
display: display.map(({ logo, background_image, ...displayRest }) => ({
|
|
418
|
+
...displayRest,
|
|
419
|
+
// url became uri and also required
|
|
420
|
+
// so if there's no url in the logo, we remove the whole logo object
|
|
421
|
+
...logo?.url ? {
|
|
422
|
+
// TODO: we should add the other params from logo as well
|
|
423
|
+
logo: {
|
|
424
|
+
uri: logo.url
|
|
425
|
+
}
|
|
426
|
+
} : {},
|
|
427
|
+
// TODO: we should add the other params from background_image as well
|
|
428
|
+
// url became uri and also required
|
|
429
|
+
// so if there's no url in the background_image, we remove the whole logo object
|
|
430
|
+
...background_image?.url ? {
|
|
431
|
+
background_image: {
|
|
432
|
+
uri: background_image.url
|
|
433
|
+
}
|
|
434
|
+
} : {}
|
|
435
|
+
}))
|
|
436
|
+
} : {}
|
|
437
|
+
})).transform((data, ctx) => {
|
|
438
|
+
const formatSpecificTransformations = {
|
|
439
|
+
[zLdpVcFormatIdentifier.value]: zLdpVcCredentialIssuerMetadataDraft11To14,
|
|
440
|
+
[zJwtVcJsonFormatIdentifier.value]: zJwtVcJsonCredentialIssuerMetadataDraft11To14,
|
|
441
|
+
[zJwtVcJsonLdFormatIdentifier.value]: zJwtVcJsonLdCredentialIssuerMetadataDraft11To14
|
|
442
|
+
};
|
|
443
|
+
if (!Object.keys(formatSpecificTransformations).includes(data.format)) return data;
|
|
444
|
+
const schema = formatSpecificTransformations[data.format];
|
|
445
|
+
const result = schema.safeParse(data);
|
|
446
|
+
if (result.success) return result.data;
|
|
447
|
+
for (const issue of result.error.issues) {
|
|
448
|
+
ctx.addIssue(issue);
|
|
449
|
+
}
|
|
450
|
+
return z9.NEVER;
|
|
451
|
+
}).pipe(zCredentialConfigurationSupportedWithFormats);
|
|
452
|
+
var zCredentialConfigurationSupportedDraft14To11 = zCredentialConfigurationSupportedWithFormats.and(
|
|
453
|
+
z9.object({
|
|
454
|
+
id: z9.string()
|
|
455
|
+
}).passthrough()
|
|
456
|
+
).transform(({ id, credential_signing_alg_values_supported, display, proof_types_supported, scope, ...rest }) => ({
|
|
457
|
+
...rest,
|
|
458
|
+
...credential_signing_alg_values_supported ? { cryptographic_suites_supported: credential_signing_alg_values_supported } : {},
|
|
459
|
+
...display ? {
|
|
460
|
+
display: display.map(({ logo, background_image, ...displayRest }) => {
|
|
461
|
+
const { uri: logoUri, ...logoRest } = logo ?? {};
|
|
462
|
+
const { uri: backgroundImageUri, ...backgroundImageRest } = background_image ?? {};
|
|
463
|
+
return {
|
|
464
|
+
...displayRest,
|
|
465
|
+
// draft 11 uses url, draft 13/14 uses uri
|
|
466
|
+
...logoUri ? { logo: { url: logoUri, ...logoRest } } : {},
|
|
467
|
+
// draft 11 uses url, draft 13/14 uses uri
|
|
468
|
+
...backgroundImageUri ? { logo: { url: backgroundImageUri, ...backgroundImageRest } } : {}
|
|
469
|
+
};
|
|
470
|
+
})
|
|
471
|
+
} : {},
|
|
472
|
+
id
|
|
473
|
+
})).pipe(
|
|
474
|
+
z9.union([
|
|
475
|
+
zLdpVcCredentialIssuerMetadataDraft14To11,
|
|
476
|
+
zJwtVcJsonCredentialIssuerMetadataDraft14To11,
|
|
477
|
+
zJwtVcJsonLdCredentialIssuerMetadataDraft14To11,
|
|
478
|
+
// To handle unrecognized formats and not error immediately we allow the common format as well
|
|
479
|
+
// but they can't use any of the foramt identifiers that have a specific transformation. This way if a format is
|
|
480
|
+
// has a transformation it NEEDS to use the format specific transformation, and otherwise we fall back to the common validation
|
|
481
|
+
z9.object({
|
|
482
|
+
format: z9.string().refine(
|
|
483
|
+
(input) => ![
|
|
484
|
+
zLdpVcFormatIdentifier.value,
|
|
485
|
+
zJwtVcJsonFormatIdentifier.value,
|
|
486
|
+
zJwtVcJsonLdFormatIdentifier.value
|
|
487
|
+
].includes(input)
|
|
488
|
+
)
|
|
489
|
+
}).passthrough()
|
|
490
|
+
])
|
|
491
|
+
);
|
|
492
|
+
var zCredentialIssuerMetadataDraft11To14 = z9.object({
|
|
493
|
+
authorization_server: z9.string().optional(),
|
|
494
|
+
credentials_supported: z9.array(
|
|
495
|
+
z9.object({
|
|
496
|
+
id: z9.string().optional()
|
|
497
|
+
}).passthrough()
|
|
498
|
+
)
|
|
499
|
+
}).passthrough().transform(({ authorization_server, credentials_supported, ...rest }) => {
|
|
500
|
+
return {
|
|
501
|
+
...rest,
|
|
502
|
+
...authorization_server ? { authorization_servers: [authorization_server] } : {},
|
|
503
|
+
// Go from array to map but keep v11 structure
|
|
504
|
+
credential_configurations_supported: Object.fromEntries(
|
|
505
|
+
credentials_supported.map((supported) => supported.id ? [supported.id, supported] : void 0).filter((i) => i !== void 0)
|
|
506
|
+
)
|
|
507
|
+
};
|
|
508
|
+
}).pipe(
|
|
509
|
+
z9.object({
|
|
510
|
+
// Update from v11 structrue to v14 structure
|
|
511
|
+
credential_configurations_supported: z9.record(z9.string(), zCredentialConfigurationSupportedDraft11To14)
|
|
512
|
+
}).passthrough()
|
|
513
|
+
).pipe(zCredentialIssuerMetadataDraft14);
|
|
514
|
+
var zCredentialIssuerMetadataWithDraft11 = zCredentialIssuerMetadataDraft14.transform((issuerMetadata) => ({
|
|
515
|
+
...issuerMetadata,
|
|
516
|
+
...issuerMetadata.authorization_servers ? { authorization_server: issuerMetadata.authorization_servers[0] } : {},
|
|
517
|
+
credentials_supported: Object.entries(issuerMetadata.credential_configurations_supported).map(([id, value]) => ({
|
|
518
|
+
...value,
|
|
519
|
+
id
|
|
520
|
+
}))
|
|
521
|
+
})).pipe(
|
|
522
|
+
zCredentialIssuerMetadataDraft14.extend({
|
|
523
|
+
credentials_supported: z9.array(zCredentialConfigurationSupportedDraft14To11)
|
|
524
|
+
})
|
|
525
|
+
);
|
|
526
|
+
var zCredentialIssuerMetadata = z9.union([
|
|
527
|
+
// First prioritize draft 14 (and 13)
|
|
528
|
+
zCredentialIssuerMetadataDraft14,
|
|
529
|
+
// Then try parsing draft 11 and transform into draft 14
|
|
530
|
+
zCredentialIssuerMetadataDraft11To14
|
|
531
|
+
]);
|
|
532
|
+
var zCredentialIssuerMetadataWithDraftVersion = z9.union([
|
|
533
|
+
// First prioritize draft 14 (and 13)
|
|
534
|
+
zCredentialIssuerMetadataDraft14.transform((credentialIssuerMetadata) => ({
|
|
535
|
+
credentialIssuerMetadata,
|
|
536
|
+
originalDraftVersion: "Draft14" /* Draft14 */
|
|
537
|
+
})),
|
|
538
|
+
// Then try parsing draft 11 and transform into draft 14
|
|
539
|
+
zCredentialIssuerMetadataDraft11To14.transform((credentialIssuerMetadata) => ({
|
|
540
|
+
credentialIssuerMetadata,
|
|
541
|
+
originalDraftVersion: "Draft11" /* Draft11 */
|
|
542
|
+
}))
|
|
543
|
+
]);
|
|
544
|
+
|
|
545
|
+
// src/metadata/credential-issuer/credential-issuer-metadata.ts
|
|
546
|
+
var wellKnownCredentialIssuerSuffix = ".well-known/openid-credential-issuer";
|
|
547
|
+
async function fetchCredentialIssuerMetadata(credentialIssuer, fetch) {
|
|
548
|
+
const wellKnownMetadataUrl = joinUriParts(credentialIssuer, [wellKnownCredentialIssuerSuffix]);
|
|
549
|
+
const result = await fetchWellKnownMetadata(wellKnownMetadataUrl, zCredentialIssuerMetadataWithDraftVersion, fetch);
|
|
550
|
+
if (result && result.credentialIssuerMetadata.credential_issuer !== credentialIssuer) {
|
|
551
|
+
throw new Oauth2Error(
|
|
552
|
+
`The 'credential_issuer' parameter '${result.credentialIssuerMetadata.credential_issuer}' in the well known credential issuer metadata at '${wellKnownMetadataUrl}' does not match the provided credential issuer '${credentialIssuer}'.`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
return result;
|
|
556
|
+
}
|
|
557
|
+
function extractKnownCredentialConfigurationSupportedFormats(credentialConfigurationsSupported) {
|
|
558
|
+
return Object.fromEntries(
|
|
559
|
+
Object.entries(credentialConfigurationsSupported).filter(
|
|
560
|
+
(entry) => allCredentialIssuerMetadataFormatIdentifiers.includes(entry[1].format)
|
|
561
|
+
)
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// src/credential-request/credential-request-configurations.ts
|
|
566
|
+
function getCredentialConfigurationsMatchingRequestFormat({
|
|
567
|
+
requestFormat,
|
|
568
|
+
credentialConfigurations
|
|
569
|
+
}) {
|
|
570
|
+
const knownCredentialConfigurations = extractKnownCredentialConfigurationSupportedFormats(credentialConfigurations);
|
|
571
|
+
return Object.fromEntries(
|
|
572
|
+
Object.entries(knownCredentialConfigurations).filter(([, credentialConfiguration]) => {
|
|
573
|
+
if (credentialConfiguration.format !== requestFormat.format) return false;
|
|
574
|
+
const r = requestFormat;
|
|
575
|
+
const c = credentialConfiguration;
|
|
576
|
+
if ((c.format === "ldp_vc" || c.format === "jwt_vc_json-ld") && r.format === c.format) {
|
|
577
|
+
return arrayEqualsIgnoreOrder(r.credential_definition.type, c.credential_definition.type) && arrayEqualsIgnoreOrder(r.credential_definition["@context"], c.credential_definition["@context"]);
|
|
578
|
+
}
|
|
579
|
+
if (c.format === "jwt_vc_json" && r.format === c.format) {
|
|
580
|
+
return arrayEqualsIgnoreOrder(r.credential_definition.type, c.credential_definition.type);
|
|
581
|
+
}
|
|
582
|
+
if (c.format === "vc+sd-jwt" && r.format === c.format) {
|
|
583
|
+
return r.vct === c.vct;
|
|
584
|
+
}
|
|
585
|
+
if (c.format === "mso_mdoc" && r.format === c.format) {
|
|
586
|
+
return r.doctype === c.doctype;
|
|
587
|
+
}
|
|
588
|
+
return false;
|
|
589
|
+
})
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// src/error/Openid4vciError.ts
|
|
594
|
+
var Openid4vciError = class extends Error {
|
|
595
|
+
constructor(message, options) {
|
|
596
|
+
const errorMessage = message ?? "Unknown error occured.";
|
|
597
|
+
const causeMessage = options?.cause instanceof Error ? ` ${options.cause.message}` : options?.cause ? ` ${options?.cause}` : "";
|
|
598
|
+
super(`${errorMessage}${causeMessage}`);
|
|
599
|
+
this.cause = options?.cause;
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// src/error/Openid4vciRetrieveCredentialsError.ts
|
|
604
|
+
var Openid4vciRetrieveCredentialsError = class extends Openid4vciError {
|
|
605
|
+
constructor(message, response, responseText) {
|
|
606
|
+
super(
|
|
607
|
+
`${message}
|
|
608
|
+
${JSON.stringify(response.credentialResponseResult?.data ?? response.credentialErrorResponseResult?.data ?? responseText, null, 2)}`
|
|
609
|
+
);
|
|
610
|
+
this.response = response;
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
// src/error/Openid4vciSendNotificationError.ts
|
|
615
|
+
var Openid4vciSendNotificationError = class extends Openid4vciError {
|
|
616
|
+
constructor(message, response) {
|
|
617
|
+
super(message);
|
|
618
|
+
this.response = response;
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
// src/metadata/credential-issuer/credential-configurations.ts
|
|
623
|
+
import { Oauth2Error as Oauth2Error2 } from "@openid4vc/oauth2";
|
|
624
|
+
import { ValidationError } from "@openid4vc/utils";
|
|
625
|
+
function extractScopesForCredentialConfigurationIds(options) {
|
|
626
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
627
|
+
for (const credentialConfigurationId of options.credentialConfigurationIds) {
|
|
628
|
+
const credentialConfiguration = options.issuerMetadata.credentialIssuer.credential_configurations_supported[credentialConfigurationId];
|
|
629
|
+
if (!credentialConfiguration) {
|
|
630
|
+
throw new Oauth2Error2(
|
|
631
|
+
`Credential configuration with id '${credentialConfigurationId}' not found in metadata from credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}'`
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
const scope = credentialConfiguration.scope;
|
|
635
|
+
if (scope) scopes.add(scope);
|
|
636
|
+
else if (!scope && options.throwOnConfigurationWithoutScope) {
|
|
637
|
+
throw new Oauth2Error2(
|
|
638
|
+
`Credential configuration with id '${credentialConfigurationId}' does not have a 'scope' configured, and 'throwOnConfigurationWithoutScope' was enabled.`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return scopes.size > 0 ? Array.from(scopes) : void 0;
|
|
643
|
+
}
|
|
644
|
+
function credentialsSupportedToCredentialConfigurationsSupported(credentialsSupported) {
|
|
645
|
+
const credentialConfigurationsSupported = {};
|
|
646
|
+
for (let index = 0; index < credentialsSupported.length; index++) {
|
|
647
|
+
const credentialSupported = credentialsSupported[index];
|
|
648
|
+
if (!credentialSupported.id) {
|
|
649
|
+
throw new Openid4vciError(
|
|
650
|
+
`Credential supported at index '${index}' does not have an 'id' property. Credential configuration requires the 'id' property as key`
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
const parseResult = zCredentialConfigurationSupportedDraft11To14.safeParse(credentialSupported);
|
|
654
|
+
if (!parseResult.success) {
|
|
655
|
+
throw new ValidationError(
|
|
656
|
+
`Error transforming credential supported with id '${credentialSupported.id}' to credential configuration supported format`,
|
|
657
|
+
parseResult.error
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
credentialConfigurationsSupported[credentialSupported.id] = parseResult.data;
|
|
661
|
+
}
|
|
662
|
+
return credentialConfigurationsSupported;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/Openid4vciClient.ts
|
|
666
|
+
import {
|
|
667
|
+
Oauth2Client,
|
|
668
|
+
Oauth2ClientAuthorizationChallengeError,
|
|
669
|
+
Oauth2Error as Oauth2Error7,
|
|
670
|
+
Oauth2ErrorCodes as Oauth2ErrorCodes2,
|
|
671
|
+
authorizationCodeGrantIdentifier as authorizationCodeGrantIdentifier2,
|
|
672
|
+
getAuthorizationServerMetadataFromList as getAuthorizationServerMetadataFromList2,
|
|
673
|
+
preAuthorizedCodeGrantIdentifier as preAuthorizedCodeGrantIdentifier3
|
|
674
|
+
} from "@openid4vc/oauth2";
|
|
675
|
+
|
|
676
|
+
// src/credential-offer/credential-offer.ts
|
|
677
|
+
import {
|
|
678
|
+
InvalidFetchResponseError,
|
|
679
|
+
Oauth2Error as Oauth2Error3,
|
|
680
|
+
authorizationCodeGrantIdentifier,
|
|
681
|
+
getAuthorizationServerMetadataFromList,
|
|
682
|
+
preAuthorizedCodeGrantIdentifier as preAuthorizedCodeGrantIdentifier2
|
|
683
|
+
} from "@openid4vc/oauth2";
|
|
684
|
+
import {
|
|
685
|
+
ContentType,
|
|
686
|
+
URL,
|
|
687
|
+
URLSearchParams,
|
|
688
|
+
ValidationError as ValidationError2,
|
|
689
|
+
createZodFetcher,
|
|
690
|
+
encodeToBase64Url,
|
|
691
|
+
getQueryParams,
|
|
692
|
+
objectToQueryParams,
|
|
693
|
+
parseWithErrorHandling
|
|
694
|
+
} from "@openid4vc/utils";
|
|
695
|
+
|
|
696
|
+
// src/credential-offer/z-credential-offer.ts
|
|
697
|
+
import {
|
|
698
|
+
preAuthorizedCodeGrantIdentifier
|
|
699
|
+
} from "@openid4vc/oauth2";
|
|
700
|
+
import z11 from "zod";
|
|
701
|
+
|
|
702
|
+
// ../utils/src/validation.ts
|
|
703
|
+
import z10 from "zod";
|
|
704
|
+
|
|
705
|
+
// ../utils/src/config.ts
|
|
706
|
+
var GLOBAL_CONFIG = {
|
|
707
|
+
allowInsecureUrls: false
|
|
708
|
+
};
|
|
709
|
+
function getGlobalConfig() {
|
|
710
|
+
return GLOBAL_CONFIG;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// ../utils/src/validation.ts
|
|
714
|
+
var zHttpsUrl2 = z10.string().url().refine(
|
|
715
|
+
(url) => {
|
|
716
|
+
const { allowInsecureUrls } = getGlobalConfig();
|
|
717
|
+
return allowInsecureUrls ? url.startsWith("http://") || url.startsWith("https://") : url.startsWith("https://");
|
|
718
|
+
},
|
|
719
|
+
{ message: "url must be an https:// url" }
|
|
720
|
+
);
|
|
721
|
+
var zInteger2 = z10.number().int();
|
|
722
|
+
var zHttpMethod = z10.enum(["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE", "CONNECT", "PATCH"]);
|
|
723
|
+
|
|
724
|
+
// src/credential-offer/z-credential-offer.ts
|
|
725
|
+
var zTxCode = z11.object({
|
|
726
|
+
input_mode: z11.union([z11.literal("numeric"), z11.literal("text")]).optional(),
|
|
727
|
+
length: z11.number().int().optional(),
|
|
728
|
+
description: z11.string().max(300).optional()
|
|
729
|
+
}).passthrough();
|
|
730
|
+
var zCredentialOfferGrants = z11.object({
|
|
731
|
+
authorization_code: z11.object({
|
|
732
|
+
issuer_state: z11.string().optional(),
|
|
733
|
+
authorization_server: zHttpsUrl2.optional()
|
|
734
|
+
}).passthrough().optional(),
|
|
735
|
+
[preAuthorizedCodeGrantIdentifier]: z11.object({
|
|
736
|
+
"pre-authorized_code": z11.string(),
|
|
737
|
+
tx_code: zTxCode.optional(),
|
|
738
|
+
authorization_server: zHttpsUrl2.optional()
|
|
739
|
+
}).passthrough().optional()
|
|
740
|
+
}).passthrough();
|
|
741
|
+
var zCredentialOfferObjectDraft14 = z11.object({
|
|
742
|
+
credential_issuer: zHttpsUrl2,
|
|
743
|
+
credential_configuration_ids: z11.array(z11.string()),
|
|
744
|
+
grants: z11.optional(zCredentialOfferGrants)
|
|
745
|
+
}).passthrough();
|
|
746
|
+
var zCredentialOfferObjectDraft11To14 = z11.object({
|
|
747
|
+
credential_issuer: zHttpsUrl2,
|
|
748
|
+
// We don't support the inline offer objects from draft 11
|
|
749
|
+
credentials: z11.array(
|
|
750
|
+
z11.string({ message: "Only string credential identifiers are supported for draft 11 credential offers" })
|
|
751
|
+
),
|
|
752
|
+
grants: z11.optional(
|
|
753
|
+
z11.object({
|
|
754
|
+
// Has extra param in draft 14, but doesn't matter for transform purposes
|
|
755
|
+
authorization_code: zCredentialOfferGrants.shape.authorization_code,
|
|
756
|
+
[preAuthorizedCodeGrantIdentifier]: z11.object({
|
|
757
|
+
"pre-authorized_code": z11.string(),
|
|
758
|
+
user_pin_required: z11.optional(z11.boolean())
|
|
759
|
+
}).passthrough().optional()
|
|
760
|
+
})
|
|
761
|
+
)
|
|
762
|
+
}).passthrough().transform(({ credentials, grants, ...rest }) => {
|
|
763
|
+
const v14 = {
|
|
764
|
+
...rest,
|
|
765
|
+
credential_configuration_ids: credentials
|
|
766
|
+
};
|
|
767
|
+
if (grants) {
|
|
768
|
+
v14.grants = { ...grants };
|
|
769
|
+
if (grants[preAuthorizedCodeGrantIdentifier]) {
|
|
770
|
+
const { user_pin_required, ...restGrants } = grants[preAuthorizedCodeGrantIdentifier];
|
|
771
|
+
v14.grants[preAuthorizedCodeGrantIdentifier] = {
|
|
772
|
+
...restGrants
|
|
773
|
+
};
|
|
774
|
+
if (user_pin_required) {
|
|
775
|
+
v14.grants[preAuthorizedCodeGrantIdentifier].tx_code = {
|
|
776
|
+
input_mode: "text"
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
return v14;
|
|
782
|
+
}).pipe(zCredentialOfferObjectDraft14);
|
|
783
|
+
var zCredentialOfferObject = z11.union([
|
|
784
|
+
// First prioritize draft 14 (and 13)
|
|
785
|
+
zCredentialOfferObjectDraft14,
|
|
786
|
+
// Then try parsing draft 11 and transform into draft 14
|
|
787
|
+
zCredentialOfferObjectDraft11To14
|
|
788
|
+
]);
|
|
789
|
+
|
|
790
|
+
// src/credential-offer/credential-offer.ts
|
|
791
|
+
async function resolveCredentialOffer(credentialOffer, options) {
|
|
792
|
+
const parsedQueryParams = getQueryParams(credentialOffer);
|
|
793
|
+
let credentialOfferParseResult;
|
|
794
|
+
if (parsedQueryParams.credential_offer_uri) {
|
|
795
|
+
const fetchWithZod = createZodFetcher(options?.fetch);
|
|
796
|
+
const { response, result } = await fetchWithZod(
|
|
797
|
+
zCredentialOfferObject,
|
|
798
|
+
ContentType.Json,
|
|
799
|
+
parsedQueryParams.credential_offer_uri
|
|
800
|
+
);
|
|
801
|
+
if (!response.ok || !result) {
|
|
802
|
+
throw new InvalidFetchResponseError(
|
|
803
|
+
`Fetching credential offer from '${parsedQueryParams.credential_offer_uri}' resulted in an unsuccesfull response with status '${response.status}'`,
|
|
804
|
+
await response.clone().text(),
|
|
805
|
+
response
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
credentialOfferParseResult = result;
|
|
809
|
+
} else if (parsedQueryParams.credential_offer) {
|
|
810
|
+
let credentialOfferJson;
|
|
811
|
+
try {
|
|
812
|
+
credentialOfferJson = JSON.parse(decodeURIComponent(parsedQueryParams.credential_offer));
|
|
813
|
+
} catch (error) {
|
|
814
|
+
throw new Oauth2Error3(`Error parsing JSON from 'credential_offer' param in credential offer '${credentialOffer}'`);
|
|
815
|
+
}
|
|
816
|
+
credentialOfferParseResult = zCredentialOfferObject.safeParse(credentialOfferJson);
|
|
817
|
+
} else {
|
|
818
|
+
throw new Oauth2Error3(`Credential offer did not contain either 'credential_offer' or 'credential_offer_uri' param.`);
|
|
819
|
+
}
|
|
820
|
+
if (credentialOfferParseResult.error) {
|
|
821
|
+
throw new ValidationError2(
|
|
822
|
+
`Error parsing credential offer in draft 11, 13 or 14 format extracted from credential offer '${credentialOffer}'`,
|
|
823
|
+
credentialOfferParseResult.error
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
return credentialOfferParseResult.data;
|
|
827
|
+
}
|
|
828
|
+
function determineAuthorizationServerForCredentialOffer(options) {
|
|
829
|
+
const authorizationServers = options.issuerMetadata.credentialIssuer.authorization_servers;
|
|
830
|
+
let authorizationServer;
|
|
831
|
+
if (options.grantAuthorizationServer) {
|
|
832
|
+
authorizationServer = options.grantAuthorizationServer;
|
|
833
|
+
if (!authorizationServers) {
|
|
834
|
+
throw new Oauth2Error3(
|
|
835
|
+
`Credential offer grant contains 'authorization_server' with value '${options.grantAuthorizationServer}' but credential issuer metadata does not have an 'authorization_servers' property to match the value against.`
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
if (!authorizationServers.includes(authorizationServer)) {
|
|
839
|
+
throw new Oauth2Error3(
|
|
840
|
+
`Credential offer grant contains 'authorization_server' with value '${options.grantAuthorizationServer}' but credential issuer metadata does not include this authorization server. Available 'authorization_server' values are ${authorizationServers.join(", ")}.`
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
} else if (!authorizationServers) {
|
|
844
|
+
authorizationServer = options.issuerMetadata.credentialIssuer.credential_issuer;
|
|
845
|
+
} else {
|
|
846
|
+
if (authorizationServers.length === 0) {
|
|
847
|
+
throw new Oauth2Error3(`Credential issuer metadata has 'authorization_servers' value with length of 0`);
|
|
848
|
+
}
|
|
849
|
+
if (authorizationServers.length > 1) {
|
|
850
|
+
throw new Oauth2Error3(
|
|
851
|
+
`Credential issuer metadata has 'authorization_server' with multiple entries, but the credential offer grant did not specify which authorization server to use.`
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
authorizationServer = authorizationServers[0];
|
|
855
|
+
}
|
|
856
|
+
return authorizationServer;
|
|
857
|
+
}
|
|
858
|
+
async function createCredentialOffer(options) {
|
|
859
|
+
const {
|
|
860
|
+
[preAuthorizedCodeGrantIdentifier2]: preAuthorizedCodeGrant,
|
|
861
|
+
[authorizationCodeGrantIdentifier]: authorizationCodeGrant,
|
|
862
|
+
...restGrants
|
|
863
|
+
} = options.grants;
|
|
864
|
+
const grants = { ...restGrants };
|
|
865
|
+
if (authorizationCodeGrant) {
|
|
866
|
+
determineAuthorizationServerForCredentialOffer({
|
|
867
|
+
issuerMetadata: options.issuerMetadata,
|
|
868
|
+
grantAuthorizationServer: authorizationCodeGrant.authorization_server
|
|
869
|
+
});
|
|
870
|
+
grants[authorizationCodeGrantIdentifier] = authorizationCodeGrant;
|
|
871
|
+
}
|
|
872
|
+
if (preAuthorizedCodeGrant) {
|
|
873
|
+
determineAuthorizationServerForCredentialOffer({
|
|
874
|
+
issuerMetadata: options.issuerMetadata,
|
|
875
|
+
grantAuthorizationServer: preAuthorizedCodeGrant.authorization_server
|
|
876
|
+
});
|
|
877
|
+
grants[preAuthorizedCodeGrantIdentifier2] = {
|
|
878
|
+
...preAuthorizedCodeGrant,
|
|
879
|
+
"pre-authorized_code": preAuthorizedCodeGrant["pre-authorized_code"] ?? encodeToBase64Url(await options.callbacks.generateRandom(32))
|
|
880
|
+
};
|
|
881
|
+
const txCode = grants[preAuthorizedCodeGrantIdentifier2].tx_code;
|
|
882
|
+
if (txCode && options.issuerMetadata.originalDraftVersion === "Draft11" /* Draft11 */) {
|
|
883
|
+
grants[preAuthorizedCodeGrantIdentifier2].user_pin_required = txCode !== void 0;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
const idsNotInMetadata = options.credentialConfigurationIds.filter(
|
|
887
|
+
(id) => options.issuerMetadata.credentialIssuer.credential_configurations_supported[id] === void 0
|
|
888
|
+
);
|
|
889
|
+
if (idsNotInMetadata.length > 0) {
|
|
890
|
+
throw new Oauth2Error3(
|
|
891
|
+
`Credential configuration ids ${idsNotInMetadata} not found in the credential issuer metadata 'credential_configurations_supported'. Available ids are ${Object.keys(options.issuerMetadata.credentialIssuer.credential_configurations_supported).join(", ")}.`
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
const credentialOfferScheme = options.credentialOfferScheme ?? "openid-credential-offer://";
|
|
895
|
+
const credentialOfferObject = parseWithErrorHandling(zCredentialOfferObject, {
|
|
896
|
+
credential_issuer: options.issuerMetadata.credentialIssuer.credential_issuer,
|
|
897
|
+
credential_configuration_ids: options.credentialConfigurationIds,
|
|
898
|
+
grants,
|
|
899
|
+
...options.additionalPayload
|
|
900
|
+
});
|
|
901
|
+
if (options.issuerMetadata.originalDraftVersion === "Draft11" /* Draft11 */) {
|
|
902
|
+
credentialOfferObject.credentials = credentialOfferObject.credential_configuration_ids;
|
|
903
|
+
}
|
|
904
|
+
const url = new URL(credentialOfferScheme);
|
|
905
|
+
url.search = `?${new URLSearchParams([
|
|
906
|
+
...url.searchParams.entries(),
|
|
907
|
+
...objectToQueryParams({
|
|
908
|
+
credential_offer_uri: options.credentialOfferUri,
|
|
909
|
+
// Only add credential_offer is uri is undefined
|
|
910
|
+
credential_offer: options.credentialOfferUri ? void 0 : credentialOfferObject
|
|
911
|
+
}).entries()
|
|
912
|
+
]).toString()}`;
|
|
913
|
+
return {
|
|
914
|
+
credentialOffer: url.toString(),
|
|
915
|
+
credentialOfferObject
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// src/credential-request/format-payload.ts
|
|
920
|
+
import { zIs } from "@openid4vc/utils";
|
|
921
|
+
function getCredentialRequestFormatPayloadForCredentialConfigurationId(options) {
|
|
922
|
+
const credentialConfiguration = options.issuerMetadata.credentialIssuer.credential_configurations_supported[options.credentialConfigurationId];
|
|
923
|
+
if (!credentialConfiguration) {
|
|
924
|
+
throw new Openid4vciError(
|
|
925
|
+
`Could not find credential configuration with id '${options.credentialConfigurationId}' in metadata of credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}'.`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
if (zIs(zSdJwtVcCredentialIssuerMetadata, credentialConfiguration)) {
|
|
929
|
+
return {
|
|
930
|
+
format: credentialConfiguration.format,
|
|
931
|
+
vct: credentialConfiguration.vct
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
if (zIs(zMsoMdocCredentialIssuerMetadata, credentialConfiguration)) {
|
|
935
|
+
return {
|
|
936
|
+
format: credentialConfiguration.format,
|
|
937
|
+
doctype: credentialConfiguration.doctype
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
if (zIs(zLdpVcCredentialIssuerMetadata, credentialConfiguration)) {
|
|
941
|
+
return {
|
|
942
|
+
format: credentialConfiguration.format,
|
|
943
|
+
credential_definition: {
|
|
944
|
+
"@context": credentialConfiguration.credential_definition["@context"],
|
|
945
|
+
type: credentialConfiguration.credential_definition.type
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
if (zIs(zJwtVcJsonLdCredentialIssuerMetadata, credentialConfiguration)) {
|
|
950
|
+
return {
|
|
951
|
+
format: credentialConfiguration.format,
|
|
952
|
+
credential_definition: {
|
|
953
|
+
"@context": credentialConfiguration.credential_definition["@context"],
|
|
954
|
+
type: credentialConfiguration.credential_definition.type
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
if (zIs(zJwtVcJsonCredentialIssuerMetadata, credentialConfiguration)) {
|
|
959
|
+
return {
|
|
960
|
+
format: credentialConfiguration.format,
|
|
961
|
+
credential_definition: {
|
|
962
|
+
type: credentialConfiguration.credential_definition.type
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
throw new Openid4vciError(
|
|
967
|
+
`Unknown format '${credentialConfiguration.format}' in credential configuration with id '${options.credentialConfigurationId}' for credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}'`
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/credential-request/retrieve-credentials.ts
|
|
972
|
+
import {
|
|
973
|
+
Oauth2Error as Oauth2Error4,
|
|
974
|
+
resourceRequest
|
|
975
|
+
} from "@openid4vc/oauth2";
|
|
976
|
+
import { ContentType as ContentType2, isResponseContentType, parseWithErrorHandling as parseWithErrorHandling2 } from "@openid4vc/utils";
|
|
977
|
+
|
|
978
|
+
// src/credential-request/z-credential-request.ts
|
|
979
|
+
import z15 from "zod";
|
|
980
|
+
|
|
981
|
+
// src/credential-request/z-credential-request-common.ts
|
|
982
|
+
import { zJwk as zJwk2 } from "@openid4vc/oauth2";
|
|
983
|
+
import z14 from "zod";
|
|
984
|
+
|
|
985
|
+
// src/formats/proof-type/jwt/z-jwt-proof-type.ts
|
|
986
|
+
import { zCompactJwt as zCompactJwt2, zJwtHeader as zJwtHeader2, zJwtPayload as zJwtPayload2 } from "@openid4vc/oauth2";
|
|
987
|
+
import { zHttpsUrl as zHttpsUrl3, zInteger as zInteger3 } from "@openid4vc/utils";
|
|
988
|
+
import z12 from "zod";
|
|
989
|
+
var zJwtProofTypeIdentifier = z12.literal("jwt");
|
|
990
|
+
var jwtProofTypeIdentifier = zJwtProofTypeIdentifier.value;
|
|
991
|
+
var zCredentialRequestProofJwt = z12.object({
|
|
992
|
+
proof_type: zJwtProofTypeIdentifier,
|
|
993
|
+
jwt: zCompactJwt2
|
|
994
|
+
});
|
|
995
|
+
var zCredentialRequestJwtProofTypeHeader = zJwtHeader2.merge(
|
|
996
|
+
z12.object({
|
|
997
|
+
key_attestation: z12.optional(zCompactJwt2),
|
|
998
|
+
typ: z12.literal("openid4vci-proof+jwt")
|
|
999
|
+
})
|
|
1000
|
+
).passthrough().refine(({ kid, jwk }) => jwk === void 0 || kid === void 0, {
|
|
1001
|
+
message: `Both 'jwk' and 'kid' are defined. Only one is allowed`
|
|
1002
|
+
}).refine(({ trust_chain, kid }) => !trust_chain || !kid, {
|
|
1003
|
+
message: `When 'trust_chain' is provided, 'kid' is required`
|
|
1004
|
+
});
|
|
1005
|
+
var zCredentialRequestJwtProofTypePayload = z12.object({
|
|
1006
|
+
...zJwtPayload2.shape,
|
|
1007
|
+
aud: zHttpsUrl3,
|
|
1008
|
+
iat: zInteger3
|
|
1009
|
+
}).passthrough();
|
|
1010
|
+
|
|
1011
|
+
// src/formats/proof-type/attestation/z-attestation-proof-type.ts
|
|
1012
|
+
import { zCompactJwt as zCompactJwt3 } from "@openid4vc/oauth2";
|
|
1013
|
+
import z13 from "zod";
|
|
1014
|
+
var zAttestationProofTypeIdentifier = z13.literal("attestation");
|
|
1015
|
+
var attestationProofTypeIdentifier = zAttestationProofTypeIdentifier.value;
|
|
1016
|
+
var zCredentialRequestProofAttestation = z13.object({
|
|
1017
|
+
proof_type: zAttestationProofTypeIdentifier,
|
|
1018
|
+
attestation: zCompactJwt3
|
|
1019
|
+
});
|
|
1020
|
+
var zCredentialRequestAttestationProofTypePayload = zKeyAttestationJwtPayloadForUse("proof_type.attestation");
|
|
1021
|
+
|
|
1022
|
+
// src/credential-request/z-credential-request-common.ts
|
|
1023
|
+
var zCredentialRequestProofCommon = z14.object({
|
|
1024
|
+
proof_type: z14.string()
|
|
1025
|
+
}).passthrough();
|
|
1026
|
+
var allCredentialRequestProofs = [zCredentialRequestProofJwt, zCredentialRequestProofAttestation];
|
|
1027
|
+
var zCredentialRequestProof = z14.union([
|
|
1028
|
+
zCredentialRequestProofCommon,
|
|
1029
|
+
z14.discriminatedUnion("proof_type", allCredentialRequestProofs)
|
|
1030
|
+
]);
|
|
1031
|
+
var zCredentialRequestProofsCommon = z14.record(z14.string(), z14.array(z14.unknown()));
|
|
1032
|
+
var zCredentialRequestProofs = z14.object({
|
|
1033
|
+
[zJwtProofTypeIdentifier.value]: z14.optional(z14.array(zCredentialRequestProofJwt.shape.jwt)),
|
|
1034
|
+
[zAttestationProofTypeIdentifier.value]: z14.optional(z14.array(zCredentialRequestProofAttestation.shape.attestation))
|
|
1035
|
+
});
|
|
1036
|
+
var zCredentialRequestCommon = z14.object({
|
|
1037
|
+
proof: zCredentialRequestProof.optional(),
|
|
1038
|
+
proofs: z14.optional(
|
|
1039
|
+
z14.intersection(zCredentialRequestProofsCommon, zCredentialRequestProofs).refine((proofs) => Object.values(proofs).length === 1, {
|
|
1040
|
+
message: `The 'proofs' object in a credential request should contain exactly one attribute`
|
|
1041
|
+
})
|
|
1042
|
+
),
|
|
1043
|
+
credential_response_encryption: z14.object({
|
|
1044
|
+
jwk: zJwk2,
|
|
1045
|
+
alg: z14.string(),
|
|
1046
|
+
enc: z14.string()
|
|
1047
|
+
}).passthrough().optional()
|
|
1048
|
+
}).passthrough().refine(({ proof, proofs }) => !(proof !== void 0 && proofs !== void 0), {
|
|
1049
|
+
message: `Both 'proof' and 'proofs' are defined. Only one is allowed`
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
// src/credential-request/z-credential-request.ts
|
|
1053
|
+
var allCredentialRequestFormats = [
|
|
1054
|
+
zSdJwtVcCredentialRequestFormat,
|
|
1055
|
+
zMsoMdocCredentialRequestFormat,
|
|
1056
|
+
zLdpVcCredentialRequestFormat,
|
|
1057
|
+
zJwtVcJsonLdCredentialRequestFormat,
|
|
1058
|
+
zJwtVcJsonCredentialRequestFormat
|
|
1059
|
+
];
|
|
1060
|
+
var allCredentialRequestFormatIdentifiers = allCredentialRequestFormats.map(
|
|
1061
|
+
(format) => format.shape.format.value
|
|
1062
|
+
);
|
|
1063
|
+
var zAuthorizationDetailsCredentialRequest = z15.object({
|
|
1064
|
+
credential_identifier: z15.string(),
|
|
1065
|
+
// Cannot be present if credential identifier is present
|
|
1066
|
+
format: z15.never({ message: "'format' cannot be defined when 'credential_identifier' is set." }).optional()
|
|
1067
|
+
});
|
|
1068
|
+
var zCredentialRequestFormatNoCredentialIdentifier = z15.object({
|
|
1069
|
+
format: z15.string(),
|
|
1070
|
+
credential_identifier: z15.never({ message: "'credential_identifier' cannot be defined when 'format' is set." }).optional()
|
|
1071
|
+
}).passthrough();
|
|
1072
|
+
var zCredenialRequestDraft14WithFormat = zCredentialRequestCommon.and(zCredentialRequestFormatNoCredentialIdentifier).transform((data, ctx) => {
|
|
1073
|
+
if (!allCredentialRequestFormatIdentifiers.includes(data.format)) return data;
|
|
1074
|
+
const result = z15.object({}).passthrough().and(z15.discriminatedUnion("format", allCredentialRequestFormats)).safeParse(data);
|
|
1075
|
+
if (result.success) {
|
|
1076
|
+
return result.data;
|
|
1077
|
+
}
|
|
1078
|
+
for (const issue of result.error.issues) {
|
|
1079
|
+
ctx.addIssue(issue);
|
|
1080
|
+
}
|
|
1081
|
+
return z15.NEVER;
|
|
1082
|
+
});
|
|
1083
|
+
var zCredentialRequestDraft14 = z15.union([
|
|
1084
|
+
zCredenialRequestDraft14WithFormat,
|
|
1085
|
+
zCredentialRequestCommon.and(zAuthorizationDetailsCredentialRequest)
|
|
1086
|
+
]);
|
|
1087
|
+
var zCredentialRequestDraft11To14 = zCredentialRequestCommon.and(zCredentialRequestFormatNoCredentialIdentifier).transform((data, ctx) => {
|
|
1088
|
+
const formatSpecificTransformations = {
|
|
1089
|
+
[zLdpVcFormatIdentifier.value]: zLdpVcCredentialRequestDraft11To14,
|
|
1090
|
+
[zJwtVcJsonFormatIdentifier.value]: zJwtVcJsonCredentialRequestDraft11To14,
|
|
1091
|
+
[zJwtVcJsonLdFormatIdentifier.value]: zJwtVcJsonLdCredentialRequestDraft11To14
|
|
1092
|
+
};
|
|
1093
|
+
if (!Object.keys(formatSpecificTransformations).includes(data.format)) return data;
|
|
1094
|
+
const schema = formatSpecificTransformations[data.format];
|
|
1095
|
+
const result = schema.safeParse(data);
|
|
1096
|
+
if (result.success) return result.data;
|
|
1097
|
+
for (const issue of result.error.issues) {
|
|
1098
|
+
ctx.addIssue(issue);
|
|
1099
|
+
}
|
|
1100
|
+
return z15.NEVER;
|
|
1101
|
+
}).pipe(zCredentialRequestDraft14);
|
|
1102
|
+
var zCredentialRequestDraft14To11 = zCredentialRequestDraft14.refine(
|
|
1103
|
+
(data) => data.credential_identifier === void 0,
|
|
1104
|
+
`'credential_identifier' is not supported in OpenID4VCI draft 11`
|
|
1105
|
+
).transform((data, ctx) => {
|
|
1106
|
+
const formatSpecificTransformations = {
|
|
1107
|
+
[zLdpVcFormatIdentifier.value]: zLdpVcCredentialRequestDraft14To11,
|
|
1108
|
+
[zJwtVcJsonFormatIdentifier.value]: zJwtVcJsonCredentialRequestDraft14To11,
|
|
1109
|
+
[zJwtVcJsonLdFormatIdentifier.value]: zJwtVcJsonLdCredentialRequestDraft14To11
|
|
1110
|
+
};
|
|
1111
|
+
if (!Object.keys(formatSpecificTransformations).includes(data.format)) return data;
|
|
1112
|
+
const schema = formatSpecificTransformations[data.format];
|
|
1113
|
+
const result = schema.safeParse(data);
|
|
1114
|
+
if (result.success) return result.data;
|
|
1115
|
+
for (const issue of result.error.issues) {
|
|
1116
|
+
ctx.addIssue(issue);
|
|
1117
|
+
}
|
|
1118
|
+
return z15.NEVER;
|
|
1119
|
+
});
|
|
1120
|
+
var zCredentialRequest = z15.union([zCredentialRequestDraft14, zCredentialRequestDraft11To14]);
|
|
1121
|
+
|
|
1122
|
+
// src/credential-request/z-credential-response.ts
|
|
1123
|
+
import z17 from "zod";
|
|
1124
|
+
|
|
1125
|
+
// ../oauth2/src/common/z-oauth2-error.ts
|
|
1126
|
+
import z16 from "zod";
|
|
1127
|
+
var Oauth2ErrorCodes = /* @__PURE__ */ ((Oauth2ErrorCodes4) => {
|
|
1128
|
+
Oauth2ErrorCodes4["ServerError"] = "server_error";
|
|
1129
|
+
Oauth2ErrorCodes4["InvalidTarget"] = "invalid_target";
|
|
1130
|
+
Oauth2ErrorCodes4["InvalidRequest"] = "invalid_request";
|
|
1131
|
+
Oauth2ErrorCodes4["InvalidToken"] = "invalid_token";
|
|
1132
|
+
Oauth2ErrorCodes4["InsufficientScope"] = "insufficient_scope";
|
|
1133
|
+
Oauth2ErrorCodes4["InvalidGrant"] = "invalid_grant";
|
|
1134
|
+
Oauth2ErrorCodes4["InvalidClient"] = "invalid_client";
|
|
1135
|
+
Oauth2ErrorCodes4["UnauthorizedClient"] = "unauthorized_client";
|
|
1136
|
+
Oauth2ErrorCodes4["UnsupportedGrantType"] = "unsupported_grant_type";
|
|
1137
|
+
Oauth2ErrorCodes4["InvalidScope"] = "invalid_scope";
|
|
1138
|
+
Oauth2ErrorCodes4["InvalidDpopProof"] = "invalid_dpop_proof";
|
|
1139
|
+
Oauth2ErrorCodes4["UseDpopNonce"] = "use_dpop_nonce";
|
|
1140
|
+
Oauth2ErrorCodes4["RedirectToWeb"] = "redirect_to_web";
|
|
1141
|
+
Oauth2ErrorCodes4["InvalidSession"] = "invalid_session";
|
|
1142
|
+
Oauth2ErrorCodes4["InsufficientAuthorization"] = "insufficient_authorization";
|
|
1143
|
+
Oauth2ErrorCodes4["InvalidCredentialRequest"] = "invalid_credential_request";
|
|
1144
|
+
Oauth2ErrorCodes4["CredentialRequestDenied"] = "credential_request_denied";
|
|
1145
|
+
Oauth2ErrorCodes4["UnsupportedCredentialType"] = "unsupported_credential_type";
|
|
1146
|
+
Oauth2ErrorCodes4["UnsupportedCredentialFormat"] = "unsupported_credential_format";
|
|
1147
|
+
Oauth2ErrorCodes4["InvalidProof"] = "invalid_proof";
|
|
1148
|
+
Oauth2ErrorCodes4["InvalidNonce"] = "invalid_nonce";
|
|
1149
|
+
Oauth2ErrorCodes4["InvalidEncryptionParameters"] = "invalid_encryption_parameters";
|
|
1150
|
+
Oauth2ErrorCodes4["InvalidRequestUri"] = "invalid_request_uri";
|
|
1151
|
+
Oauth2ErrorCodes4["InvalidRequestObject"] = "invalid_request_object";
|
|
1152
|
+
Oauth2ErrorCodes4["RequestNotSupported"] = "request_not_supported";
|
|
1153
|
+
Oauth2ErrorCodes4["RequestUriNotSupported"] = "request_uri_not_supported";
|
|
1154
|
+
Oauth2ErrorCodes4["VpFormatsNotSupported"] = "vp_formats_not_supported";
|
|
1155
|
+
Oauth2ErrorCodes4["AccessDenied"] = "access_denied";
|
|
1156
|
+
Oauth2ErrorCodes4["InvalidPresentationDefinitionUri"] = "invalid_presentation_definition_uri";
|
|
1157
|
+
Oauth2ErrorCodes4["InvalidPresentationDefinitionReference"] = "invalid_presentation_definition_reference";
|
|
1158
|
+
Oauth2ErrorCodes4["InvalidRequestUriMethod"] = "invalid_request_uri_method";
|
|
1159
|
+
Oauth2ErrorCodes4["InvalidTransactionData"] = "invalid_transaction_data";
|
|
1160
|
+
Oauth2ErrorCodes4["WalletUnavailable"] = "wallet_unavailable";
|
|
1161
|
+
return Oauth2ErrorCodes4;
|
|
1162
|
+
})(Oauth2ErrorCodes || {});
|
|
1163
|
+
var zOauth2ErrorResponse = z16.object({
|
|
1164
|
+
error: z16.union([z16.nativeEnum(Oauth2ErrorCodes), z16.string()]),
|
|
1165
|
+
error_description: z16.string().optional(),
|
|
1166
|
+
error_uri: z16.string().optional()
|
|
1167
|
+
}).passthrough();
|
|
1168
|
+
|
|
1169
|
+
// src/credential-request/z-credential-response.ts
|
|
1170
|
+
var zCredentialEncoding = z17.union([z17.string(), z17.record(z17.string(), z17.any())]);
|
|
1171
|
+
var zCredentialResponse = z17.object({
|
|
1172
|
+
credential: z17.optional(zCredentialEncoding),
|
|
1173
|
+
credentials: z17.optional(z17.array(zCredentialEncoding)),
|
|
1174
|
+
transaction_id: z17.string().optional(),
|
|
1175
|
+
c_nonce: z17.string().optional(),
|
|
1176
|
+
c_nonce_expires_in: z17.number().int().optional(),
|
|
1177
|
+
notification_id: z17.string().optional()
|
|
1178
|
+
}).passthrough().refine(
|
|
1179
|
+
(value) => {
|
|
1180
|
+
const { credential, credentials, transaction_id } = value;
|
|
1181
|
+
return [credential, credentials, transaction_id].filter((i) => i !== void 0).length === 1;
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
message: `Exactly one of 'credential', 'credentials', or 'transaction_id' MUST be defined.`
|
|
1185
|
+
}
|
|
1186
|
+
);
|
|
1187
|
+
var zCredentialErrorResponse = z17.object({
|
|
1188
|
+
...zOauth2ErrorResponse.shape,
|
|
1189
|
+
c_nonce: z17.string().optional(),
|
|
1190
|
+
c_nonce_expires_in: z17.number().int().optional()
|
|
1191
|
+
}).passthrough();
|
|
1192
|
+
|
|
1193
|
+
// src/credential-request/retrieve-credentials.ts
|
|
1194
|
+
async function retrieveCredentialsWithFormat(options) {
|
|
1195
|
+
const credentialRequest = {
|
|
1196
|
+
...options.formatPayload,
|
|
1197
|
+
...options.additionalRequestPayload,
|
|
1198
|
+
proof: options.proof,
|
|
1199
|
+
proofs: options.proofs
|
|
1200
|
+
};
|
|
1201
|
+
return retrieveCredentials({
|
|
1202
|
+
callbacks: options.callbacks,
|
|
1203
|
+
credentialRequest,
|
|
1204
|
+
issuerMetadata: options.issuerMetadata,
|
|
1205
|
+
accessToken: options.accessToken,
|
|
1206
|
+
dpop: options.dpop
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
async function retrieveCredentials(options) {
|
|
1210
|
+
const credentialEndpoint = options.issuerMetadata.credentialIssuer.credential_endpoint;
|
|
1211
|
+
let credentialRequest = parseWithErrorHandling2(
|
|
1212
|
+
zCredentialRequest,
|
|
1213
|
+
options.credentialRequest,
|
|
1214
|
+
"Error validating credential request"
|
|
1215
|
+
);
|
|
1216
|
+
if (credentialRequest.proofs) {
|
|
1217
|
+
const { batch_credential_issuance } = options.issuerMetadata.credentialIssuer;
|
|
1218
|
+
if (!batch_credential_issuance) {
|
|
1219
|
+
throw new Oauth2Error4(
|
|
1220
|
+
`Credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}' does not support batch credential issuance using the 'proofs' request property. Only 'proof' is supported.`
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
const proofs = Object.values(credentialRequest.proofs)[0];
|
|
1224
|
+
if (proofs.length > batch_credential_issuance.batch_size) {
|
|
1225
|
+
throw new Oauth2Error4(
|
|
1226
|
+
`Credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}' supports batch issuance, but the max batch size is '${batch_credential_issuance.batch_size}'. A total of '${proofs.length}' proofs were provided.`
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
if (options.issuerMetadata.originalDraftVersion === "Draft11" /* Draft11 */) {
|
|
1231
|
+
credentialRequest = parseWithErrorHandling2(
|
|
1232
|
+
zCredentialRequestDraft14To11,
|
|
1233
|
+
credentialRequest,
|
|
1234
|
+
`Error transforming credential request from ${"Draft14" /* Draft14 */} to ${"Draft11" /* Draft11 */}`
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
const resourceResponse = await resourceRequest({
|
|
1238
|
+
dpop: options.dpop,
|
|
1239
|
+
accessToken: options.accessToken,
|
|
1240
|
+
callbacks: options.callbacks,
|
|
1241
|
+
url: credentialEndpoint,
|
|
1242
|
+
requestOptions: {
|
|
1243
|
+
method: "POST",
|
|
1244
|
+
headers: {
|
|
1245
|
+
"Content-Type": ContentType2.Json
|
|
1246
|
+
},
|
|
1247
|
+
body: JSON.stringify(credentialRequest)
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
if (!resourceResponse.ok) {
|
|
1251
|
+
const credentialErrorResponseResult = isResponseContentType(ContentType2.Json, resourceResponse.response) ? zCredentialErrorResponse.safeParse(await resourceResponse.response.clone().json()) : void 0;
|
|
1252
|
+
return {
|
|
1253
|
+
...resourceResponse,
|
|
1254
|
+
credentialErrorResponseResult
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
const credentialResponseResult = isResponseContentType(ContentType2.Json, resourceResponse.response) ? zCredentialResponse.safeParse(await resourceResponse.response.clone().json()) : void 0;
|
|
1258
|
+
if (!credentialResponseResult?.success) {
|
|
1259
|
+
return {
|
|
1260
|
+
...resourceResponse,
|
|
1261
|
+
ok: false,
|
|
1262
|
+
credentialResponseResult
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
return {
|
|
1266
|
+
...resourceResponse,
|
|
1267
|
+
credentialResponse: credentialResponseResult.data
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// src/formats/proof-type/jwt/jwt-proof-type.ts
|
|
1272
|
+
import { decodeJwt as decodeJwt2, isJwkInSet, jwtHeaderFromJwtSigner as jwtHeaderFromJwtSigner2 } from "@openid4vc/oauth2";
|
|
1273
|
+
import { jwtSignerFromJwt as jwtSignerFromJwt2, verifyJwt as verifyJwt2 } from "@openid4vc/oauth2";
|
|
1274
|
+
import { dateToSeconds as dateToSeconds2, parseWithErrorHandling as parseWithErrorHandling4 } from "@openid4vc/utils";
|
|
1275
|
+
|
|
1276
|
+
// src/key-attestation/key-attestation.ts
|
|
1277
|
+
import { decodeJwt, jwtHeaderFromJwtSigner } from "@openid4vc/oauth2";
|
|
1278
|
+
import { jwtSignerFromJwt, verifyJwt } from "@openid4vc/oauth2";
|
|
1279
|
+
import { dateToSeconds, parseWithErrorHandling as parseWithErrorHandling3 } from "@openid4vc/utils";
|
|
1280
|
+
async function verifyKeyAttestationJwt(options) {
|
|
1281
|
+
const { header, payload } = decodeJwt({
|
|
1282
|
+
jwt: options.keyAttestationJwt,
|
|
1283
|
+
headerSchema: zKeyAttestationJwtHeader,
|
|
1284
|
+
payloadSchema: zKeyAttestationJwtPayloadForUse(options.use)
|
|
1285
|
+
});
|
|
1286
|
+
const now = options.now?.getTime() ?? Date.now();
|
|
1287
|
+
if (options.nonceExpiresAt && now > options.nonceExpiresAt.getTime()) {
|
|
1288
|
+
throw new Openid4vciError("Nonce used for key attestation jwt expired");
|
|
1289
|
+
}
|
|
1290
|
+
const { signer } = await verifyJwt({
|
|
1291
|
+
compact: options.keyAttestationJwt,
|
|
1292
|
+
header,
|
|
1293
|
+
payload,
|
|
1294
|
+
signer: jwtSignerFromJwt({ header, payload }),
|
|
1295
|
+
verifyJwtCallback: options.callbacks.verifyJwt,
|
|
1296
|
+
errorMessage: "Error verifiying key attestation jwt",
|
|
1297
|
+
expectedNonce: options.expectedNonce,
|
|
1298
|
+
now: options.now
|
|
1299
|
+
});
|
|
1300
|
+
return {
|
|
1301
|
+
header,
|
|
1302
|
+
payload,
|
|
1303
|
+
signer
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// src/formats/proof-type/jwt/jwt-proof-type.ts
|
|
1308
|
+
async function createCredentialRequestJwtProof(options) {
|
|
1309
|
+
const header = parseWithErrorHandling4(zCredentialRequestJwtProofTypeHeader, {
|
|
1310
|
+
...jwtHeaderFromJwtSigner2(options.signer),
|
|
1311
|
+
key_attestation: options.keyAttestationJwt,
|
|
1312
|
+
typ: "openid4vci-proof+jwt"
|
|
1313
|
+
});
|
|
1314
|
+
const payload = parseWithErrorHandling4(zCredentialRequestJwtProofTypePayload, {
|
|
1315
|
+
nonce: options.nonce,
|
|
1316
|
+
aud: options.credentialIssuer,
|
|
1317
|
+
iat: dateToSeconds2(options.issuedAt),
|
|
1318
|
+
iss: options.clientId
|
|
1319
|
+
});
|
|
1320
|
+
const { jwt, signerJwk } = await options.callbacks.signJwt(options.signer, { header, payload });
|
|
1321
|
+
if (options.keyAttestationJwt) {
|
|
1322
|
+
const decodedKeyAttestation = decodeJwt2({
|
|
1323
|
+
jwt: options.keyAttestationJwt,
|
|
1324
|
+
headerSchema: zKeyAttestationJwtHeader,
|
|
1325
|
+
payloadSchema: zKeyAttestationJwtPayload
|
|
1326
|
+
});
|
|
1327
|
+
const isSigedWithAttestedKey = await isJwkInSet({
|
|
1328
|
+
jwk: signerJwk,
|
|
1329
|
+
jwks: decodedKeyAttestation.payload.attested_keys,
|
|
1330
|
+
callbacks: options.callbacks
|
|
1331
|
+
});
|
|
1332
|
+
if (!isSigedWithAttestedKey) {
|
|
1333
|
+
throw new Openid4vciError(
|
|
1334
|
+
`Credential request jwt proof is not signed with a key in the 'key_attestation' jwt payload 'attested_keys'`
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
return jwt;
|
|
1339
|
+
}
|
|
1340
|
+
async function verifyCredentialRequestJwtProof(options) {
|
|
1341
|
+
const { header, payload } = decodeJwt2({
|
|
1342
|
+
jwt: options.jwt,
|
|
1343
|
+
headerSchema: zCredentialRequestJwtProofTypeHeader,
|
|
1344
|
+
payloadSchema: zCredentialRequestJwtProofTypePayload
|
|
1345
|
+
});
|
|
1346
|
+
const now = options.now?.getTime() ?? Date.now();
|
|
1347
|
+
if (options.nonceExpiresAt && now > options.nonceExpiresAt.getTime()) {
|
|
1348
|
+
throw new Openid4vciError("Nonce used for credential request proof expired");
|
|
1349
|
+
}
|
|
1350
|
+
const { signer } = await verifyJwt2({
|
|
1351
|
+
compact: options.jwt,
|
|
1352
|
+
header,
|
|
1353
|
+
payload,
|
|
1354
|
+
signer: jwtSignerFromJwt2({ header, payload }),
|
|
1355
|
+
verifyJwtCallback: options.callbacks.verifyJwt,
|
|
1356
|
+
errorMessage: "Error verifiying credential request proof jwt",
|
|
1357
|
+
expectedNonce: options.expectedNonce,
|
|
1358
|
+
expectedAudience: options.credentialIssuer,
|
|
1359
|
+
expectedIssuer: options.clientId,
|
|
1360
|
+
now: options.now
|
|
1361
|
+
});
|
|
1362
|
+
let keyAttestationResult = void 0;
|
|
1363
|
+
if (header.key_attestation) {
|
|
1364
|
+
keyAttestationResult = await verifyKeyAttestationJwt({
|
|
1365
|
+
callbacks: options.callbacks,
|
|
1366
|
+
keyAttestationJwt: header.key_attestation,
|
|
1367
|
+
use: "proof_type.jwt"
|
|
1368
|
+
});
|
|
1369
|
+
const isSigedWithAttestedKey = await isJwkInSet({
|
|
1370
|
+
jwk: signer.publicJwk,
|
|
1371
|
+
jwks: keyAttestationResult.payload.attested_keys,
|
|
1372
|
+
callbacks: options.callbacks
|
|
1373
|
+
});
|
|
1374
|
+
if (!isSigedWithAttestedKey) {
|
|
1375
|
+
throw new Openid4vciError(
|
|
1376
|
+
`Credential request jwt proof is not signed with a key in the 'key_attestation' jwt payload 'attested_keys'`
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
header,
|
|
1382
|
+
payload,
|
|
1383
|
+
signer,
|
|
1384
|
+
keyAttestation: keyAttestationResult
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
// src/metadata/fetch-issuer-metadata.ts
|
|
1389
|
+
import {
|
|
1390
|
+
Oauth2Error as Oauth2Error5,
|
|
1391
|
+
fetchAuthorizationServerMetadata,
|
|
1392
|
+
zAuthorizationServerMetadata
|
|
1393
|
+
} from "@openid4vc/oauth2";
|
|
1394
|
+
import { parseWithErrorHandling as parseWithErrorHandling5 } from "@openid4vc/utils";
|
|
1395
|
+
async function resolveIssuerMetadata(credentialIssuer, options) {
|
|
1396
|
+
const allowAuthorizationMetadataFromCredentialIssuerMetadata = options?.allowAuthorizationMetadataFromCredentialIssuerMetadata ?? true;
|
|
1397
|
+
const credentialIssuerMetadataWithDraftVersion = await fetchCredentialIssuerMetadata(credentialIssuer, options?.fetch);
|
|
1398
|
+
if (!credentialIssuerMetadataWithDraftVersion) {
|
|
1399
|
+
throw new Oauth2Error5(`Well known credential issuer metadata for issuer '${credentialIssuer}' not found.`);
|
|
1400
|
+
}
|
|
1401
|
+
const { credentialIssuerMetadata, originalDraftVersion } = credentialIssuerMetadataWithDraftVersion;
|
|
1402
|
+
const authorizationServers = credentialIssuerMetadata.authorization_servers ?? [credentialIssuer];
|
|
1403
|
+
const authoriationServersMetadata = [];
|
|
1404
|
+
for (const authorizationServer of authorizationServers) {
|
|
1405
|
+
if (options?.restrictToAuthorizationServers && !options.restrictToAuthorizationServers.includes(authorizationServer)) {
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
let authorizationServerMetadata = await fetchAuthorizationServerMetadata(authorizationServer, options?.fetch);
|
|
1409
|
+
if (!authorizationServerMetadata && authorizationServer === credentialIssuer && allowAuthorizationMetadataFromCredentialIssuerMetadata) {
|
|
1410
|
+
authorizationServerMetadata = parseWithErrorHandling5(
|
|
1411
|
+
zAuthorizationServerMetadata,
|
|
1412
|
+
{
|
|
1413
|
+
token_endpoint: credentialIssuerMetadata.token_endpoint,
|
|
1414
|
+
issuer: credentialIssuer
|
|
1415
|
+
},
|
|
1416
|
+
`Well known authorization server metadata for authorization server '${authorizationServer}' not found, and could also not extract required values from the credential issuer metadata as a fallback.`
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
if (!authorizationServerMetadata) {
|
|
1420
|
+
throw new Oauth2Error5(
|
|
1421
|
+
`Well known openid configuration or authorization server metadata for authorization server '${authorizationServer}' not found.`
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
authoriationServersMetadata.push(authorizationServerMetadata);
|
|
1425
|
+
}
|
|
1426
|
+
return {
|
|
1427
|
+
originalDraftVersion,
|
|
1428
|
+
credentialIssuer: credentialIssuerMetadata,
|
|
1429
|
+
authorizationServers: authoriationServersMetadata
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// src/nonce/nonce-request.ts
|
|
1434
|
+
import { InvalidFetchResponseError as InvalidFetchResponseError2 } from "@openid4vc/oauth2";
|
|
1435
|
+
import { ContentType as ContentType3, ValidationError as ValidationError3, createZodFetcher as createZodFetcher2, parseWithErrorHandling as parseWithErrorHandling6 } from "@openid4vc/utils";
|
|
1436
|
+
|
|
1437
|
+
// src/nonce/z-nonce.ts
|
|
1438
|
+
import { zInteger as zInteger4 } from "@openid4vc/utils";
|
|
1439
|
+
import z18 from "zod";
|
|
1440
|
+
var zNonceResponse = z18.object({
|
|
1441
|
+
c_nonce: z18.string(),
|
|
1442
|
+
c_nonce_expires_in: z18.optional(zInteger4)
|
|
1443
|
+
}).passthrough();
|
|
1444
|
+
|
|
1445
|
+
// src/nonce/nonce-request.ts
|
|
1446
|
+
async function requestNonce(options) {
|
|
1447
|
+
const fetchWithZod = createZodFetcher2(options?.fetch);
|
|
1448
|
+
const nonceEndpoint = options.issuerMetadata.credentialIssuer.nonce_endpoint;
|
|
1449
|
+
if (!nonceEndpoint) {
|
|
1450
|
+
throw new Openid4vciError(
|
|
1451
|
+
`Credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}' does not have a nonce endpoint.`
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
const { response, result } = await fetchWithZod(zNonceResponse, ContentType3.Json, nonceEndpoint, {
|
|
1455
|
+
method: "POST"
|
|
1456
|
+
});
|
|
1457
|
+
if (!response.ok || !result) {
|
|
1458
|
+
throw new InvalidFetchResponseError2(
|
|
1459
|
+
`Requesting nonce from '${nonceEndpoint}' resulted in an unsuccesfull response with status '${response.status}'`,
|
|
1460
|
+
await response.clone().text(),
|
|
1461
|
+
response
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
if (!result.success) {
|
|
1465
|
+
throw new ValidationError3("Error parsing nonce response", result.error);
|
|
1466
|
+
}
|
|
1467
|
+
return result.data;
|
|
1468
|
+
}
|
|
1469
|
+
function createNonceResponse(options) {
|
|
1470
|
+
return parseWithErrorHandling6(zNonceResponse, {
|
|
1471
|
+
c_nonce: options.cNonce,
|
|
1472
|
+
c_nonce_expires_in: options.cNonceExpiresIn,
|
|
1473
|
+
...options.additionalPayload
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/notification/notification.ts
|
|
1478
|
+
import {
|
|
1479
|
+
Oauth2Error as Oauth2Error6,
|
|
1480
|
+
resourceRequest as resourceRequest2
|
|
1481
|
+
} from "@openid4vc/oauth2";
|
|
1482
|
+
import { ContentType as ContentType4, isResponseContentType as isResponseContentType2, parseWithErrorHandling as parseWithErrorHandling7 } from "@openid4vc/utils";
|
|
1483
|
+
|
|
1484
|
+
// src/notification/z-notification.ts
|
|
1485
|
+
import z19 from "zod";
|
|
1486
|
+
var zNotificationEvent = z19.enum(["credential_accepted", "credential_failure", "credential_deleted"]);
|
|
1487
|
+
var zNotificationRequest = z19.object({
|
|
1488
|
+
notification_id: z19.string(),
|
|
1489
|
+
event: zNotificationEvent,
|
|
1490
|
+
event_description: z19.optional(z19.string())
|
|
1491
|
+
}).passthrough();
|
|
1492
|
+
var zNotificationErrorResponse = z19.object({
|
|
1493
|
+
error: z19.enum(["invalid_notification_id", "invalid_notification_request"])
|
|
1494
|
+
}).passthrough();
|
|
1495
|
+
|
|
1496
|
+
// src/notification/notification.ts
|
|
1497
|
+
async function sendNotifcation(options) {
|
|
1498
|
+
const notificationEndpoint = options.issuerMetadata.credentialIssuer.notification_endpoint;
|
|
1499
|
+
if (!notificationEndpoint) {
|
|
1500
|
+
throw new Oauth2Error6(
|
|
1501
|
+
`Credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}' does not have a notification endpiont configured.`
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
const notificationRequest = parseWithErrorHandling7(
|
|
1505
|
+
zNotificationRequest,
|
|
1506
|
+
{
|
|
1507
|
+
event: options.notification.event,
|
|
1508
|
+
notification_id: options.notification.notificationId,
|
|
1509
|
+
event_description: options.notification.eventDescription
|
|
1510
|
+
},
|
|
1511
|
+
"Error validating notification request"
|
|
1512
|
+
);
|
|
1513
|
+
const resourceResponse = await resourceRequest2({
|
|
1514
|
+
dpop: options.dpop,
|
|
1515
|
+
accessToken: options.accessToken,
|
|
1516
|
+
callbacks: options.callbacks,
|
|
1517
|
+
url: notificationEndpoint,
|
|
1518
|
+
requestOptions: {
|
|
1519
|
+
method: "POST",
|
|
1520
|
+
headers: {
|
|
1521
|
+
"Content-Type": ContentType4.Json
|
|
1522
|
+
},
|
|
1523
|
+
body: JSON.stringify(notificationRequest)
|
|
1524
|
+
}
|
|
1525
|
+
});
|
|
1526
|
+
if (!resourceResponse.ok) {
|
|
1527
|
+
const notificationErrorResponseResult = isResponseContentType2(ContentType4.Json, resourceResponse.response) ? zNotificationErrorResponse.safeParse(await resourceResponse.response.clone().json()) : void 0;
|
|
1528
|
+
return {
|
|
1529
|
+
...resourceResponse,
|
|
1530
|
+
notificationErrorResponseResult
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
return resourceResponse;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
// src/Openid4vciClient.ts
|
|
1537
|
+
var AuthorizationFlow = /* @__PURE__ */ ((AuthorizationFlow2) => {
|
|
1538
|
+
AuthorizationFlow2["Oauth2Redirect"] = "Oauth2Redirect";
|
|
1539
|
+
AuthorizationFlow2["PresentationDuringIssuance"] = "PresentationDuringIssuance";
|
|
1540
|
+
return AuthorizationFlow2;
|
|
1541
|
+
})(AuthorizationFlow || {});
|
|
1542
|
+
var Openid4vciClient = class {
|
|
1543
|
+
constructor(options) {
|
|
1544
|
+
this.options = options;
|
|
1545
|
+
this.oauth2Client = new Oauth2Client({
|
|
1546
|
+
callbacks: this.options.callbacks
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
getKnownCredentialConfigurationsSupported(credentialIssuerMetadata) {
|
|
1550
|
+
return extractKnownCredentialConfigurationSupportedFormats(
|
|
1551
|
+
credentialIssuerMetadata.credential_configurations_supported
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Resolve a credential offer into a credential offer object, handling both
|
|
1556
|
+
* 'credential_offer' and 'credential_offer_uri' params.
|
|
1557
|
+
*/
|
|
1558
|
+
async resolveCredentialOffer(credentialOffer) {
|
|
1559
|
+
return resolveCredentialOffer(credentialOffer, {
|
|
1560
|
+
fetch: this.options.callbacks.fetch
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
async resolveIssuerMetadata(credentialIssuer) {
|
|
1564
|
+
return resolveIssuerMetadata(credentialIssuer, {
|
|
1565
|
+
fetch: this.options.callbacks.fetch
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Retrieve an authorization code for a presentation during issuance session
|
|
1570
|
+
*
|
|
1571
|
+
* This can only be called if an authorization challenge was performed before and returned a
|
|
1572
|
+
* `presentation` paramater along with an `auth_session`. If the presentation response included
|
|
1573
|
+
* an `presentation_during_issuance_session` parameter it MUST be included in this request as well.
|
|
1574
|
+
*/
|
|
1575
|
+
async retrieveAuthorizationCodeUsingPresentation(options) {
|
|
1576
|
+
if (!options.credentialOffer.grants?.[authorizationCodeGrantIdentifier2]) {
|
|
1577
|
+
throw new Oauth2Error7(`Provided credential offer does not include the 'authorization_code' grant.`);
|
|
1578
|
+
}
|
|
1579
|
+
const authorizationCodeGrant = options.credentialOffer.grants[authorizationCodeGrantIdentifier2];
|
|
1580
|
+
const authorizationServer = determineAuthorizationServerForCredentialOffer({
|
|
1581
|
+
issuerMetadata: options.issuerMetadata,
|
|
1582
|
+
grantAuthorizationServer: authorizationCodeGrant.authorization_server
|
|
1583
|
+
});
|
|
1584
|
+
const authorizationServerMetadata = getAuthorizationServerMetadataFromList2(
|
|
1585
|
+
options.issuerMetadata.authorizationServers,
|
|
1586
|
+
authorizationServer
|
|
1587
|
+
);
|
|
1588
|
+
const oauth2Client = new Oauth2Client({ callbacks: this.options.callbacks });
|
|
1589
|
+
const { authorizationChallengeResponse, dpop } = await oauth2Client.sendAuthorizationChallengeRequest({
|
|
1590
|
+
authorizationServerMetadata,
|
|
1591
|
+
authSession: options.authSession,
|
|
1592
|
+
presentationDuringIssuanceSession: options.presentationDuringIssuanceSession,
|
|
1593
|
+
dpop: options.dpop
|
|
1594
|
+
});
|
|
1595
|
+
return { authorizationChallengeResponse, dpop };
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* Initiates authorization for credential issuance. It handles the following cases:
|
|
1599
|
+
* - Authorization Challenge
|
|
1600
|
+
* - Pushed Authorization Request
|
|
1601
|
+
* - Regular Authorization url
|
|
1602
|
+
*
|
|
1603
|
+
* In case the authorization challenge request returns an error with `insufficient_authorization`
|
|
1604
|
+
* with a `presentation` field it means the authorization server expects presentation of credentials
|
|
1605
|
+
* before issuance of crednetials. If this is the case, the value in `presentation` should be treated
|
|
1606
|
+
* as an openid4vp authorization request and submitted to the verifier. Once the presentation response
|
|
1607
|
+
* has been submitted, the RP will respnosd with a `presentation_during_issuance_session` parameter.
|
|
1608
|
+
* Together with the `auth_session` parameter returned in this call you can retrieve an `authorization_code`
|
|
1609
|
+
* using
|
|
1610
|
+
*/
|
|
1611
|
+
async initiateAuthorization(options) {
|
|
1612
|
+
if (!options.credentialOffer.grants?.[authorizationCodeGrantIdentifier2]) {
|
|
1613
|
+
throw new Oauth2Error7(`Provided credential offer does not include the 'authorization_code' grant.`);
|
|
1614
|
+
}
|
|
1615
|
+
const authorizationCodeGrant = options.credentialOffer.grants[authorizationCodeGrantIdentifier2];
|
|
1616
|
+
const authorizationServer = determineAuthorizationServerForCredentialOffer({
|
|
1617
|
+
issuerMetadata: options.issuerMetadata,
|
|
1618
|
+
grantAuthorizationServer: authorizationCodeGrant.authorization_server
|
|
1619
|
+
});
|
|
1620
|
+
const authorizationServerMetadata = getAuthorizationServerMetadataFromList2(
|
|
1621
|
+
options.issuerMetadata.authorizationServers,
|
|
1622
|
+
authorizationServer
|
|
1623
|
+
);
|
|
1624
|
+
const oauth2Client = new Oauth2Client({ callbacks: this.options.callbacks });
|
|
1625
|
+
try {
|
|
1626
|
+
const result = await oauth2Client.initiateAuthorization({
|
|
1627
|
+
clientId: options.clientId,
|
|
1628
|
+
pkceCodeVerifier: options.pkceCodeVerifier,
|
|
1629
|
+
redirectUri: options.redirectUri,
|
|
1630
|
+
scope: options.scope,
|
|
1631
|
+
additionalRequestPayload: {
|
|
1632
|
+
...options.additionalRequestPayload,
|
|
1633
|
+
issuer_state: options.credentialOffer?.grants?.authorization_code?.issuer_state
|
|
1634
|
+
},
|
|
1635
|
+
dpop: options.dpop,
|
|
1636
|
+
clientAttestation: options.clientAttestation,
|
|
1637
|
+
resource: options.issuerMetadata.credentialIssuer.credential_issuer,
|
|
1638
|
+
authorizationServerMetadata
|
|
1639
|
+
});
|
|
1640
|
+
return {
|
|
1641
|
+
...result,
|
|
1642
|
+
authorizationFlow: "Oauth2Redirect" /* Oauth2Redirect */,
|
|
1643
|
+
authorizationServer: authorizationServerMetadata.issuer
|
|
1644
|
+
};
|
|
1645
|
+
} catch (error) {
|
|
1646
|
+
if (error instanceof Oauth2ClientAuthorizationChallengeError && error.errorResponse.error === Oauth2ErrorCodes2.InsufficientAuthorization && error.errorResponse.presentation) {
|
|
1647
|
+
if (!error.errorResponse.auth_session) {
|
|
1648
|
+
throw new Openid4vciError(
|
|
1649
|
+
`Expected 'auth_session' to be defined with authorization challenge response error '${error.errorResponse.error}' and 'presentation' parameter`
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
return {
|
|
1653
|
+
authorizationFlow: "PresentationDuringIssuance" /* PresentationDuringIssuance */,
|
|
1654
|
+
openid4vpRequestUrl: error.errorResponse.presentation,
|
|
1655
|
+
authSession: error.errorResponse.auth_session,
|
|
1656
|
+
authorizationServer: authorizationServerMetadata.issuer
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
throw error;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Convenience method around {@link Oauth2Client.createAuthorizationRequestUrl}
|
|
1664
|
+
* but specifically focused on a credential offer
|
|
1665
|
+
*/
|
|
1666
|
+
async createAuthorizationRequestUrlFromOffer(options) {
|
|
1667
|
+
if (!options.credentialOffer.grants?.[authorizationCodeGrantIdentifier2]) {
|
|
1668
|
+
throw new Oauth2Error7(`Provided credential offer does not include the 'authorization_code' grant.`);
|
|
1669
|
+
}
|
|
1670
|
+
const authorizationCodeGrant = options.credentialOffer.grants[authorizationCodeGrantIdentifier2];
|
|
1671
|
+
const authorizationServer = determineAuthorizationServerForCredentialOffer({
|
|
1672
|
+
issuerMetadata: options.issuerMetadata,
|
|
1673
|
+
grantAuthorizationServer: authorizationCodeGrant.authorization_server
|
|
1674
|
+
});
|
|
1675
|
+
const authorizationServerMetadata = getAuthorizationServerMetadataFromList2(
|
|
1676
|
+
options.issuerMetadata.authorizationServers,
|
|
1677
|
+
authorizationServer
|
|
1678
|
+
);
|
|
1679
|
+
const { authorizationRequestUrl, pkce, dpop } = await this.oauth2Client.createAuthorizationRequestUrl({
|
|
1680
|
+
authorizationServerMetadata,
|
|
1681
|
+
clientId: options.clientId,
|
|
1682
|
+
additionalRequestPayload: {
|
|
1683
|
+
...options.additionalRequestPayload,
|
|
1684
|
+
issuer_state: options.credentialOffer?.grants?.authorization_code?.issuer_state
|
|
1685
|
+
},
|
|
1686
|
+
resource: options.issuerMetadata.credentialIssuer.credential_issuer,
|
|
1687
|
+
redirectUri: options.redirectUri,
|
|
1688
|
+
scope: options.scope,
|
|
1689
|
+
pkceCodeVerifier: options.pkceCodeVerifier,
|
|
1690
|
+
clientAttestation: options.clientAttestation,
|
|
1691
|
+
dpop: options.dpop
|
|
1692
|
+
});
|
|
1693
|
+
return {
|
|
1694
|
+
authorizationRequestUrl,
|
|
1695
|
+
pkce,
|
|
1696
|
+
dpop,
|
|
1697
|
+
authorizationServer: authorizationServerMetadata.issuer
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Convenience method around {@link Oauth2Client.retrievePreAuthorizedCodeAccessToken}
|
|
1702
|
+
* but specifically focused on a credential offer
|
|
1703
|
+
*/
|
|
1704
|
+
async retrievePreAuthorizedCodeAccessTokenFromOffer({
|
|
1705
|
+
credentialOffer,
|
|
1706
|
+
issuerMetadata,
|
|
1707
|
+
additionalRequestPayload,
|
|
1708
|
+
txCode,
|
|
1709
|
+
dpop,
|
|
1710
|
+
clientAttestation
|
|
1711
|
+
}) {
|
|
1712
|
+
if (!credentialOffer.grants?.[preAuthorizedCodeGrantIdentifier3]) {
|
|
1713
|
+
throw new Oauth2Error7(`The credential offer does not contain the '${preAuthorizedCodeGrantIdentifier3}' grant.`);
|
|
1714
|
+
}
|
|
1715
|
+
if (credentialOffer.grants[preAuthorizedCodeGrantIdentifier3].tx_code && !txCode) {
|
|
1716
|
+
throw new Oauth2Error7(
|
|
1717
|
+
`Retrieving access token requires a 'tx_code' in the request, but the 'txCode' parameter was not provided.`
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
const preAuthorizedCode = credentialOffer.grants[preAuthorizedCodeGrantIdentifier3]["pre-authorized_code"];
|
|
1721
|
+
const authorizationServer = determineAuthorizationServerForCredentialOffer({
|
|
1722
|
+
grantAuthorizationServer: credentialOffer.grants[preAuthorizedCodeGrantIdentifier3].authorization_server,
|
|
1723
|
+
issuerMetadata
|
|
1724
|
+
});
|
|
1725
|
+
const authorizationServerMetadata = getAuthorizationServerMetadataFromList2(
|
|
1726
|
+
issuerMetadata.authorizationServers,
|
|
1727
|
+
authorizationServer
|
|
1728
|
+
);
|
|
1729
|
+
const result = await this.oauth2Client.retrievePreAuthorizedCodeAccessToken({
|
|
1730
|
+
authorizationServerMetadata,
|
|
1731
|
+
preAuthorizedCode,
|
|
1732
|
+
txCode,
|
|
1733
|
+
resource: issuerMetadata.credentialIssuer.credential_issuer,
|
|
1734
|
+
additionalRequestPayload,
|
|
1735
|
+
dpop,
|
|
1736
|
+
clientAttestation
|
|
1737
|
+
});
|
|
1738
|
+
return {
|
|
1739
|
+
...result,
|
|
1740
|
+
authorizationServer
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* Convenience method around {@link Oauth2Client.retrieveAuthorizationCodeAccessTokenFrom}
|
|
1745
|
+
* but specifically focused on a credential offer
|
|
1746
|
+
*/
|
|
1747
|
+
async retrieveAuthorizationCodeAccessTokenFromOffer({
|
|
1748
|
+
issuerMetadata,
|
|
1749
|
+
additionalRequestPayload,
|
|
1750
|
+
credentialOffer,
|
|
1751
|
+
authorizationCode,
|
|
1752
|
+
pkceCodeVerifier,
|
|
1753
|
+
redirectUri,
|
|
1754
|
+
dpop,
|
|
1755
|
+
clientAttestation
|
|
1756
|
+
}) {
|
|
1757
|
+
if (!credentialOffer.grants?.[authorizationCodeGrantIdentifier2]) {
|
|
1758
|
+
throw new Oauth2Error7(`The credential offer does not contain the '${authorizationCodeGrantIdentifier2}' grant.`);
|
|
1759
|
+
}
|
|
1760
|
+
const authorizationServer = determineAuthorizationServerForCredentialOffer({
|
|
1761
|
+
grantAuthorizationServer: credentialOffer.grants[authorizationCodeGrantIdentifier2].authorization_server,
|
|
1762
|
+
issuerMetadata
|
|
1763
|
+
});
|
|
1764
|
+
const authorizationServerMetadata = getAuthorizationServerMetadataFromList2(
|
|
1765
|
+
issuerMetadata.authorizationServers,
|
|
1766
|
+
authorizationServer
|
|
1767
|
+
);
|
|
1768
|
+
const result = await this.oauth2Client.retrieveAuthorizationCodeAccessToken({
|
|
1769
|
+
authorizationServerMetadata,
|
|
1770
|
+
authorizationCode,
|
|
1771
|
+
pkceCodeVerifier,
|
|
1772
|
+
additionalRequestPayload,
|
|
1773
|
+
dpop,
|
|
1774
|
+
clientAttestation,
|
|
1775
|
+
redirectUri,
|
|
1776
|
+
resource: issuerMetadata.credentialIssuer.credential_issuer
|
|
1777
|
+
});
|
|
1778
|
+
return {
|
|
1779
|
+
...result,
|
|
1780
|
+
authorizationServer
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Request a nonce to be used in credential request proofs from the `nonce_endpoint`
|
|
1785
|
+
*
|
|
1786
|
+
* @throws Openid4vciError - if no `nonce_endpoint` is configured in the issuer metadata
|
|
1787
|
+
* @thrwos InvalidFetchResponseError - if the nonce endpoint did not return a succesfull response
|
|
1788
|
+
* @throws ValidationError - if validating the nonce response failed
|
|
1789
|
+
*/
|
|
1790
|
+
async requestNonce(options) {
|
|
1791
|
+
return requestNonce(options);
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Creates the jwt proof payload and header to be included in a credential request.
|
|
1795
|
+
*/
|
|
1796
|
+
async createCredentialRequestJwtProof(options) {
|
|
1797
|
+
const credentialConfiguration = options.issuerMetadata.credentialIssuer.credential_configurations_supported[options.credentialConfigurationId];
|
|
1798
|
+
if (!credentialConfiguration) {
|
|
1799
|
+
throw new Openid4vciError(
|
|
1800
|
+
`Credential configuration with '${options.credentialConfigurationId}' not found in 'credential_configurations_supported' from credential issuer '${options.issuerMetadata.credentialIssuer.credential_issuer}'`
|
|
1801
|
+
);
|
|
1802
|
+
}
|
|
1803
|
+
if (credentialConfiguration.proof_types_supported) {
|
|
1804
|
+
if (!credentialConfiguration.proof_types_supported.jwt) {
|
|
1805
|
+
throw new Openid4vciError(
|
|
1806
|
+
`Credential configuration with id '${options.credentialConfigurationId}' does not support the 'jwt' proof type.`
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
if (!credentialConfiguration.proof_types_supported.jwt.proof_signing_alg_values_supported.includes(
|
|
1810
|
+
options.signer.alg
|
|
1811
|
+
)) {
|
|
1812
|
+
throw new Openid4vciError(
|
|
1813
|
+
`Credential configuration with id '${options.credentialConfigurationId}' does not support the '${options.signer.alg}' alg for 'jwt' proof type.`
|
|
1814
|
+
);
|
|
1815
|
+
}
|
|
1816
|
+
if (credentialConfiguration.proof_types_supported.jwt.key_attestations_required && !options.keyAttestationJwt) {
|
|
1817
|
+
throw new Openid4vciError(
|
|
1818
|
+
`Credential configuration with id '${options.credentialConfigurationId}' requires key attestations for 'jwt' proof type but no 'keyAttestationJwt' was provided`
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
const jwt = await createCredentialRequestJwtProof({
|
|
1823
|
+
credentialIssuer: options.issuerMetadata.credentialIssuer.credential_issuer,
|
|
1824
|
+
signer: options.signer,
|
|
1825
|
+
clientId: options.clientId,
|
|
1826
|
+
issuedAt: options.issuedAt,
|
|
1827
|
+
nonce: options.nonce,
|
|
1828
|
+
keyAttestationJwt: options.keyAttestationJwt,
|
|
1829
|
+
callbacks: this.options.callbacks
|
|
1830
|
+
});
|
|
1831
|
+
return {
|
|
1832
|
+
jwt
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* @throws Openid4vciRetrieveCredentialsError - if an unsuccesfull response or the respnose couldn't be parsed as credential response
|
|
1837
|
+
* @throws ValidationError - if validation of the credential request failed
|
|
1838
|
+
* @throws Openid4vciError - if the `credentialConfigurationId` couldn't be found, or if the the format specific request couldn't be constructed
|
|
1839
|
+
*/
|
|
1840
|
+
async retrieveCredentials({
|
|
1841
|
+
issuerMetadata,
|
|
1842
|
+
proof,
|
|
1843
|
+
proofs,
|
|
1844
|
+
credentialConfigurationId,
|
|
1845
|
+
additionalRequestPayload,
|
|
1846
|
+
accessToken,
|
|
1847
|
+
dpop
|
|
1848
|
+
}) {
|
|
1849
|
+
const formatPayload = getCredentialRequestFormatPayloadForCredentialConfigurationId({
|
|
1850
|
+
credentialConfigurationId,
|
|
1851
|
+
issuerMetadata
|
|
1852
|
+
});
|
|
1853
|
+
const credentialResponse = await retrieveCredentialsWithFormat({
|
|
1854
|
+
accessToken,
|
|
1855
|
+
formatPayload,
|
|
1856
|
+
issuerMetadata,
|
|
1857
|
+
additionalRequestPayload,
|
|
1858
|
+
proof,
|
|
1859
|
+
proofs,
|
|
1860
|
+
callbacks: this.options.callbacks,
|
|
1861
|
+
dpop
|
|
1862
|
+
});
|
|
1863
|
+
if (!credentialResponse.ok) {
|
|
1864
|
+
throw new Openid4vciRetrieveCredentialsError(
|
|
1865
|
+
`Error retrieving credentials from '${issuerMetadata.credentialIssuer.credential_issuer}'`,
|
|
1866
|
+
credentialResponse,
|
|
1867
|
+
await credentialResponse.response.clone().text()
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
return credentialResponse;
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* @throws Openid4vciSendNotificationError - if an unsuccesfull response
|
|
1874
|
+
* @throws ValidationError - if validation of the notification request failed
|
|
1875
|
+
*/
|
|
1876
|
+
async sendNotification({
|
|
1877
|
+
issuerMetadata,
|
|
1878
|
+
notification,
|
|
1879
|
+
additionalRequestPayload,
|
|
1880
|
+
accessToken,
|
|
1881
|
+
dpop
|
|
1882
|
+
}) {
|
|
1883
|
+
const notificationResponse = await sendNotifcation({
|
|
1884
|
+
accessToken,
|
|
1885
|
+
issuerMetadata,
|
|
1886
|
+
additionalRequestPayload,
|
|
1887
|
+
callbacks: this.options.callbacks,
|
|
1888
|
+
dpop,
|
|
1889
|
+
notification
|
|
1890
|
+
});
|
|
1891
|
+
if (!notificationResponse.ok) {
|
|
1892
|
+
throw new Openid4vciSendNotificationError(
|
|
1893
|
+
`Error sending notification to '${issuerMetadata.credentialIssuer.credential_issuer}'`,
|
|
1894
|
+
notificationResponse
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
return notificationResponse;
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1901
|
+
// src/Openid4vciIssuer.ts
|
|
1902
|
+
import {
|
|
1903
|
+
Oauth2ErrorCodes as Oauth2ErrorCodes3,
|
|
1904
|
+
Oauth2JwtVerificationError,
|
|
1905
|
+
Oauth2ServerErrorResponseError
|
|
1906
|
+
} from "@openid4vc/oauth2";
|
|
1907
|
+
import { ValidationError as ValidationError4, parseWithErrorHandling as parseWithErrorHandling10 } from "@openid4vc/utils";
|
|
1908
|
+
|
|
1909
|
+
// src/credential-request/credential-response.ts
|
|
1910
|
+
import { parseWithErrorHandling as parseWithErrorHandling8 } from "@openid4vc/utils";
|
|
1911
|
+
function createCredentialResponse(options) {
|
|
1912
|
+
const credentialResponse = parseWithErrorHandling8(zCredentialResponse, {
|
|
1913
|
+
c_nonce: options.cNonce,
|
|
1914
|
+
c_nonce_expires_in: options.cNonceExpiresInSeconds,
|
|
1915
|
+
credential: options.credential,
|
|
1916
|
+
credentials: options.credentials,
|
|
1917
|
+
notification_id: options.notificationId,
|
|
1918
|
+
// NOTE `format` is removed in draft 13. For now if a format was requested
|
|
1919
|
+
// we just always return it in the response as well.
|
|
1920
|
+
format: options.credentialRequest.format?.format,
|
|
1921
|
+
...options.additionalPayload
|
|
1922
|
+
});
|
|
1923
|
+
return credentialResponse;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// src/credential-request/parse-credential-request.ts
|
|
1927
|
+
import { parseWithErrorHandling as parseWithErrorHandling9 } from "@openid4vc/utils";
|
|
1928
|
+
import z20 from "zod";
|
|
1929
|
+
function parseCredentialRequest(options) {
|
|
1930
|
+
const credentialRequest = parseWithErrorHandling9(
|
|
1931
|
+
zCredentialRequest,
|
|
1932
|
+
options.credentialRequest,
|
|
1933
|
+
"Error validating credential request"
|
|
1934
|
+
);
|
|
1935
|
+
let proofs = void 0;
|
|
1936
|
+
const knownProofs = zCredentialRequestProofs.strict().safeParse(credentialRequest.proofs);
|
|
1937
|
+
if (knownProofs.success) {
|
|
1938
|
+
proofs = knownProofs.data;
|
|
1939
|
+
}
|
|
1940
|
+
const knownProof = z20.union(allCredentialRequestProofs).safeParse(credentialRequest.proof);
|
|
1941
|
+
if (knownProof.success && knownProof.data.proof_type === jwtProofTypeIdentifier) {
|
|
1942
|
+
proofs = { [jwtProofTypeIdentifier]: [knownProof.data.jwt] };
|
|
1943
|
+
} else if (knownProof.success && knownProof.data.proof_type === attestationProofTypeIdentifier) {
|
|
1944
|
+
proofs = { [attestationProofTypeIdentifier]: [knownProof.data.attestation] };
|
|
1945
|
+
}
|
|
1946
|
+
if (credentialRequest.credential_identifier) {
|
|
1947
|
+
return {
|
|
1948
|
+
credentialIdentifier: credentialRequest.credential_identifier,
|
|
1949
|
+
credentialRequest,
|
|
1950
|
+
proofs
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
if (credentialRequest.format && allCredentialRequestFormatIdentifiers.includes(credentialRequest.format)) {
|
|
1954
|
+
return {
|
|
1955
|
+
// Removes all claims that are not specific to this format
|
|
1956
|
+
format: parseWithErrorHandling9(
|
|
1957
|
+
z20.union(allCredentialRequestFormats),
|
|
1958
|
+
credentialRequest,
|
|
1959
|
+
"Unable to validate format specific properties from credential request"
|
|
1960
|
+
),
|
|
1961
|
+
credentialRequest,
|
|
1962
|
+
proofs
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
return {
|
|
1966
|
+
credentialRequest,
|
|
1967
|
+
proofs
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
// src/formats/proof-type/attestation/attestation-proof-type.ts
|
|
1972
|
+
async function verifyCredentialRequestAttestationProof(options) {
|
|
1973
|
+
const verificationResult = await verifyKeyAttestationJwt({
|
|
1974
|
+
...options,
|
|
1975
|
+
use: "proof_type.attestation"
|
|
1976
|
+
});
|
|
1977
|
+
return verificationResult;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// src/Openid4vciIssuer.ts
|
|
1981
|
+
var Openid4vciIssuer = class {
|
|
1982
|
+
constructor(options) {
|
|
1983
|
+
this.options = options;
|
|
1984
|
+
}
|
|
1985
|
+
getCredentialIssuerMetadataDraft11(credentialIssuerMetadata) {
|
|
1986
|
+
return parseWithErrorHandling10(zCredentialIssuerMetadataWithDraft11, credentialIssuerMetadata);
|
|
1987
|
+
}
|
|
1988
|
+
getKnownCredentialConfigurationsSupported(credentialIssuerMetadata) {
|
|
1989
|
+
return extractKnownCredentialConfigurationSupportedFormats(
|
|
1990
|
+
credentialIssuerMetadata.credential_configurations_supported
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Create issuer metadata and validates the structure is correct
|
|
1995
|
+
*/
|
|
1996
|
+
createCredentialIssuerMetadata(credentialIssuerMetadata) {
|
|
1997
|
+
return parseWithErrorHandling10(
|
|
1998
|
+
zCredentialIssuerMetadata,
|
|
1999
|
+
credentialIssuerMetadata,
|
|
2000
|
+
"Error validating credential issuer metadata"
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
2003
|
+
async createCredentialOffer(options) {
|
|
2004
|
+
return createCredentialOffer({
|
|
2005
|
+
callbacks: this.options.callbacks,
|
|
2006
|
+
credentialConfigurationIds: options.credentialConfigurationIds,
|
|
2007
|
+
grants: options.grants,
|
|
2008
|
+
issuerMetadata: options.issuerMetadata,
|
|
2009
|
+
additionalPayload: options.additionalPayload,
|
|
2010
|
+
credentialOfferScheme: options.credentialOfferScheme,
|
|
2011
|
+
credentialOfferUri: options.credentialOfferUri
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* @throws Oauth2ServerErrorResponseError - if verification of the jwt failed. You can extract
|
|
2016
|
+
* the credential error response from this.
|
|
2017
|
+
*/
|
|
2018
|
+
async verifyCredentialRequestJwtProof(options) {
|
|
2019
|
+
try {
|
|
2020
|
+
return await verifyCredentialRequestJwtProof({
|
|
2021
|
+
callbacks: this.options.callbacks,
|
|
2022
|
+
credentialIssuer: options.issuerMetadata.credentialIssuer.credential_issuer,
|
|
2023
|
+
expectedNonce: options.expectedNonce,
|
|
2024
|
+
nonceExpiresAt: options.nonceExpiresAt,
|
|
2025
|
+
jwt: options.jwt,
|
|
2026
|
+
clientId: options.clientId,
|
|
2027
|
+
now: options.now
|
|
2028
|
+
});
|
|
2029
|
+
} catch (error) {
|
|
2030
|
+
throw new Oauth2ServerErrorResponseError(
|
|
2031
|
+
{
|
|
2032
|
+
error: Oauth2ErrorCodes3.InvalidProof,
|
|
2033
|
+
error_description: (
|
|
2034
|
+
// TOOD: error should have a internalErrorMessage and a publicErrorMessage
|
|
2035
|
+
error instanceof Oauth2JwtVerificationError || error instanceof Openid4vciError ? error.message : "Invalid proof"
|
|
2036
|
+
)
|
|
2037
|
+
},
|
|
2038
|
+
{
|
|
2039
|
+
internalMessage: "Error verifying credential request proof jwt",
|
|
2040
|
+
cause: error
|
|
2041
|
+
}
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* @throws Oauth2ServerErrorResponseError - if verification of the key attestation failed. You can extract
|
|
2047
|
+
* the credential error response from this.
|
|
2048
|
+
*/
|
|
2049
|
+
async verifyCredentialRequestAttestationProof(options) {
|
|
2050
|
+
try {
|
|
2051
|
+
return await verifyCredentialRequestAttestationProof({
|
|
2052
|
+
callbacks: this.options.callbacks,
|
|
2053
|
+
expectedNonce: options.expectedNonce,
|
|
2054
|
+
keyAttestationJwt: options.keyAttestationJwt,
|
|
2055
|
+
nonceExpiresAt: options.nonceExpiresAt,
|
|
2056
|
+
now: options.now
|
|
2057
|
+
});
|
|
2058
|
+
} catch (error) {
|
|
2059
|
+
throw new Oauth2ServerErrorResponseError(
|
|
2060
|
+
{
|
|
2061
|
+
error: Oauth2ErrorCodes3.InvalidProof,
|
|
2062
|
+
error_description: (
|
|
2063
|
+
// TOOD: error should have a internalErrorMessage and a publicErrorMessage
|
|
2064
|
+
error instanceof Oauth2JwtVerificationError || error instanceof Openid4vciError ? error.message : "Invalid proof"
|
|
2065
|
+
)
|
|
2066
|
+
},
|
|
2067
|
+
{
|
|
2068
|
+
internalMessage: "Error verifying credential request proof attestation",
|
|
2069
|
+
cause: error
|
|
2070
|
+
}
|
|
2071
|
+
);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* @throws Oauth2ServerErrorResponseError - when validation of the credential request fails
|
|
2076
|
+
* You can extract the credential error response from this.
|
|
2077
|
+
*/
|
|
2078
|
+
parseCredentialRequest(options) {
|
|
2079
|
+
try {
|
|
2080
|
+
return parseCredentialRequest(options);
|
|
2081
|
+
} catch (error) {
|
|
2082
|
+
throw new Oauth2ServerErrorResponseError(
|
|
2083
|
+
{
|
|
2084
|
+
error: Oauth2ErrorCodes3.InvalidCredentialRequest,
|
|
2085
|
+
error_description: (
|
|
2086
|
+
// TODO: error should have a internalErrorMessage and a publicErrorMessage
|
|
2087
|
+
error instanceof ValidationError4 ? error.message : "Invalid request"
|
|
2088
|
+
)
|
|
2089
|
+
},
|
|
2090
|
+
{
|
|
2091
|
+
internalMessage: "Error verifying credential request proof jwt",
|
|
2092
|
+
cause: error
|
|
2093
|
+
}
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* @throws ValidationError - when validation of the credential response fails
|
|
2099
|
+
*/
|
|
2100
|
+
createCredentialResponse(options) {
|
|
2101
|
+
return createCredentialResponse(options);
|
|
2102
|
+
}
|
|
2103
|
+
/**
|
|
2104
|
+
* @throws ValidationError - when validation of the nonce response fails
|
|
2105
|
+
*/
|
|
2106
|
+
createNonceResponse(options) {
|
|
2107
|
+
return createNonceResponse(options);
|
|
2108
|
+
}
|
|
2109
|
+
};
|
|
2110
|
+
export {
|
|
2111
|
+
AuthorizationFlow,
|
|
2112
|
+
Openid4vciClient,
|
|
2113
|
+
Openid4vciDraftVersion,
|
|
2114
|
+
Openid4vciError,
|
|
2115
|
+
Openid4vciIssuer,
|
|
2116
|
+
Openid4vciRetrieveCredentialsError,
|
|
2117
|
+
Openid4vciSendNotificationError,
|
|
2118
|
+
credentialsSupportedToCredentialConfigurationsSupported,
|
|
2119
|
+
extractScopesForCredentialConfigurationIds,
|
|
2120
|
+
getCredentialConfigurationsMatchingRequestFormat,
|
|
2121
|
+
getGlobalConfig2 as getGlobalConfig,
|
|
2122
|
+
setGlobalConfig
|
|
2123
|
+
};
|
|
2124
|
+
//# sourceMappingURL=index.mjs.map
|