@openid4vc/openid4vp 0.3.0-alpha-20251001121503 → 0.3.0-alpha-20251017082202

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/dist/index.mjs CHANGED
@@ -1,2180 +1,1768 @@
1
- // src/client-identifier-prefix/parse-client-identifier-prefix.ts
2
- import { Oauth2ErrorCodes, Oauth2ServerErrorResponseError } from "@openid4vc/oauth2";
3
- import { HashAlgorithm } from "@openid4vc/oauth2";
4
- import { URL as URL2, decodeBase64, encodeToBase64Url, zHttpsUrl as zHttpsUrl3 } from "@openid4vc/utils";
5
-
6
- // src/authorization-request/z-authorization-request-dc-api.ts
7
- import { z as z6 } from "zod";
1
+ import { HashAlgorithm, Oauth2Error, Oauth2ErrorCodes, Oauth2ServerErrorResponseError, decodeJwt, decodeJwtHeader, fetchJwks, jwtHeaderFromJwtSigner, jwtSignerFromJwt, verifyJwt, zAlgValueNotNone, zCompactJwe, zCompactJwt, zJwkSet, zJwtHeader, zJwtPayload } from "@openid4vc/oauth2";
2
+ import { ContentType, URL, URLSearchParams, addSecondsToDate, createFetcher, createZodFetcher, dateToSeconds, decodeBase64, decodeUtf8String, encodeToBase64Url, encodeToUtf8String, getGlobalConfig, objectToQueryParams, parseIfJson, parseWithErrorHandling, stringToJsonWithErrorHandling, zHttpsUrl, zStringToJson } from "@openid4vc/utils";
3
+ import z$1, { z } from "zod";
4
+
5
+ //#region src/jar/create-jar-authorization-request.ts
6
+ /**
7
+ * Creates a JAR (JWT Authorization Request) request object.
8
+ *
9
+ * @param options - The input parameters
10
+ * @param options.authorizationRequestPayload - The authorization request parameters
11
+ * @param options.jwtSigner - The JWT signer
12
+ * @param options.jweEncryptor - The JWE encryptor (optional) if provided, the request object will be encrypted
13
+ * @param options.requestUri - The request URI (optional) if provided, the request object needs to be fetched from the URI
14
+ * @param options.callbacks - The callback context
15
+ * @returns the requestParams, signerJwk, encryptionJwk, and requestObjectJwt
16
+ */
17
+ async function createJarAuthorizationRequest(options) {
18
+ const { jwtSigner, jweEncryptor, authorizationRequestPayload, requestUri, callbacks } = options;
19
+ let authorizationRequestJwt;
20
+ let encryptionJwk;
21
+ const now = options.now ?? /* @__PURE__ */ new Date();
22
+ const { jwt, signerJwk } = await callbacks.signJwt(jwtSigner, {
23
+ header: {
24
+ ...jwtHeaderFromJwtSigner(jwtSigner),
25
+ typ: "oauth-authz-req+jwt"
26
+ },
27
+ payload: {
28
+ iat: dateToSeconds(now),
29
+ exp: dateToSeconds(addSecondsToDate(now, options.expiresInSeconds)),
30
+ ...options.additionalJwtPayload,
31
+ ...authorizationRequestPayload
32
+ }
33
+ });
34
+ authorizationRequestJwt = jwt;
35
+ if (jweEncryptor) {
36
+ const encryptionResult = await callbacks.encryptJwe(jweEncryptor, authorizationRequestJwt);
37
+ authorizationRequestJwt = encryptionResult.jwe;
38
+ encryptionJwk = encryptionResult.encryptionJwk;
39
+ }
40
+ const client_id = authorizationRequestPayload.client_id;
41
+ return {
42
+ jarAuthorizationRequest: requestUri ? {
43
+ client_id,
44
+ request_uri: requestUri
45
+ } : {
46
+ client_id,
47
+ request: authorizationRequestJwt
48
+ },
49
+ signerJwk,
50
+ encryptionJwk,
51
+ authorizationRequestJwt
52
+ };
53
+ }
8
54
 
9
- // src/authorization-request/z-authorization-request.ts
10
- import { URL, zHttpsUrl as zHttpsUrl2, zStringToJson } from "@openid4vc/utils";
11
- import { z as z5 } from "zod";
55
+ //#endregion
56
+ //#region src/authorization-request/validate-authorization-request.ts
57
+ /**
58
+ * Validate the OpenId4Vp Authorization Request parameters
59
+ */
60
+ const validateOpenid4vpAuthorizationRequestPayload = (options) => {
61
+ const { params, walletVerificationOptions } = options;
62
+ if (!params.redirect_uri && !params.response_uri) throw new Oauth2ServerErrorResponseError({
63
+ error: Oauth2ErrorCodes.InvalidRequest,
64
+ error_description: `Missing required 'redirect_uri' or 'response_uri' in openid4vp authorization request.`
65
+ });
66
+ if (params.response_uri && !["direct_post", "direct_post.jwt"].find((mode) => mode === params.response_mode)) throw new Oauth2ServerErrorResponseError({
67
+ error: Oauth2ErrorCodes.InvalidRequest,
68
+ error_description: `The 'response_mode' parameter MUST be 'direct_post' or 'direct_post.jwt' when 'response_uri' is provided. Current: ${params.response_mode}`
69
+ });
70
+ if ([
71
+ params.presentation_definition_uri,
72
+ params.presentation_definition,
73
+ params.dcql_query,
74
+ params.scope
75
+ ].filter(Boolean).length > 1) throw new Oauth2ServerErrorResponseError({
76
+ error: Oauth2ErrorCodes.InvalidRequest,
77
+ error_description: "Exactly one of the following parameters MUST be present in the authorization request: dcql_query, presentation_definition, presentation_definition_uri, or a scope value representing a Presentation Definition."
78
+ });
79
+ if (params.request_uri_method && !params.request_uri) throw new Oauth2ServerErrorResponseError({
80
+ error: Oauth2ErrorCodes.InvalidRequest,
81
+ error_description: "The \"request_uri_method\" parameter MUST NOT be present in the authorization request if the \"request_uri\" parameter is not present."
82
+ });
83
+ if (params.request_uri_method && !["GET", "POST"].includes(params.request_uri_method)) throw new Oauth2ServerErrorResponseError({
84
+ error: Oauth2ErrorCodes.InvalidRequestUriMethod,
85
+ error_description: `The 'request_uri_method' parameter MUST be 'GET' or 'POST'. Current: ${params.request_uri_method}`
86
+ });
87
+ if (params.trust_chain && !zHttpsUrl.safeParse(params.client_id).success) throw new Oauth2ServerErrorResponseError({
88
+ error: Oauth2ErrorCodes.InvalidRequest,
89
+ error_description: "The \"trust_chain\" parameter MUST NOT be present in the authorization request if the \"client_id\" is not an OpenId Federation Entity Identifier starting with http:// or https://."
90
+ });
91
+ if (walletVerificationOptions?.expectedNonce && !params.wallet_nonce) throw new Oauth2ServerErrorResponseError({
92
+ error: Oauth2ErrorCodes.InvalidRequest,
93
+ error_description: "The \"wallet_nonce\" parameter MUST be present in the authorization request when the \"expectedNonce\" parameter is provided."
94
+ });
95
+ if (walletVerificationOptions?.expectedNonce !== params.wallet_nonce) throw new Oauth2ServerErrorResponseError({
96
+ error: Oauth2ErrorCodes.InvalidRequest,
97
+ error_description: "The \"wallet_nonce\" parameter MUST match the \"expectedNonce\" parameter when the \"expectedNonce\" parameter is provided."
98
+ });
99
+ if (params.client_id.startsWith("web-origin:") || params.client_id.startsWith("origin:")) throw new Oauth2ServerErrorResponseError({
100
+ error: Oauth2ErrorCodes.InvalidRequest,
101
+ error_description: `The 'client_id' parameter MUST NOT use client identifier scheme '${params.client_id.split(":")[0]}' when not using the dc_api response mode. Current: ${params.client_id}`
102
+ });
103
+ };
12
104
 
13
- // src/models/z-client-metadata.ts
14
- import { zJwkSet } from "@openid4vc/oauth2";
15
- import { zHttpsUrl } from "@openid4vc/utils";
16
- import { z as z3 } from "zod";
105
+ //#endregion
106
+ //#region src/authorization-request/validate-authorization-request-dc-api.ts
107
+ /**
108
+ * Validate the OpenId4Vp Authorization Request parameters for the dc_api response mode
109
+ */
110
+ const validateOpenid4vpAuthorizationRequestDcApiPayload = (options) => {
111
+ const { params, isJarRequest, disableOriginValidation, origin } = options;
112
+ if (isJarRequest && !params.expected_origins) throw new Oauth2ServerErrorResponseError({
113
+ error: Oauth2ErrorCodes.InvalidRequest,
114
+ error_description: `The 'expected_origins' parameter MUST be present when using the dc_api response mode in combinaction with jar.`
115
+ });
116
+ if ([params.presentation_definition, params.dcql_query].filter(Boolean).length !== 1) throw new Oauth2ServerErrorResponseError({
117
+ error: Oauth2ErrorCodes.InvalidRequest,
118
+ error_description: "Exactly one of the following parameters MUST be present in the Authorization Request: dcql_query or presentation_definition"
119
+ });
120
+ if (params.expected_origins && !disableOriginValidation) {
121
+ if (!origin) throw new Oauth2ServerErrorResponseError({
122
+ error: Oauth2ErrorCodes.InvalidRequest,
123
+ error_description: `Failed to validate the 'origin' of the authorization request. The 'origin' was not provided.`
124
+ });
125
+ if (params.expected_origins && !params.expected_origins.includes(origin)) throw new Oauth2ServerErrorResponseError({
126
+ error: Oauth2ErrorCodes.InvalidRequest,
127
+ error_description: `The 'expected_origins' parameter MUST include the origin of the authorization request. Current: ${params.expected_origins.join(", ")}`
128
+ });
129
+ }
130
+ };
17
131
 
18
- // src/jarm/metadata/z-jarm-client-metadata.ts
19
- import { Oauth2Error, zAlgValueNotNone } from "@openid4vc/oauth2";
20
- import { parseWithErrorHandling } from "@openid4vc/utils";
21
- import { z } from "zod";
22
- var zJarmSignOnlyClientMetadata = z.object({
23
- authorization_signed_response_alg: zAlgValueNotNone,
24
- authorization_encrypted_response_alg: z.optional(z.never()),
25
- authorization_encrypted_response_enc: z.optional(z.never())
132
+ //#endregion
133
+ //#region src/jarm/metadata/z-jarm-client-metadata.ts
134
+ const zJarmSignOnlyClientMetadata = z.object({
135
+ authorization_signed_response_alg: zAlgValueNotNone,
136
+ authorization_encrypted_response_alg: z.optional(z.never()),
137
+ authorization_encrypted_response_enc: z.optional(z.never())
26
138
  });
27
- var zJarmEncryptOnlyClientMetadata = z.object({
28
- authorization_signed_response_alg: z.optional(z.never()),
29
- authorization_encrypted_response_alg: z.string(),
30
- authorization_encrypted_response_enc: z.optional(z.string())
139
+ const zJarmEncryptOnlyClientMetadata = z.object({
140
+ authorization_signed_response_alg: z.optional(z.never()),
141
+ authorization_encrypted_response_alg: z.string(),
142
+ authorization_encrypted_response_enc: z.optional(z.string())
31
143
  });
32
- var zJarmSignEncryptClientMetadata = z.object({
33
- authorization_signed_response_alg: zJarmSignOnlyClientMetadata.shape.authorization_signed_response_alg,
34
- authorization_encrypted_response_alg: zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_alg,
35
- authorization_encrypted_response_enc: zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_enc
144
+ const zJarmSignEncryptClientMetadata = z.object({
145
+ authorization_signed_response_alg: zJarmSignOnlyClientMetadata.shape.authorization_signed_response_alg,
146
+ authorization_encrypted_response_alg: zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_alg,
147
+ authorization_encrypted_response_enc: zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_enc
36
148
  });
37
- var zJarmClientMetadata = z.object({
38
- authorization_signed_response_alg: z.optional(zJarmSignOnlyClientMetadata.shape.authorization_signed_response_alg),
39
- authorization_encrypted_response_alg: z.optional(
40
- zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_alg
41
- ),
42
- authorization_encrypted_response_enc: z.optional(
43
- zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_enc
44
- )
149
+ /**
150
+ * Clients may register their public encryption keys using the jwks_uri or jwks metadata parameters.
151
+ */
152
+ const zJarmClientMetadata = z.object({
153
+ authorization_signed_response_alg: z.optional(zJarmSignOnlyClientMetadata.shape.authorization_signed_response_alg),
154
+ authorization_encrypted_response_alg: z.optional(zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_alg),
155
+ authorization_encrypted_response_enc: z.optional(zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_enc)
45
156
  });
46
- var zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata) => {
47
- const parsedClientMeta = parseWithErrorHandling(
48
- z.union([zJarmEncryptOnlyClientMetadata, zJarmSignOnlyClientMetadata, zJarmSignEncryptClientMetadata]),
49
- client_metadata,
50
- "Invalid jarm client metadata."
51
- );
52
- const SignEncrypt = zJarmSignEncryptClientMetadata.safeParse(parsedClientMeta);
53
- if (SignEncrypt.success) {
54
- return {
55
- type: "sign_encrypt",
56
- client_metadata: {
57
- ...SignEncrypt.data,
58
- authorization_encrypted_response_enc: client_metadata.authorization_encrypted_response_enc
59
- }
60
- };
61
- }
62
- const encryptOnly = zJarmEncryptOnlyClientMetadata.safeParse(parsedClientMeta);
63
- if (encryptOnly.success) {
64
- return {
65
- type: "encrypt",
66
- client_metadata: {
67
- ...encryptOnly.data,
68
- authorization_encrypted_response_enc: parsedClientMeta.authorization_encrypted_response_enc
69
- }
70
- };
71
- }
72
- const signOnly = zJarmSignOnlyClientMetadata.safeParse(parsedClientMeta);
73
- if (signOnly.success) {
74
- return {
75
- type: "sign",
76
- client_metadata: {
77
- ...signOnly.data,
78
- authorization_signed_response_alg: parsedClientMeta.authorization_signed_response_alg
79
- }
80
- };
81
- }
82
- throw new Oauth2Error("Invalid jarm client metadata. Failed to parse.");
157
+ const zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata) => {
158
+ const parsedClientMeta = parseWithErrorHandling(z.union([
159
+ zJarmEncryptOnlyClientMetadata,
160
+ zJarmSignOnlyClientMetadata,
161
+ zJarmSignEncryptClientMetadata
162
+ ]), client_metadata, "Invalid jarm client metadata.");
163
+ const SignEncrypt = zJarmSignEncryptClientMetadata.safeParse(parsedClientMeta);
164
+ if (SignEncrypt.success) return {
165
+ type: "sign_encrypt",
166
+ client_metadata: {
167
+ ...SignEncrypt.data,
168
+ authorization_encrypted_response_enc: client_metadata.authorization_encrypted_response_enc
169
+ }
170
+ };
171
+ const encryptOnly = zJarmEncryptOnlyClientMetadata.safeParse(parsedClientMeta);
172
+ if (encryptOnly.success) return {
173
+ type: "encrypt",
174
+ client_metadata: {
175
+ ...encryptOnly.data,
176
+ authorization_encrypted_response_enc: parsedClientMeta.authorization_encrypted_response_enc
177
+ }
178
+ };
179
+ const signOnly = zJarmSignOnlyClientMetadata.safeParse(parsedClientMeta);
180
+ if (signOnly.success) return {
181
+ type: "sign",
182
+ client_metadata: {
183
+ ...signOnly.data,
184
+ authorization_signed_response_alg: parsedClientMeta.authorization_signed_response_alg
185
+ }
186
+ };
187
+ throw new Oauth2Error("Invalid jarm client metadata. Failed to parse.");
83
188
  });
84
189
 
85
- // src/models/z-vp-formats-supported.ts
86
- import { z as z2 } from "zod";
87
- var zVpFormatsSupported = z2.object({
88
- "dc+sd-jwt": z2.optional(
89
- z2.object({
90
- "sd-jwt_alg_values": z2.optional(z2.array(z2.string()).nonempty()),
91
- "kb-jwt_alg_values": z2.optional(z2.array(z2.string()).nonempty())
92
- }).passthrough()
93
- ),
94
- jwt_vc_json: z2.optional(
95
- z2.object({
96
- alg_values: z2.optional(z2.array(z2.string()).nonempty())
97
- }).passthrough()
98
- ),
99
- ldp_vc: z2.optional(
100
- z2.object({
101
- proof_type_values: z2.optional(z2.array(z2.string()).nonempty()),
102
- cryptosuite_values: z2.optional(z2.array(z2.string()).nonempty())
103
- }).passthrough()
104
- ),
105
- mso_mdoc: z2.optional(
106
- z2.object({
107
- // Draft 27
108
- issuer_signed_alg_values: z2.optional(z2.array(z2.number()).nonempty()),
109
- device_signed_alg_values: z2.optional(z2.array(z2.number()).nonempty()),
110
- // Draft 28+
111
- issuerauth_alg_values: z2.optional(z2.array(z2.number()).nonempty()),
112
- deviceauth_alg_values: z2.optional(z2.array(z2.number()).nonempty())
113
- }).passthrough()
114
- )
115
- }).passthrough().catchall(z2.object({}).passthrough());
116
- var zLegacyVpFormats = z2.record(
117
- z2.string(),
118
- z2.object({
119
- alg_values_supported: z2.optional(z2.array(z2.string()))
120
- }).passthrough()
121
- );
122
-
123
- // src/models/z-client-metadata.ts
124
- var zClientMetadata = z3.object({
125
- // Up until draft 22
126
- jwks_uri: z3.string().url().optional(),
127
- jwks: z3.optional(zJwkSet),
128
- // Up until draft 26
129
- vp_formats: z3.optional(zLegacyVpFormats),
130
- // From draft 27
131
- vp_formats_supported: z3.optional(zVpFormatsSupported),
132
- // From draft 28
133
- encrypted_response_enc_values_supported: z3.optional(z3.array(z3.string())),
134
- ...zJarmClientMetadata.shape,
135
- logo_uri: zHttpsUrl.optional(),
136
- client_name: z3.string().optional()
190
+ //#endregion
191
+ //#region src/models/z-vp-formats-supported.ts
192
+ const zVpFormatsSupported = z.object({
193
+ "dc+sd-jwt": z.optional(z.object({
194
+ "sd-jwt_alg_values": z.optional(z.array(z.string()).nonempty()),
195
+ "kb-jwt_alg_values": z.optional(z.array(z.string()).nonempty())
196
+ }).passthrough()),
197
+ jwt_vc_json: z.optional(z.object({ alg_values: z.optional(z.array(z.string()).nonempty()) }).passthrough()),
198
+ ldp_vc: z.optional(z.object({
199
+ proof_type_values: z.optional(z.array(z.string()).nonempty()),
200
+ cryptosuite_values: z.optional(z.array(z.string()).nonempty())
201
+ }).passthrough()),
202
+ mso_mdoc: z.optional(z.object({
203
+ issuer_signed_alg_values: z.optional(z.array(z.number()).nonempty()),
204
+ device_signed_alg_values: z.optional(z.array(z.number()).nonempty()),
205
+ issuerauth_alg_values: z.optional(z.array(z.number()).nonempty()),
206
+ deviceauth_alg_values: z.optional(z.array(z.number()).nonempty())
207
+ }).passthrough())
208
+ }).passthrough().catchall(z.object({}).passthrough());
209
+ const zLegacyVpFormats = z.record(z.string(), z.object({ alg_values_supported: z.optional(z.array(z.string())) }).passthrough());
210
+
211
+ //#endregion
212
+ //#region src/models/z-client-metadata.ts
213
+ const zClientMetadata = z.object({
214
+ jwks_uri: z.string().url().optional(),
215
+ jwks: z.optional(zJwkSet),
216
+ vp_formats: z.optional(zLegacyVpFormats),
217
+ vp_formats_supported: z.optional(zVpFormatsSupported),
218
+ encrypted_response_enc_values_supported: z.optional(z.array(z.string())),
219
+ ...zJarmClientMetadata.shape,
220
+ logo_uri: zHttpsUrl.optional(),
221
+ client_name: z.string().optional()
137
222
  }).passthrough();
138
223
 
139
- // src/models/z-verifier-attestations.ts
140
- import z4 from "zod";
141
- var zVerifierAttestation = z4.object({
142
- format: z4.string(),
143
- data: z4.record(z4.unknown()).or(z4.string()),
144
- credential_ids: z4.array(z4.string()).optional()
224
+ //#endregion
225
+ //#region src/models/z-verifier-attestations.ts
226
+ const zVerifierAttestation = z$1.object({
227
+ format: z$1.string(),
228
+ data: z$1.record(z$1.unknown()).or(z$1.string()),
229
+ credential_ids: z$1.array(z$1.string()).optional()
145
230
  });
146
- var zVerifierAttestations = z4.array(zVerifierAttestation);
147
-
148
- // src/authorization-request/z-authorization-request.ts
149
- var zOpenid4vpAuthorizationRequest = z5.object({
150
- response_type: z5.literal("vp_token"),
151
- client_id: z5.string(),
152
- redirect_uri: zHttpsUrl2.optional(),
153
- response_uri: zHttpsUrl2.optional(),
154
- request_uri: zHttpsUrl2.optional(),
155
- request_uri_method: z5.optional(z5.string()),
156
- response_mode: z5.enum(["direct_post", "direct_post.jwt"]).optional(),
157
- nonce: z5.string(),
158
- wallet_nonce: z5.string().optional(),
159
- scope: z5.string().optional(),
160
- presentation_definition: z5.record(z5.any()).or(zStringToJson).optional(),
161
- presentation_definition_uri: zHttpsUrl2.optional(),
162
- dcql_query: z5.record(z5.any()).or(zStringToJson).optional(),
163
- client_metadata: zClientMetadata.optional(),
164
- client_metadata_uri: zHttpsUrl2.optional(),
165
- state: z5.string().optional(),
166
- transaction_data: z5.array(z5.string().base64url()).optional(),
167
- trust_chain: z5.array(z5.string()).nonempty().optional(),
168
- client_id_scheme: z5.enum([
169
- "pre-registered",
170
- "redirect_uri",
171
- "entity_id",
172
- "did",
173
- "verifier_attestation",
174
- "x509_san_dns",
175
- "x509_san_uri",
176
- "x509_hash"
177
- ]).optional(),
178
- verifier_attestations: zVerifierAttestations.optional(),
179
- verifier_info: zVerifierAttestations.optional()
231
+ const zVerifierAttestations = z$1.array(zVerifierAttestation);
232
+
233
+ //#endregion
234
+ //#region src/authorization-request/z-authorization-request.ts
235
+ const zOpenid4vpAuthorizationRequest = z.object({
236
+ response_type: z.literal("vp_token"),
237
+ client_id: z.string(),
238
+ redirect_uri: zHttpsUrl.optional(),
239
+ response_uri: zHttpsUrl.optional(),
240
+ request_uri: zHttpsUrl.optional(),
241
+ request_uri_method: z.optional(z.string()),
242
+ response_mode: z.enum(["direct_post", "direct_post.jwt"]).optional(),
243
+ nonce: z.string(),
244
+ wallet_nonce: z.string().optional(),
245
+ scope: z.string().optional(),
246
+ presentation_definition: z.record(z.any()).or(zStringToJson).optional(),
247
+ presentation_definition_uri: zHttpsUrl.optional(),
248
+ dcql_query: z.record(z.any()).or(zStringToJson).optional(),
249
+ client_metadata: zClientMetadata.optional(),
250
+ client_metadata_uri: zHttpsUrl.optional(),
251
+ state: z.string().optional(),
252
+ transaction_data: z.array(z.string().base64url()).optional(),
253
+ trust_chain: z.array(z.string()).nonempty().optional(),
254
+ client_id_scheme: z.enum([
255
+ "pre-registered",
256
+ "redirect_uri",
257
+ "entity_id",
258
+ "did",
259
+ "verifier_attestation",
260
+ "x509_san_dns",
261
+ "x509_san_uri",
262
+ "x509_hash"
263
+ ]).optional(),
264
+ verifier_attestations: zVerifierAttestations.optional(),
265
+ verifier_info: zVerifierAttestations.optional()
180
266
  }).passthrough();
181
- var zOpenid4vpAuthorizationRequestFromUriParams = z5.string().url().transform((url) => Object.fromEntries(new URL(url).searchParams)).pipe(
182
- z5.object({
183
- presentation_definition: zStringToJson.optional(),
184
- client_metadata: zStringToJson.optional(),
185
- dcql_query: zStringToJson.optional(),
186
- transaction_data: zStringToJson.optional(),
187
- verifier_attestations: zStringToJson.optional(),
188
- verifier_info: zStringToJson.optional()
189
- }).passthrough()
190
- );
191
-
192
- // src/authorization-request/z-authorization-request-dc-api.ts
193
- var zOpenid4vpResponseModeDcApi = z6.enum(["dc_api", "dc_api.jwt", "w3c_dc_api.jwt", "w3c_dc_api"]);
194
- var zOpenid4vpAuthorizationRequestDcApi = zOpenid4vpAuthorizationRequest.pick({
195
- response_type: true,
196
- nonce: true,
197
- presentation_definition: true,
198
- client_metadata: true,
199
- transaction_data: true,
200
- dcql_query: true,
201
- trust_chain: true,
202
- state: true,
203
- verifier_attestations: true,
204
- verifier_info: true
267
+ const zOpenid4vpAuthorizationRequestFromUriParams = z.string().url().transform((url) => Object.fromEntries(new URL(url).searchParams)).pipe(z.object({
268
+ presentation_definition: zStringToJson.optional(),
269
+ client_metadata: zStringToJson.optional(),
270
+ dcql_query: zStringToJson.optional(),
271
+ transaction_data: zStringToJson.optional(),
272
+ verifier_attestations: zStringToJson.optional(),
273
+ verifier_info: zStringToJson.optional()
274
+ }).passthrough());
275
+
276
+ //#endregion
277
+ //#region src/authorization-request/z-authorization-request-dc-api.ts
278
+ const zOpenid4vpResponseModeDcApi = z.enum([
279
+ "dc_api",
280
+ "dc_api.jwt",
281
+ "w3c_dc_api.jwt",
282
+ "w3c_dc_api"
283
+ ]);
284
+ const zOpenid4vpAuthorizationRequestDcApi = zOpenid4vpAuthorizationRequest.pick({
285
+ response_type: true,
286
+ nonce: true,
287
+ presentation_definition: true,
288
+ client_metadata: true,
289
+ transaction_data: true,
290
+ dcql_query: true,
291
+ trust_chain: true,
292
+ state: true,
293
+ verifier_attestations: true,
294
+ verifier_info: true
205
295
  }).extend({
206
- client_id: z6.optional(z6.string()),
207
- expected_origins: z6.array(z6.string()).optional(),
208
- response_mode: zOpenid4vpResponseModeDcApi,
209
- // Not allowed with dc_api, but added to make working with interfaces easier
210
- client_id_scheme: z6.never().optional(),
211
- scope: z6.never().optional()
212
- // TODO: should we disallow any properties specifically, such as redirect_uri and response_uri?
296
+ client_id: z.optional(z.string()),
297
+ expected_origins: z.array(z.string()).optional(),
298
+ response_mode: zOpenid4vpResponseModeDcApi,
299
+ client_id_scheme: z.never().optional(),
300
+ scope: z.never().optional()
213
301
  });
214
302
  function isOpenid4vpResponseModeDcApi(responseMode) {
215
- return responseMode !== void 0 && zOpenid4vpResponseModeDcApi.options.includes(responseMode);
303
+ return responseMode !== void 0 && zOpenid4vpResponseModeDcApi.options.includes(responseMode);
216
304
  }
217
305
  function isOpenid4vpAuthorizationRequestDcApi(request) {
218
- return isOpenid4vpResponseModeDcApi(request.response_mode);
219
- }
220
-
221
- // src/client-identifier-prefix/z-client-id-prefix.ts
222
- import { getGlobalConfig } from "@openid4vc/utils";
223
- import { z as z7 } from "zod";
224
- var zClientIdPrefix = z7.enum([
225
- "pre-registered",
226
- "redirect_uri",
227
- "verifier_attestation",
228
- "https",
229
- // pre draft 26
230
- "openid_federation",
231
- // from draft 26
232
- "did",
233
- // pre draft 26
234
- "decentralized_identifier",
235
- // from draft 26
236
- "x509_san_uri",
237
- // pre-draft 25
238
- "x509_hash",
239
- // from draft 25
240
- "x509_san_dns",
241
- "origin",
242
- // from draft 25
243
- "web-origin"
244
- // pre-draft 25
245
- ]);
246
- var zUniformClientIdPrefix = zClientIdPrefix.exclude(["did", "https", "web-origin"]);
247
- var zClientIdToClientIdPrefixAndIdentifier = z7.union(
248
- [
249
- z7.string({ message: "client_id MUST be a string" }).includes(":").transform((clientId) => {
250
- const colonIndex = clientId.indexOf(":");
251
- const clientIdPrefix = clientId.slice(0, colonIndex);
252
- const clientIdIdentifier = clientId.slice(colonIndex + 1);
253
- if (clientIdPrefix === "http" && getGlobalConfig().allowInsecureUrls) {
254
- return ["https", clientId];
255
- }
256
- if (clientIdPrefix === "did" || clientIdPrefix === "http" || clientIdPrefix === "https") {
257
- return [clientIdPrefix, clientId];
258
- }
259
- return [clientIdPrefix, clientIdIdentifier];
260
- }).pipe(z7.tuple([zClientIdPrefix.exclude(["pre-registered"]), z7.string()])),
261
- z7.string().refine((clientId) => clientId.includes(":") === false).transform((clientId) => ["pre-registered", clientId])
262
- ],
263
- {
264
- message: `client_id must either start with a known prefix followed by ':' or contain no ':'. Known prefixes are ${zClientIdPrefix.exclude(["pre-registered"]).options.join(", ")}`
265
- }
266
- );
267
- var zClientIdPrefixToUniform = zClientIdPrefix.transform(
268
- (prefix) => prefix === "did" ? "decentralized_identifier" : prefix === "https" ? "openid_federation" : prefix === "web-origin" ? "origin" : prefix
269
- );
270
- var zLegacyClientIdScheme = z7.enum([
271
- "pre-registered",
272
- "redirect_uri",
273
- "entity_id",
274
- "did",
275
- "verifier_attestation",
276
- "x509_san_dns",
277
- "x509_san_uri"
278
- ]);
279
- var zLegacyClientIdSchemeToClientIdPrefix = zLegacyClientIdScheme.optional().default("pre-registered").transform(
280
- (clientIdScheme) => clientIdScheme === "entity_id" ? "openid_federation" : clientIdScheme === "did" ? "decentralized_identifier" : clientIdScheme
281
- );
282
-
283
- // src/client-identifier-prefix/parse-client-identifier-prefix.ts
284
- function getOpenid4vpClientId(options) {
285
- const original = {
286
- clientId: options.clientId
287
- };
288
- const version = options.version ?? 100;
289
- if (isOpenid4vpResponseModeDcApi(options.responseMode)) {
290
- if (!options.clientId) {
291
- if (!options.origin) {
292
- throw new Oauth2ServerErrorResponseError({
293
- error: Oauth2ErrorCodes.InvalidRequest,
294
- error_description: "Failed to parse client identifier. 'origin' is required for requests without a client_id and response_mode 'dc_api' and 'dc_api.jwt'"
295
- });
296
- }
297
- return {
298
- clientIdPrefix: "origin",
299
- effectiveClientIdPrefix: "origin",
300
- clientIdIdentifier: options.origin,
301
- effectiveClientId: version >= 25 ? `origin:${options.origin}` : `web-origin:${options.origin}`,
302
- original
303
- };
304
- }
305
- const parsedClientIdPrefixAndIdentifier2 = zClientIdToClientIdPrefixAndIdentifier.safeParse(options.clientId);
306
- if (!parsedClientIdPrefixAndIdentifier2.success) {
307
- throw new Oauth2ServerErrorResponseError({
308
- error: Oauth2ErrorCodes.InvalidRequest,
309
- error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
310
- });
311
- }
312
- const [clientIdScheme2, clientIdIdentifier2] = parsedClientIdPrefixAndIdentifier2.data;
313
- const uniformClientIdScheme2 = zClientIdPrefixToUniform.safeParse(clientIdScheme2);
314
- if (!uniformClientIdScheme2.success) {
315
- throw new Oauth2ServerErrorResponseError({
316
- error: Oauth2ErrorCodes.InvalidRequest,
317
- error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
318
- });
319
- }
320
- return {
321
- effectiveClientId: options.clientId,
322
- effectiveClientIdPrefix: clientIdScheme2,
323
- original,
324
- clientIdPrefix: uniformClientIdScheme2.data,
325
- clientIdIdentifier: clientIdIdentifier2
326
- };
327
- }
328
- if (!options.clientId) {
329
- throw new Oauth2ServerErrorResponseError({
330
- error: Oauth2ErrorCodes.InvalidRequest,
331
- error_description: `Failed to parse client identifier. Missing required client_id parameter for response_mode '${options.responseMode}'.`
332
- });
333
- }
334
- if (options.legacyClientIdScheme) {
335
- const parsedClientIdPrefix = zLegacyClientIdSchemeToClientIdPrefix.safeParse(options.legacyClientIdScheme);
336
- if (!parsedClientIdPrefix.success) {
337
- throw new Oauth2ServerErrorResponseError({
338
- error: Oauth2ErrorCodes.InvalidRequest,
339
- error_description: `Failed to parse client identifier. Unsupported client_id_scheme value '${options.legacyClientIdScheme}'.`
340
- });
341
- }
342
- const clientIdPrefix = parsedClientIdPrefix.data;
343
- return {
344
- effectiveClientId: options.clientId,
345
- clientIdIdentifier: options.clientId,
346
- clientIdPrefix,
347
- effectiveClientIdPrefix: options.legacyClientIdScheme ?? "pre-registered",
348
- original: {
349
- ...original,
350
- clientIdScheme: options.legacyClientIdScheme
351
- }
352
- };
353
- }
354
- const parsedClientIdPrefixAndIdentifier = zClientIdToClientIdPrefixAndIdentifier.safeParse(options.clientId);
355
- if (!parsedClientIdPrefixAndIdentifier.success) {
356
- throw new Oauth2ServerErrorResponseError({
357
- error: Oauth2ErrorCodes.InvalidRequest,
358
- error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
359
- });
360
- }
361
- const [clientIdScheme, clientIdIdentifier] = parsedClientIdPrefixAndIdentifier.data;
362
- const uniformClientIdScheme = zClientIdPrefixToUniform.safeParse(clientIdScheme);
363
- if (!uniformClientIdScheme.success) {
364
- throw new Oauth2ServerErrorResponseError({
365
- error: Oauth2ErrorCodes.InvalidRequest,
366
- error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
367
- });
368
- }
369
- return {
370
- effectiveClientId: options.clientId,
371
- clientIdPrefix: uniformClientIdScheme.data,
372
- effectiveClientIdPrefix: clientIdScheme,
373
- clientIdIdentifier,
374
- original
375
- };
376
- }
377
- async function validateOpenid4vpClientId(options, parserConfig) {
378
- const { authorizationRequestPayload, jar, origin } = options;
379
- const parserConfigWithDefaults = {
380
- supportedSchemes: parserConfig?.supportedSchemes || Object.values(zClientIdPrefix.options)
381
- };
382
- const { clientIdIdentifier, clientIdPrefix, effectiveClientId, original } = getOpenid4vpClientId({
383
- clientId: authorizationRequestPayload.client_id,
384
- legacyClientIdScheme: authorizationRequestPayload.client_id_scheme,
385
- responseMode: authorizationRequestPayload.response_mode,
386
- origin
387
- });
388
- if (clientIdPrefix === "pre-registered") {
389
- return {
390
- prefix: "pre-registered",
391
- identifier: clientIdIdentifier,
392
- effective: effectiveClientId,
393
- original
394
- };
395
- }
396
- if (!parserConfigWithDefaults.supportedSchemes.includes(clientIdPrefix)) {
397
- throw new Oauth2ServerErrorResponseError({
398
- error: Oauth2ErrorCodes.InvalidRequest,
399
- error_description: `Unsupported client identifier prefix. ${clientIdPrefix} is not supported.`
400
- });
401
- }
402
- if (clientIdPrefix === "openid_federation") {
403
- if (!zHttpsUrl3.safeParse(clientIdIdentifier).success) {
404
- throw new Oauth2ServerErrorResponseError(
405
- {
406
- error: Oauth2ErrorCodes.InvalidRequest,
407
- error_description: "Invalid client identifier. Client identifier must start with https://"
408
- },
409
- {
410
- internalMessage: `Insecure http:// urls can be enabled by setting the 'allowInsecureUrls' option using setGlobalConfig`
411
- }
412
- );
413
- }
414
- if (!jar) {
415
- throw new Oauth2ServerErrorResponseError({
416
- error: Oauth2ErrorCodes.InvalidRequest,
417
- error_description: 'Using client identifier prefix "https" requires a signed JAR request.'
418
- });
419
- }
420
- if (jar.signer.method !== "federation") {
421
- throw new Oauth2ServerErrorResponseError({
422
- error: Oauth2ErrorCodes.InvalidRequest,
423
- error_description: "Something went wrong. The JWT signer method is not federation but the client identifier prefix is https."
424
- });
425
- }
426
- return {
427
- prefix: "openid_federation",
428
- identifier: clientIdIdentifier,
429
- effective: effectiveClientId,
430
- original,
431
- trustChain: authorizationRequestPayload.trust_chain
432
- };
433
- }
434
- if (clientIdPrefix === "redirect_uri") {
435
- if (jar) {
436
- throw new Oauth2ServerErrorResponseError({
437
- error: Oauth2ErrorCodes.InvalidRequest,
438
- error_description: 'Using client identifier prefix "redirect_uri" the request MUST NOT be signed.'
439
- });
440
- }
441
- if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
442
- throw new Oauth2ServerErrorResponseError({
443
- error: Oauth2ErrorCodes.InvalidRequest,
444
- error_description: `The client identifier prefix 'redirect_uri' is not supported when using the dc_api response mode.`
445
- });
446
- }
447
- if (authorizationRequestPayload.redirect_uri && authorizationRequestPayload.redirect_uri !== clientIdIdentifier) {
448
- throw new Oauth2ServerErrorResponseError({
449
- error: Oauth2ErrorCodes.InvalidClient,
450
- error_description: `When the client identifier prefix is 'redirect_uri', the client id identifier MUST match the redirect_uri.`
451
- });
452
- }
453
- if (authorizationRequestPayload.response_uri && authorizationRequestPayload.response_uri !== clientIdIdentifier) {
454
- throw new Oauth2ServerErrorResponseError({
455
- error: Oauth2ErrorCodes.InvalidClient,
456
- error_description: `When the client identifier prefix is 'redirect_uri', the client id identifier MUST match the response_uri.`
457
- });
458
- }
459
- return {
460
- prefix: clientIdPrefix,
461
- identifier: clientIdIdentifier,
462
- effective: effectiveClientId,
463
- original,
464
- clientMetadata: authorizationRequestPayload.client_metadata,
465
- redirectUri: authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri
466
- };
467
- }
468
- if (clientIdPrefix === "decentralized_identifier") {
469
- if (!jar) {
470
- throw new Oauth2ServerErrorResponseError({
471
- error: Oauth2ErrorCodes.InvalidRequest,
472
- error_description: 'Using client identifier prefix "did" requires a signed JAR request.'
473
- });
474
- }
475
- if (jar.signer.method !== "did") {
476
- throw new Oauth2ServerErrorResponseError({
477
- error: Oauth2ErrorCodes.InvalidRequest,
478
- error_description: "Something went wrong. The JWT signer method is not did but the client identifier prefix is did."
479
- });
480
- }
481
- if (!clientIdIdentifier.startsWith("did:")) {
482
- throw new Oauth2ServerErrorResponseError({
483
- error: Oauth2ErrorCodes.InvalidRequest,
484
- error_description: "Invalid client identifier. Client id identifier must start with 'did:'"
485
- });
486
- }
487
- const [did] = jar.signer.didUrl.split("#");
488
- if (clientIdIdentifier !== did) {
489
- throw new Oauth2ServerErrorResponseError({
490
- error: Oauth2ErrorCodes.InvalidRequest,
491
- error_description: `With client identifier prefix '${clientIdPrefix}' the JAR request must be signed by the same DID as the client identifier.`
492
- });
493
- }
494
- return {
495
- prefix: "decentralized_identifier",
496
- identifier: clientIdIdentifier,
497
- effective: effectiveClientId,
498
- original,
499
- clientMetadata: authorizationRequestPayload.client_metadata,
500
- didUrl: jar.signer.didUrl
501
- };
502
- }
503
- if (clientIdPrefix === "x509_san_dns" || clientIdPrefix === "x509_san_uri" || clientIdPrefix === "x509_hash") {
504
- if (!jar) {
505
- throw new Oauth2ServerErrorResponseError({
506
- error: Oauth2ErrorCodes.InvalidRequest,
507
- error_description: `Using client identifier prefix '${clientIdPrefix}' requires a signed JAR request.`
508
- });
509
- }
510
- if (jar.signer.method !== "x5c") {
511
- throw new Oauth2ServerErrorResponseError({
512
- error: Oauth2ErrorCodes.InvalidRequest,
513
- error_description: `Something went wrong. The JWT signer method is not x5c but the client identifier prefix is '${clientIdPrefix}'`
514
- });
515
- }
516
- if (!options.callbacks.getX509CertificateMetadata) {
517
- throw new Oauth2ServerErrorResponseError(
518
- {
519
- error: Oauth2ErrorCodes.ServerError
520
- },
521
- {
522
- internalMessage: `Missing required 'getX509CertificateMetadata' callback for verification of '${clientIdPrefix}' client id prefix`
523
- }
524
- );
525
- }
526
- if (clientIdPrefix === "x509_san_dns") {
527
- const { sanDnsNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
528
- if (!sanDnsNames.includes(clientIdIdentifier)) {
529
- throw new Oauth2ServerErrorResponseError({
530
- error: Oauth2ErrorCodes.InvalidRequest,
531
- error_description: `Invalid client identifier. One of the leaf certificates san dns names [${sanDnsNames.join(", ")}] must match the client identifier '${clientIdIdentifier}'. `
532
- });
533
- }
534
- if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
535
- const uri = authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri;
536
- if (!uri || new URL2(uri).hostname !== clientIdIdentifier) {
537
- throw new Oauth2ServerErrorResponseError({
538
- error: Oauth2ErrorCodes.InvalidRequest,
539
- error_description: "Invalid client identifier. The fully qualified domain name of the redirect_uri value MUST match the Client Identifier without the prefix x509_san_dns."
540
- });
541
- }
542
- }
543
- } else if (clientIdPrefix === "x509_san_uri") {
544
- const { sanUriNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
545
- if (!sanUriNames.includes(clientIdIdentifier)) {
546
- throw new Oauth2ServerErrorResponseError({
547
- error: Oauth2ErrorCodes.InvalidRequest,
548
- error_description: `Invalid client identifier. One of the leaf certificates san uri names [${sanUriNames.join(", ")}] must match the client identifier '${clientIdIdentifier}'.`
549
- });
550
- }
551
- if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
552
- const uri = authorizationRequestPayload.redirect_uri || authorizationRequestPayload.response_uri;
553
- if (!uri || uri !== clientIdIdentifier) {
554
- throw new Oauth2ServerErrorResponseError({
555
- error: Oauth2ErrorCodes.InvalidRequest,
556
- error_description: "The redirect_uri value MUST match the Client Identifier without the prefix x509_san_uri"
557
- });
558
- }
559
- }
560
- } else if (clientIdPrefix === "x509_hash") {
561
- const x509Hash = encodeToBase64Url(
562
- await options.callbacks.hash(decodeBase64(jar.signer.x5c[0]), HashAlgorithm.Sha256)
563
- );
564
- if (x509Hash !== clientIdIdentifier) {
565
- throw new Oauth2ServerErrorResponseError({
566
- error: Oauth2ErrorCodes.InvalidRequest,
567
- error_description: `Invalid client identifier. Expected the base64url encoded sha-256 hash of the leaf x5c certificate ('${x509Hash}') to match the client identifier '${clientIdIdentifier}'.`
568
- });
569
- }
570
- }
571
- return {
572
- prefix: clientIdPrefix,
573
- identifier: clientIdIdentifier,
574
- effective: effectiveClientId,
575
- original,
576
- x5c: jar.signer.x5c,
577
- clientMetadata: authorizationRequestPayload.client_metadata
578
- };
579
- }
580
- if (clientIdPrefix === "origin") {
581
- return {
582
- prefix: clientIdPrefix,
583
- identifier: clientIdIdentifier,
584
- effective: effectiveClientId,
585
- original,
586
- clientMetadata: authorizationRequestPayload.client_metadata
587
- };
588
- }
589
- if (clientIdPrefix === "verifier_attestation") {
590
- if (!jar) {
591
- throw new Oauth2ServerErrorResponseError({
592
- error: Oauth2ErrorCodes.InvalidRequest,
593
- error_description: 'Using client identifier prefix "verifier_attestation" requires a signed JAR request.'
594
- });
595
- }
596
- }
597
- return {
598
- prefix: clientIdPrefix,
599
- clientMetadata: authorizationRequestPayload.client_metadata,
600
- identifier: clientIdIdentifier,
601
- effective: effectiveClientId,
602
- original
603
- };
604
- }
605
-
606
- // src/jarm/jarm-authorization-response/verify-jarm-authorization-response.ts
607
- import {
608
- Oauth2Error as Oauth2Error3,
609
- decodeJwt,
610
- decodeJwtHeader,
611
- jwtSignerFromJwt,
612
- zCompactJwe,
613
- zCompactJwt,
614
- zJwtHeader as zJwtHeader2
615
- } from "@openid4vc/oauth2";
616
- import { stringToJsonWithErrorHandling } from "@openid4vc/utils";
617
- import z9 from "zod";
618
-
619
- // src/jarm/jarm-extract-jwks.ts
620
- function extractEncryptionJwkFromJwks(jwks, {
621
- kid,
622
- supportedAlgValues
623
- }) {
624
- if (kid) {
625
- return jwks.keys.find((jwk) => jwk.kid === kid);
626
- }
627
- let algFiltered = jwks.keys.filter((key) => key.alg && supportedAlgValues?.includes(key.alg));
628
- if (algFiltered.length === 0) algFiltered = jwks.keys;
629
- let encFiltered = algFiltered.filter((key) => key.use === "enc");
630
- if (!encFiltered) encFiltered = algFiltered.filter((key) => key.use !== "sig");
631
- return encFiltered.length > 0 ? encFiltered[0] : jwks.keys[0];
306
+ return isOpenid4vpResponseModeDcApi(request.response_mode);
632
307
  }
633
308
 
634
- // src/jarm/jarm-authorization-response/jarm-validate-authorization-response.ts
635
- import { Oauth2Error as Oauth2Error2 } from "@openid4vc/oauth2";
636
- import { dateToSeconds } from "@openid4vc/utils";
637
-
638
- // src/jarm/jarm-authorization-response/z-jarm-authorization-response.ts
639
- import { zJwtHeader, zJwtPayload } from "@openid4vc/oauth2";
640
- import { z as z8 } from "zod";
641
- var zJarmHeader = z8.object({ ...zJwtHeader.shape, apu: z8.string().optional(), apv: z8.string().optional() });
642
- var zJarmAuthorizationResponse = z8.object({
643
- /**
644
- * iss: The issuer URL of the authorization server that created the response
645
- * aud: The client_id of the client the response is intended for
646
- * exp: The expiration time of the JWT. A maximum JWT lifetime of 10 minutes is RECOMMENDED.
647
- */
648
- ...zJwtPayload.shape,
649
- ...zJwtPayload.pick({ iss: true, aud: true, exp: true }).required().shape,
650
- state: z8.optional(z8.string())
651
- }).passthrough();
652
- var zJarmAuthorizationResponseEncryptedOnly = z8.object({
653
- ...zJwtPayload.shape,
654
- state: z8.optional(z8.string())
655
- }).passthrough();
656
-
657
- // src/jarm/jarm-authorization-response/jarm-validate-authorization-response.ts
658
- var jarmAuthorizationResponseValidate = (options) => {
659
- const { expectedClientId, authorizationResponse } = options;
660
- if (!zJarmAuthorizationResponse.safeParse(authorizationResponse).success) {
661
- return;
662
- }
663
- if (expectedClientId !== authorizationResponse.aud) {
664
- throw new Oauth2Error2(
665
- `Invalid 'aud' claim in JARM authorization response. Expected '${expectedClientId}' received '${JSON.stringify(authorizationResponse.aud)}'.`
666
- );
667
- }
668
- if (authorizationResponse.exp !== void 0 && authorizationResponse.exp < dateToSeconds()) {
669
- throw new Oauth2Error2("Jarm auth response is expired.");
670
- }
671
- };
672
-
673
- // src/jarm/jarm-authorization-response/verify-jarm-authorization-response.ts
674
- var JarmMode = /* @__PURE__ */ ((JarmMode2) => {
675
- JarmMode2["Signed"] = "Signed";
676
- JarmMode2["Encrypted"] = "Encrypted";
677
- JarmMode2["SignedEncrypted"] = "SignedEncrypted";
678
- return JarmMode2;
679
- })(JarmMode || {});
680
- var decryptJarmAuthorizationResponseJwt = async (options) => {
681
- const { jarmAuthorizationResponseJwt, callbacks, authorizationRequestPayload } = options;
682
- let encryptionJwk = void 0;
683
- const { header } = decodeJwtHeader({
684
- jwt: jarmAuthorizationResponseJwt
685
- });
686
- if (authorizationRequestPayload.client_metadata?.jwks) {
687
- encryptionJwk = extractEncryptionJwkFromJwks(authorizationRequestPayload.client_metadata.jwks, {
688
- // Kid always take precedence
689
- kid: header.kid,
690
- // This value was removed in draft 26, but if it's still provided, we can use it to determine the key to use
691
- supportedAlgValues: authorizationRequestPayload.client_metadata.authorization_encrypted_response_alg ? [authorizationRequestPayload.client_metadata.authorization_encrypted_response_alg] : void 0
692
- });
693
- }
694
- const result = await callbacks.decryptJwe(jarmAuthorizationResponseJwt, { jwk: encryptionJwk });
695
- if (!result.decrypted) {
696
- throw new Oauth2Error3("Failed to decrypt jarm auth response.");
697
- }
698
- return {
699
- decryptionJwk: result.decryptionJwk,
700
- payload: result.payload
701
- };
702
- };
703
- async function verifyJarmAuthorizationResponse(options) {
704
- const { jarmAuthorizationResponseJwt, callbacks, expectedClientId, authorizationRequestPayload } = options;
705
- const requestDataIsEncrypted = zCompactJwe.safeParse(jarmAuthorizationResponseJwt).success;
706
- const decryptedRequestData = requestDataIsEncrypted ? await decryptJarmAuthorizationResponseJwt({
707
- jarmAuthorizationResponseJwt,
708
- callbacks,
709
- authorizationRequestPayload
710
- }) : { payload: jarmAuthorizationResponseJwt, decryptionJwk: void 0 };
711
- const responseIsSigned = zCompactJwt.safeParse(decryptedRequestData.payload).success;
712
- if (!requestDataIsEncrypted && !responseIsSigned) {
713
- throw new Oauth2Error3("Jarm Auth Response must be either encrypted, signed, or signed and encrypted.");
714
- }
715
- let jarmAuthorizationResponse;
716
- if (responseIsSigned) {
717
- const { header: jwsProtectedHeader, payload: jwsPayload } = decodeJwt({
718
- jwt: decryptedRequestData.payload,
719
- headerSchema: z9.object({ ...zJwtHeader2.shape, kid: z9.string() })
720
- });
721
- const response = zJarmAuthorizationResponse.parse(jwsPayload);
722
- const jwtSigner = jwtSignerFromJwt({ header: jwsProtectedHeader, payload: jwsPayload });
723
- const verificationResult = await options.callbacks.verifyJwt(jwtSigner, {
724
- compact: decryptedRequestData.payload,
725
- header: jwsProtectedHeader,
726
- payload: jwsPayload
727
- });
728
- if (!verificationResult.verified) {
729
- throw new Oauth2Error3("Jarm Auth Response is not valid.");
730
- }
731
- jarmAuthorizationResponse = response;
732
- } else {
733
- const jsonRequestData = stringToJsonWithErrorHandling(
734
- decryptedRequestData.payload,
735
- "Unable to parse decrypted JARM JWE body to JSON"
736
- );
737
- jarmAuthorizationResponse = zJarmAuthorizationResponseEncryptedOnly.parse(jsonRequestData);
738
- }
739
- jarmAuthorizationResponseValidate({
740
- expectedClientId,
741
- authorizationResponse: jarmAuthorizationResponse
742
- });
743
- const type = requestDataIsEncrypted && responseIsSigned ? "SignedEncrypted" /* SignedEncrypted */ : requestDataIsEncrypted ? "Encrypted" /* Encrypted */ : "Signed" /* Signed */;
744
- const issuer = jarmAuthorizationResponse.iss;
745
- return {
746
- jarmAuthorizationResponse,
747
- type,
748
- issuer,
749
- decryptionJwk: decryptedRequestData.decryptionJwk
750
- };
751
- }
752
-
753
- // src/version.ts
754
- import { Oauth2ErrorCodes as Oauth2ErrorCodes2, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError2 } from "@openid4vc/oauth2";
755
- function parseAuthorizationRequestVersion(request) {
756
- const requirements = [];
757
- if (request.verifier_info) {
758
- requirements.push([">=", 100]);
759
- }
760
- if (request.verifier_attestations) {
761
- requirements.push(["<", 100]);
762
- }
763
- if (request.client_metadata?.vp_formats_supported?.mso_mdoc?.deviceauth_alg_values || request.client_metadata?.vp_formats_supported?.mso_mdoc?.deviceauth_alg_values) {
764
- requirements.push([">=", 28]);
765
- }
766
- if (request.client_metadata?.vp_formats_supported?.mso_mdoc?.issuer_signed_alg_values || request.client_metadata?.vp_formats_supported?.mso_mdoc?.device_signed_alg_values) {
767
- requirements.push(["<", 28]);
768
- }
769
- if (request.client_metadata?.vp_formats_supported) {
770
- requirements.push([">=", 27]);
771
- }
772
- if (request.client_metadata?.vp_formats) {
773
- requirements.push(["<", 27]);
774
- }
775
- if (request.client_id?.startsWith("openid_federation:") || request.client_id?.startsWith("decentralized_identifier:")) {
776
- requirements.push([">=", 26]);
777
- }
778
- if (request.client_id?.startsWith("did:")) {
779
- requirements.push(["<", 26]);
780
- }
781
- if (request.presentation_definition || request.presentation_definition_uri) {
782
- requirements.push(["<", 26]);
783
- }
784
- if (request.verifier_attestations) {
785
- requirements.push([">=", 26]);
786
- }
787
- if (request.client_id?.startsWith("x509_san_uri:")) {
788
- requirements.push(["<", 25]);
789
- }
790
- if (request.client_id?.startsWith("x509_hash:")) {
791
- requirements.push([">=", 25]);
792
- }
793
- if (request.client_id?.startsWith("web-origin:")) {
794
- requirements.push(["<", 25]);
795
- }
796
- if (request.client_id?.startsWith("origin:")) {
797
- requirements.push([">=", 25]);
798
- }
799
- if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "w3c_dc_api" || request.response_mode === "w3c_dc_api.jwt")) {
800
- requirements.push(["<", 23]);
801
- requirements.push([">=", 21]);
802
- }
803
- if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt")) {
804
- requirements.push([">=", 23]);
805
- }
806
- if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.transaction_data || request.dcql_query)) {
807
- requirements.push([">=", 23]);
808
- }
809
- if (request.transaction_data) {
810
- requirements.push([">=", 22]);
811
- }
812
- if (request.client_id_scheme) {
813
- requirements.push(["<", 22]);
814
- }
815
- if (request.client_id) {
816
- const colonIndex = request.client_id.indexOf(":");
817
- const schemePart = request.client_id.substring(0, colonIndex);
818
- const parsedScheme = zClientIdPrefix.safeParse(schemePart);
819
- if (parsedScheme.success && parsedScheme.data !== "did" && parsedScheme.data !== "https") {
820
- requirements.push([">=", 22]);
821
- }
822
- }
823
- if (!request.client_id) {
824
- requirements.push([">=", 21]);
825
- }
826
- if (request.dcql_query) {
827
- requirements.push([">=", 21]);
828
- }
829
- if (request.client_metadata_uri) {
830
- requirements.push(["<", 21]);
831
- }
832
- if (isOpenid4vpAuthorizationRequestDcApi(request)) {
833
- requirements.push([">=", 21]);
834
- }
835
- if (request.request_uri_method || request.wallet_nonce) {
836
- requirements.push([">=", 21]);
837
- }
838
- if (request.client_id_scheme === "verifier_attestation") {
839
- requirements.push([">=", 20]);
840
- }
841
- if (request.client_id_scheme === "x509_san_dns" || request.client_id_scheme === "x509_san_uri") {
842
- requirements.push([">=", 19]);
843
- }
844
- const lessThanVersions = requirements.filter(([operator]) => operator === "<").map(([_, version]) => version);
845
- const greaterThanVersions = requirements.filter(([operator]) => operator === ">=").map(([_, version]) => version);
846
- const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 100;
847
- const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
848
- if (lowestRequiredVersion > highestPossibleVersion) {
849
- throw new Oauth2ServerErrorResponseError2({
850
- error: Oauth2ErrorCodes2.InvalidRequest,
851
- error_description: `Could not infer openid4vp version from the openid4vp request payload. Based on specification requirements, lowest required version is ${lowestRequiredVersion} and highest possible version is ${highestPossibleVersion}`
852
- });
853
- }
854
- return highestPossibleVersion;
855
- }
856
-
857
- // src/authorization-request/create-authorization-request.ts
858
- import { Oauth2Error as Oauth2Error4 } from "@openid4vc/oauth2";
859
- import { URL as URL3, URLSearchParams, objectToQueryParams, parseWithErrorHandling as parseWithErrorHandling2 } from "@openid4vc/utils";
860
-
861
- // src/jar/create-jar-authorization-request.ts
862
- import {
863
- jwtHeaderFromJwtSigner
864
- } from "@openid4vc/oauth2";
865
- import { addSecondsToDate, dateToSeconds as dateToSeconds2 } from "@openid4vc/utils";
866
- async function createJarAuthorizationRequest(options) {
867
- const { jwtSigner, jweEncryptor, authorizationRequestPayload, requestUri, callbacks } = options;
868
- let authorizationRequestJwt;
869
- let encryptionJwk;
870
- const now = options.now ?? /* @__PURE__ */ new Date();
871
- const { jwt, signerJwk } = await callbacks.signJwt(jwtSigner, {
872
- header: { ...jwtHeaderFromJwtSigner(jwtSigner), typ: "oauth-authz-req+jwt" },
873
- payload: {
874
- iat: dateToSeconds2(now),
875
- exp: dateToSeconds2(addSecondsToDate(now, options.expiresInSeconds)),
876
- ...options.additionalJwtPayload,
877
- ...authorizationRequestPayload
878
- }
879
- });
880
- authorizationRequestJwt = jwt;
881
- if (jweEncryptor) {
882
- const encryptionResult = await callbacks.encryptJwe(jweEncryptor, authorizationRequestJwt);
883
- authorizationRequestJwt = encryptionResult.jwe;
884
- encryptionJwk = encryptionResult.encryptionJwk;
885
- }
886
- const client_id = authorizationRequestPayload.client_id;
887
- const jarAuthorizationRequest = requestUri ? { client_id, request_uri: requestUri } : { client_id, request: authorizationRequestJwt };
888
- return { jarAuthorizationRequest, signerJwk, encryptionJwk, authorizationRequestJwt };
889
- }
890
-
891
- // src/authorization-request/validate-authorization-request.ts
892
- import { Oauth2ErrorCodes as Oauth2ErrorCodes3, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError3 } from "@openid4vc/oauth2";
893
- import { zHttpsUrl as zHttpsUrl4 } from "@openid4vc/utils";
894
- var validateOpenid4vpAuthorizationRequestPayload = (options) => {
895
- const { params, walletVerificationOptions } = options;
896
- if (!params.redirect_uri && !params.response_uri) {
897
- throw new Oauth2ServerErrorResponseError3({
898
- error: Oauth2ErrorCodes3.InvalidRequest,
899
- error_description: `Missing required 'redirect_uri' or 'response_uri' in openid4vp authorization request.`
900
- });
901
- }
902
- if (params.response_uri && !["direct_post", "direct_post.jwt"].find((mode) => mode === params.response_mode)) {
903
- throw new Oauth2ServerErrorResponseError3({
904
- error: Oauth2ErrorCodes3.InvalidRequest,
905
- error_description: `The 'response_mode' parameter MUST be 'direct_post' or 'direct_post.jwt' when 'response_uri' is provided. Current: ${params.response_mode}`
906
- });
907
- }
908
- if ([params.presentation_definition_uri, params.presentation_definition, params.dcql_query, params.scope].filter(
909
- Boolean
910
- ).length > 1) {
911
- throw new Oauth2ServerErrorResponseError3({
912
- error: Oauth2ErrorCodes3.InvalidRequest,
913
- error_description: "Exactly one of the following parameters MUST be present in the authorization request: dcql_query, presentation_definition, presentation_definition_uri, or a scope value representing a Presentation Definition."
914
- });
915
- }
916
- if (params.request_uri_method && !params.request_uri) {
917
- throw new Oauth2ServerErrorResponseError3({
918
- error: Oauth2ErrorCodes3.InvalidRequest,
919
- error_description: 'The "request_uri_method" parameter MUST NOT be present in the authorization request if the "request_uri" parameter is not present.'
920
- });
921
- }
922
- if (params.request_uri_method && !["GET", "POST"].includes(params.request_uri_method)) {
923
- throw new Oauth2ServerErrorResponseError3({
924
- error: Oauth2ErrorCodes3.InvalidRequestUriMethod,
925
- error_description: `The 'request_uri_method' parameter MUST be 'GET' or 'POST'. Current: ${params.request_uri_method}`
926
- });
927
- }
928
- if (params.trust_chain && !zHttpsUrl4.safeParse(params.client_id).success) {
929
- throw new Oauth2ServerErrorResponseError3({
930
- error: Oauth2ErrorCodes3.InvalidRequest,
931
- error_description: 'The "trust_chain" parameter MUST NOT be present in the authorization request if the "client_id" is not an OpenId Federation Entity Identifier starting with http:// or https://.'
932
- });
933
- }
934
- if (walletVerificationOptions?.expectedNonce && !params.wallet_nonce) {
935
- throw new Oauth2ServerErrorResponseError3({
936
- error: Oauth2ErrorCodes3.InvalidRequest,
937
- error_description: 'The "wallet_nonce" parameter MUST be present in the authorization request when the "expectedNonce" parameter is provided.'
938
- });
939
- }
940
- if (walletVerificationOptions?.expectedNonce !== params.wallet_nonce) {
941
- throw new Oauth2ServerErrorResponseError3({
942
- error: Oauth2ErrorCodes3.InvalidRequest,
943
- error_description: 'The "wallet_nonce" parameter MUST match the "expectedNonce" parameter when the "expectedNonce" parameter is provided.'
944
- });
945
- }
946
- if (params.client_id.startsWith("web-origin:") || params.client_id.startsWith("origin:")) {
947
- throw new Oauth2ServerErrorResponseError3({
948
- error: Oauth2ErrorCodes3.InvalidRequest,
949
- error_description: `The 'client_id' parameter MUST NOT use client identifier scheme '${params.client_id.split(":")[0]}' when not using the dc_api response mode. Current: ${params.client_id}`
950
- });
951
- }
952
- };
953
-
954
- // src/authorization-request/validate-authorization-request-dc-api.ts
955
- import { Oauth2ErrorCodes as Oauth2ErrorCodes4, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError4 } from "@openid4vc/oauth2";
956
- var validateOpenid4vpAuthorizationRequestDcApiPayload = (options) => {
957
- const { params, isJarRequest, disableOriginValidation, origin } = options;
958
- if (isJarRequest && !params.expected_origins) {
959
- throw new Oauth2ServerErrorResponseError4({
960
- error: Oauth2ErrorCodes4.InvalidRequest,
961
- error_description: `The 'expected_origins' parameter MUST be present when using the dc_api response mode in combinaction with jar.`
962
- });
963
- }
964
- if ([params.presentation_definition, params.dcql_query].filter(Boolean).length !== 1) {
965
- throw new Oauth2ServerErrorResponseError4({
966
- error: Oauth2ErrorCodes4.InvalidRequest,
967
- error_description: "Exactly one of the following parameters MUST be present in the Authorization Request: dcql_query or presentation_definition"
968
- });
969
- }
970
- if (params.expected_origins && !disableOriginValidation) {
971
- if (!origin) {
972
- throw new Oauth2ServerErrorResponseError4({
973
- error: Oauth2ErrorCodes4.InvalidRequest,
974
- error_description: `Failed to validate the 'origin' of the authorization request. The 'origin' was not provided.`
975
- });
976
- }
977
- if (params.expected_origins && !params.expected_origins.includes(origin)) {
978
- throw new Oauth2ServerErrorResponseError4({
979
- error: Oauth2ErrorCodes4.InvalidRequest,
980
- error_description: `The 'expected_origins' parameter MUST include the origin of the authorization request. Current: ${params.expected_origins.join(", ")}`
981
- });
982
- }
983
- }
984
- };
985
-
986
- // src/authorization-request/create-authorization-request.ts
309
+ //#endregion
310
+ //#region src/authorization-request/create-authorization-request.ts
311
+ /**
312
+ * Creates an OpenID4VP authorization request, optionally with a JWT Secured Authorization Request (JAR)
313
+ * If the request is created after receiving wallet metadata via a POST to the request_uri endpoint, the wallet nonce needs to be provided
314
+ *
315
+ * @param options Configuration options for creating the authorization request
316
+ * @param input.scheme Optional URI scheme to use (defaults to 'openid4vp://')
317
+ * @param input.authorizationRequestPayload The OpenID4VP authorization request parameters
318
+ * @param input.jar Optional JWT Secured Authorization Request (JAR) configuration
319
+ * @param input.jar.requestUri The URI where the JAR will be accessible
320
+ * @param input.jar.jwtSigner Function to sign the JAR JWT
321
+ * @param input.jar.jweEncryptor Optional function to encrypt the JAR JWT
322
+ * @param input.jar.additionalJwtPayload Optional additional claims to include in JAR JWT
323
+ * @param input.wallet Optional wallet-specific parameters
324
+ * @param input.wallet.nonce Optional wallet nonce
325
+ * @param input.callbacks Callback functions for JWT operations
326
+ * @returns Object containing the authorization request parameters, URI and optional JAR details
327
+ */
987
328
  async function createOpenid4vpAuthorizationRequest(options) {
988
- const { jar, scheme = "openid4vp://", wallet, callbacks } = options;
989
- let additionalJwtPayload;
990
- let authorizationRequestPayload;
991
- if (isOpenid4vpAuthorizationRequestDcApi(options.authorizationRequestPayload)) {
992
- authorizationRequestPayload = parseWithErrorHandling2(
993
- zOpenid4vpAuthorizationRequestDcApi,
994
- options.authorizationRequestPayload,
995
- "Invalid authorization request. Could not parse openid4vp dc_api authorization request."
996
- );
997
- if (jar && !authorizationRequestPayload.expected_origins) {
998
- throw new Oauth2Error4(
999
- `The 'expected_origins' parameter MUST be present when using the dc_api response mode in combination with jar.`
1000
- );
1001
- }
1002
- validateOpenid4vpAuthorizationRequestDcApiPayload({
1003
- params: authorizationRequestPayload,
1004
- isJarRequest: Boolean(jar),
1005
- disableOriginValidation: true
1006
- });
1007
- } else {
1008
- authorizationRequestPayload = parseWithErrorHandling2(
1009
- zOpenid4vpAuthorizationRequest,
1010
- options.authorizationRequestPayload,
1011
- "Invalid authorization request. Could not parse openid4vp authorization request."
1012
- );
1013
- validateOpenid4vpAuthorizationRequestPayload({
1014
- params: authorizationRequestPayload,
1015
- walletVerificationOptions: wallet
1016
- });
1017
- }
1018
- if (jar) {
1019
- if (!jar.additionalJwtPayload?.aud) {
1020
- additionalJwtPayload = { ...jar.additionalJwtPayload, aud: jar.requestUri };
1021
- }
1022
- const jarResult = await createJarAuthorizationRequest({
1023
- ...jar,
1024
- authorizationRequestPayload,
1025
- additionalJwtPayload,
1026
- callbacks
1027
- });
1028
- const url2 = new URL3(scheme);
1029
- url2.search = `?${new URLSearchParams([
1030
- ...url2.searchParams.entries(),
1031
- ...objectToQueryParams(jarResult.jarAuthorizationRequest).entries(),
1032
- // Add client_id_scheme if defined for backwards compat
1033
- ...authorizationRequestPayload.client_id_scheme ? [["client_id_scheme", authorizationRequestPayload.client_id_scheme]] : []
1034
- ]).toString()}`;
1035
- return {
1036
- authorizationRequestPayload,
1037
- authorizationRequestObject: jarResult.jarAuthorizationRequest,
1038
- authorizationRequest: url2.toString(),
1039
- jar: { ...jar, ...jarResult }
1040
- };
1041
- }
1042
- const url = new URL3(scheme);
1043
- url.search = `?${new URLSearchParams([
1044
- ...url.searchParams.entries(),
1045
- ...objectToQueryParams(authorizationRequestPayload).entries()
1046
- ]).toString()}`;
1047
- return {
1048
- authorizationRequestPayload,
1049
- authorizationRequestObject: authorizationRequestPayload,
1050
- authorizationRequest: url.toString(),
1051
- jar: void 0
1052
- };
329
+ const { jar, scheme = "openid4vp://", wallet, callbacks } = options;
330
+ let additionalJwtPayload;
331
+ let authorizationRequestPayload;
332
+ if (isOpenid4vpAuthorizationRequestDcApi(options.authorizationRequestPayload)) {
333
+ authorizationRequestPayload = parseWithErrorHandling(zOpenid4vpAuthorizationRequestDcApi, options.authorizationRequestPayload, "Invalid authorization request. Could not parse openid4vp dc_api authorization request.");
334
+ if (jar && !authorizationRequestPayload.expected_origins) throw new Oauth2Error(`The 'expected_origins' parameter MUST be present when using the dc_api response mode in combination with jar.`);
335
+ validateOpenid4vpAuthorizationRequestDcApiPayload({
336
+ params: authorizationRequestPayload,
337
+ isJarRequest: Boolean(jar),
338
+ disableOriginValidation: true
339
+ });
340
+ } else {
341
+ authorizationRequestPayload = parseWithErrorHandling(zOpenid4vpAuthorizationRequest, options.authorizationRequestPayload, "Invalid authorization request. Could not parse openid4vp authorization request.");
342
+ validateOpenid4vpAuthorizationRequestPayload({
343
+ params: authorizationRequestPayload,
344
+ walletVerificationOptions: wallet
345
+ });
346
+ }
347
+ if (jar) {
348
+ if (!jar.additionalJwtPayload?.aud) additionalJwtPayload = {
349
+ ...jar.additionalJwtPayload,
350
+ aud: jar.requestUri
351
+ };
352
+ const jarResult = await createJarAuthorizationRequest({
353
+ ...jar,
354
+ authorizationRequestPayload,
355
+ additionalJwtPayload,
356
+ callbacks
357
+ });
358
+ const url$1 = new URL(scheme);
359
+ url$1.search = `?${new URLSearchParams([
360
+ ...url$1.searchParams.entries(),
361
+ ...objectToQueryParams(jarResult.jarAuthorizationRequest).entries(),
362
+ ...authorizationRequestPayload.client_id_scheme ? [["client_id_scheme", authorizationRequestPayload.client_id_scheme]] : []
363
+ ]).toString()}`;
364
+ return {
365
+ authorizationRequestPayload,
366
+ authorizationRequestObject: jarResult.jarAuthorizationRequest,
367
+ authorizationRequest: url$1.toString(),
368
+ jar: {
369
+ ...jar,
370
+ ...jarResult
371
+ }
372
+ };
373
+ }
374
+ const url = new URL(scheme);
375
+ url.search = `?${new URLSearchParams([...url.searchParams.entries(), ...objectToQueryParams(authorizationRequestPayload).entries()]).toString()}`;
376
+ return {
377
+ authorizationRequestPayload,
378
+ authorizationRequestObject: authorizationRequestPayload,
379
+ authorizationRequest: url.toString(),
380
+ jar: void 0
381
+ };
1053
382
  }
1054
383
 
1055
- // src/authorization-request/parse-authorization-request-params.ts
1056
- import { decodeJwt as decodeJwt2 } from "@openid4vc/oauth2";
1057
- import { parseWithErrorHandling as parseWithErrorHandling3 } from "@openid4vc/utils";
1058
- import z11 from "zod";
1059
-
1060
- // src/jar/z-jar-authorization-request.ts
1061
- import { Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError5 } from "@openid4vc/oauth2";
1062
- import { zHttpsUrl as zHttpsUrl5 } from "@openid4vc/utils";
1063
- import { z as z10 } from "zod";
1064
- var zJarAuthorizationRequest = z10.object({
1065
- request: z10.optional(z10.string()),
1066
- request_uri: z10.optional(zHttpsUrl5),
1067
- request_uri_method: z10.optional(z10.string()),
1068
- client_id: z10.optional(z10.string())
384
+ //#endregion
385
+ //#region src/jar/z-jar-authorization-request.ts
386
+ const zJarAuthorizationRequest = z.object({
387
+ request: z.optional(z.string()),
388
+ request_uri: z.optional(zHttpsUrl),
389
+ request_uri_method: z.optional(z.string()),
390
+ client_id: z.optional(z.string())
1069
391
  }).passthrough();
1070
392
  function validateJarRequestParams(options) {
1071
- const { jarRequestParams } = options;
1072
- if (jarRequestParams.request && jarRequestParams.request_uri) {
1073
- throw new Oauth2ServerErrorResponseError5({
1074
- error: "invalid_request_object",
1075
- error_description: "request and request_uri cannot both be present in a JAR request"
1076
- });
1077
- }
1078
- if (!jarRequestParams.request && !jarRequestParams.request_uri) {
1079
- throw new Oauth2ServerErrorResponseError5({
1080
- error: "invalid_request_object",
1081
- error_description: "request or request_uri must be present"
1082
- });
1083
- }
1084
- return jarRequestParams;
393
+ const { jarRequestParams } = options;
394
+ if (jarRequestParams.request && jarRequestParams.request_uri) throw new Oauth2ServerErrorResponseError({
395
+ error: "invalid_request_object",
396
+ error_description: "request and request_uri cannot both be present in a JAR request"
397
+ });
398
+ if (!jarRequestParams.request && !jarRequestParams.request_uri) throw new Oauth2ServerErrorResponseError({
399
+ error: "invalid_request_object",
400
+ error_description: "request or request_uri must be present"
401
+ });
402
+ return jarRequestParams;
1085
403
  }
1086
404
  function isJarAuthorizationRequest(request) {
1087
- return "request" in request || "request_uri" in request;
405
+ return "request" in request || "request_uri" in request;
1088
406
  }
1089
407
 
1090
- // src/authorization-request/parse-authorization-request-params.ts
408
+ //#endregion
409
+ //#region src/authorization-request/parse-authorization-request-params.ts
1091
410
  function parseOpenid4vpAuthorizationRequest(options) {
1092
- const { authorizationRequest } = options;
1093
- let provided = "params";
1094
- let params;
1095
- if (typeof authorizationRequest === "string") {
1096
- if (authorizationRequest.includes(":")) {
1097
- params = parseWithErrorHandling3(
1098
- zOpenid4vpAuthorizationRequestFromUriParams,
1099
- authorizationRequest,
1100
- "Unable to parse openid4vp authorization request uri to a valid object"
1101
- );
1102
- provided = "uri";
1103
- } else {
1104
- const decoded = decodeJwt2({ jwt: authorizationRequest });
1105
- params = decoded.payload;
1106
- provided = "jwt";
1107
- }
1108
- } else {
1109
- params = authorizationRequest;
1110
- }
1111
- const parsedRequest = parseWithErrorHandling3(
1112
- z11.union([zOpenid4vpAuthorizationRequest, zJarAuthorizationRequest, zOpenid4vpAuthorizationRequestDcApi]),
1113
- params
1114
- );
1115
- if (isJarAuthorizationRequest(parsedRequest)) {
1116
- return {
1117
- type: "jar",
1118
- provided,
1119
- params: parsedRequest
1120
- };
1121
- }
1122
- if (isOpenid4vpAuthorizationRequestDcApi(parsedRequest)) {
1123
- return {
1124
- type: "openid4vp_dc_api",
1125
- provided,
1126
- params: parsedRequest
1127
- };
1128
- }
1129
- return {
1130
- type: "openid4vp",
1131
- provided,
1132
- params: parsedRequest
1133
- };
411
+ const { authorizationRequest } = options;
412
+ let provided = "params";
413
+ let params;
414
+ if (typeof authorizationRequest === "string") if (authorizationRequest.includes(":")) {
415
+ params = parseWithErrorHandling(zOpenid4vpAuthorizationRequestFromUriParams, authorizationRequest, "Unable to parse openid4vp authorization request uri to a valid object");
416
+ provided = "uri";
417
+ } else {
418
+ params = decodeJwt({ jwt: authorizationRequest }).payload;
419
+ provided = "jwt";
420
+ }
421
+ else params = authorizationRequest;
422
+ const parsedRequest = parseWithErrorHandling(z$1.union([
423
+ zOpenid4vpAuthorizationRequest,
424
+ zJarAuthorizationRequest,
425
+ zOpenid4vpAuthorizationRequestDcApi
426
+ ]), params);
427
+ if (isJarAuthorizationRequest(parsedRequest)) return {
428
+ type: "jar",
429
+ provided,
430
+ params: parsedRequest
431
+ };
432
+ if (isOpenid4vpAuthorizationRequestDcApi(parsedRequest)) return {
433
+ type: "openid4vp_dc_api",
434
+ provided,
435
+ params: parsedRequest
436
+ };
437
+ return {
438
+ type: "openid4vp",
439
+ provided,
440
+ params: parsedRequest
441
+ };
1134
442
  }
1135
443
 
1136
- // src/authorization-request/resolve-authorization-request.ts
1137
- import { Oauth2ErrorCodes as Oauth2ErrorCodes9, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError10 } from "@openid4vc/oauth2";
1138
- import { parseWithErrorHandling as parseWithErrorHandling4 } from "@openid4vc/utils";
1139
- import z15 from "zod";
444
+ //#endregion
445
+ //#region src/client-identifier-prefix/z-client-id-prefix.ts
446
+ const zClientIdPrefix = z.enum([
447
+ "pre-registered",
448
+ "redirect_uri",
449
+ "verifier_attestation",
450
+ "https",
451
+ "openid_federation",
452
+ "did",
453
+ "decentralized_identifier",
454
+ "x509_san_uri",
455
+ "x509_hash",
456
+ "x509_san_dns",
457
+ "origin",
458
+ "web-origin"
459
+ ]);
460
+ const zUniformClientIdPrefix = zClientIdPrefix.exclude([
461
+ "did",
462
+ "https",
463
+ "web-origin"
464
+ ]);
465
+ const zClientIdToClientIdPrefixAndIdentifier = z.union([z.string({ message: "client_id MUST be a string" }).includes(":").transform((clientId) => {
466
+ const colonIndex = clientId.indexOf(":");
467
+ const clientIdPrefix = clientId.slice(0, colonIndex);
468
+ const clientIdIdentifier = clientId.slice(colonIndex + 1);
469
+ if (clientIdPrefix === "http" && getGlobalConfig().allowInsecureUrls) return ["https", clientId];
470
+ if (clientIdPrefix === "did" || clientIdPrefix === "http" || clientIdPrefix === "https") return [clientIdPrefix, clientId];
471
+ return [clientIdPrefix, clientIdIdentifier];
472
+ }).pipe(z.tuple([zClientIdPrefix.exclude(["pre-registered"]), z.string()])), z.string().refine((clientId) => clientId.includes(":") === false).transform((clientId) => ["pre-registered", clientId])], { message: `client_id must either start with a known prefix followed by ':' or contain no ':'. Known prefixes are ${zClientIdPrefix.exclude(["pre-registered"]).options.join(", ")}` });
473
+ const zClientIdPrefixToUniform = zClientIdPrefix.transform((prefix) => prefix === "did" ? "decentralized_identifier" : prefix === "https" ? "openid_federation" : prefix === "web-origin" ? "origin" : prefix);
474
+ const zLegacyClientIdScheme = z.enum([
475
+ "pre-registered",
476
+ "redirect_uri",
477
+ "entity_id",
478
+ "did",
479
+ "verifier_attestation",
480
+ "x509_san_dns",
481
+ "x509_san_uri"
482
+ ]);
483
+ const zLegacyClientIdSchemeToClientIdPrefix = zLegacyClientIdScheme.optional().default("pre-registered").transform((clientIdScheme) => clientIdScheme === "entity_id" ? "openid_federation" : clientIdScheme === "did" ? "decentralized_identifier" : clientIdScheme);
484
+
485
+ //#endregion
486
+ //#region src/client-identifier-prefix/parse-client-identifier-prefix.ts
487
+ /**
488
+ * Get the client id for an authorization request based on the response_mode, client_id, client_id_scheme and origin values.
489
+ *
490
+ * It will return the client id prefix as used in OpenID4VP v1, and optionally provide the legacyClientId if the
491
+ * client id was provided with a client_id_scheme
492
+ */
493
+ function getOpenid4vpClientId(options) {
494
+ const original = { clientId: options.clientId };
495
+ const version = options.version ?? 100;
496
+ if (isOpenid4vpResponseModeDcApi(options.responseMode)) {
497
+ if (!options.clientId) {
498
+ if (!options.origin) throw new Oauth2ServerErrorResponseError({
499
+ error: Oauth2ErrorCodes.InvalidRequest,
500
+ error_description: "Failed to parse client identifier. 'origin' is required for requests without a client_id and response_mode 'dc_api' and 'dc_api.jwt'"
501
+ });
502
+ return {
503
+ clientIdPrefix: "origin",
504
+ effectiveClientIdPrefix: "origin",
505
+ clientIdIdentifier: options.origin,
506
+ effectiveClientId: version >= 25 ? `origin:${options.origin}` : `web-origin:${options.origin}`,
507
+ original
508
+ };
509
+ }
510
+ const parsedClientIdPrefixAndIdentifier$1 = zClientIdToClientIdPrefixAndIdentifier.safeParse(options.clientId);
511
+ if (!parsedClientIdPrefixAndIdentifier$1.success) throw new Oauth2ServerErrorResponseError({
512
+ error: Oauth2ErrorCodes.InvalidRequest,
513
+ error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
514
+ });
515
+ const [clientIdScheme$1, clientIdIdentifier$1] = parsedClientIdPrefixAndIdentifier$1.data;
516
+ const uniformClientIdScheme$1 = zClientIdPrefixToUniform.safeParse(clientIdScheme$1);
517
+ if (!uniformClientIdScheme$1.success) throw new Oauth2ServerErrorResponseError({
518
+ error: Oauth2ErrorCodes.InvalidRequest,
519
+ error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
520
+ });
521
+ return {
522
+ effectiveClientId: options.clientId,
523
+ effectiveClientIdPrefix: clientIdScheme$1,
524
+ original,
525
+ clientIdPrefix: uniformClientIdScheme$1.data,
526
+ clientIdIdentifier: clientIdIdentifier$1
527
+ };
528
+ }
529
+ if (!options.clientId) throw new Oauth2ServerErrorResponseError({
530
+ error: Oauth2ErrorCodes.InvalidRequest,
531
+ error_description: `Failed to parse client identifier. Missing required client_id parameter for response_mode '${options.responseMode}'.`
532
+ });
533
+ if (options.legacyClientIdScheme) {
534
+ const parsedClientIdPrefix = zLegacyClientIdSchemeToClientIdPrefix.safeParse(options.legacyClientIdScheme);
535
+ if (!parsedClientIdPrefix.success) throw new Oauth2ServerErrorResponseError({
536
+ error: Oauth2ErrorCodes.InvalidRequest,
537
+ error_description: `Failed to parse client identifier. Unsupported client_id_scheme value '${options.legacyClientIdScheme}'.`
538
+ });
539
+ const clientIdPrefix = parsedClientIdPrefix.data;
540
+ return {
541
+ effectiveClientId: options.clientId,
542
+ clientIdIdentifier: options.clientId,
543
+ clientIdPrefix,
544
+ effectiveClientIdPrefix: options.legacyClientIdScheme ?? "pre-registered",
545
+ original: {
546
+ ...original,
547
+ clientIdScheme: options.legacyClientIdScheme
548
+ }
549
+ };
550
+ }
551
+ const parsedClientIdPrefixAndIdentifier = zClientIdToClientIdPrefixAndIdentifier.safeParse(options.clientId);
552
+ if (!parsedClientIdPrefixAndIdentifier.success) throw new Oauth2ServerErrorResponseError({
553
+ error: Oauth2ErrorCodes.InvalidRequest,
554
+ error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
555
+ });
556
+ const [clientIdScheme, clientIdIdentifier] = parsedClientIdPrefixAndIdentifier.data;
557
+ const uniformClientIdScheme = zClientIdPrefixToUniform.safeParse(clientIdScheme);
558
+ if (!uniformClientIdScheme.success) throw new Oauth2ServerErrorResponseError({
559
+ error: Oauth2ErrorCodes.InvalidRequest,
560
+ error_description: `Failed to parse client identifier. Unsupported client_id '${options.clientId}'.`
561
+ });
562
+ return {
563
+ effectiveClientId: options.clientId,
564
+ clientIdPrefix: uniformClientIdScheme.data,
565
+ effectiveClientIdPrefix: clientIdScheme,
566
+ clientIdIdentifier,
567
+ original
568
+ };
569
+ }
570
+ /**
571
+ * Parse and validate a client identifier
572
+ */
573
+ async function validateOpenid4vpClientId(options, parserConfig) {
574
+ const { authorizationRequestPayload, jar, origin } = options;
575
+ const parserConfigWithDefaults = { supportedSchemes: parserConfig?.supportedSchemes || Object.values(zClientIdPrefix.options) };
576
+ const { clientIdIdentifier, clientIdPrefix, effectiveClientId, original } = getOpenid4vpClientId({
577
+ clientId: authorizationRequestPayload.client_id,
578
+ legacyClientIdScheme: authorizationRequestPayload.client_id_scheme,
579
+ responseMode: authorizationRequestPayload.response_mode,
580
+ origin
581
+ });
582
+ if (clientIdPrefix === "pre-registered") return {
583
+ prefix: "pre-registered",
584
+ identifier: clientIdIdentifier,
585
+ effective: effectiveClientId,
586
+ original
587
+ };
588
+ if (!parserConfigWithDefaults.supportedSchemes.includes(clientIdPrefix)) throw new Oauth2ServerErrorResponseError({
589
+ error: Oauth2ErrorCodes.InvalidRequest,
590
+ error_description: `Unsupported client identifier prefix. ${clientIdPrefix} is not supported.`
591
+ });
592
+ if (clientIdPrefix === "openid_federation") {
593
+ if (!zHttpsUrl.safeParse(clientIdIdentifier).success) throw new Oauth2ServerErrorResponseError({
594
+ error: Oauth2ErrorCodes.InvalidRequest,
595
+ error_description: "Invalid client identifier. Client identifier must start with https://"
596
+ }, { internalMessage: `Insecure http:// urls can be enabled by setting the 'allowInsecureUrls' option using setGlobalConfig` });
597
+ if (!jar) throw new Oauth2ServerErrorResponseError({
598
+ error: Oauth2ErrorCodes.InvalidRequest,
599
+ error_description: "Using client identifier prefix \"https\" requires a signed JAR request."
600
+ });
601
+ if (jar.signer.method !== "federation") throw new Oauth2ServerErrorResponseError({
602
+ error: Oauth2ErrorCodes.InvalidRequest,
603
+ error_description: "Something went wrong. The JWT signer method is not federation but the client identifier prefix is https."
604
+ });
605
+ return {
606
+ prefix: "openid_federation",
607
+ identifier: clientIdIdentifier,
608
+ effective: effectiveClientId,
609
+ original,
610
+ trustChain: authorizationRequestPayload.trust_chain
611
+ };
612
+ }
613
+ if (clientIdPrefix === "redirect_uri") {
614
+ if (jar) throw new Oauth2ServerErrorResponseError({
615
+ error: Oauth2ErrorCodes.InvalidRequest,
616
+ error_description: "Using client identifier prefix \"redirect_uri\" the request MUST NOT be signed."
617
+ });
618
+ if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) throw new Oauth2ServerErrorResponseError({
619
+ error: Oauth2ErrorCodes.InvalidRequest,
620
+ error_description: `The client identifier prefix 'redirect_uri' is not supported when using the dc_api response mode.`
621
+ });
622
+ if (authorizationRequestPayload.redirect_uri && authorizationRequestPayload.redirect_uri !== clientIdIdentifier) throw new Oauth2ServerErrorResponseError({
623
+ error: Oauth2ErrorCodes.InvalidClient,
624
+ error_description: `When the client identifier prefix is 'redirect_uri', the client id identifier MUST match the redirect_uri.`
625
+ });
626
+ if (authorizationRequestPayload.response_uri && authorizationRequestPayload.response_uri !== clientIdIdentifier) throw new Oauth2ServerErrorResponseError({
627
+ error: Oauth2ErrorCodes.InvalidClient,
628
+ error_description: `When the client identifier prefix is 'redirect_uri', the client id identifier MUST match the response_uri.`
629
+ });
630
+ return {
631
+ prefix: clientIdPrefix,
632
+ identifier: clientIdIdentifier,
633
+ effective: effectiveClientId,
634
+ original,
635
+ clientMetadata: authorizationRequestPayload.client_metadata,
636
+ redirectUri: authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri
637
+ };
638
+ }
639
+ if (clientIdPrefix === "decentralized_identifier") {
640
+ if (!jar) throw new Oauth2ServerErrorResponseError({
641
+ error: Oauth2ErrorCodes.InvalidRequest,
642
+ error_description: "Using client identifier prefix \"did\" requires a signed JAR request."
643
+ });
644
+ if (jar.signer.method !== "did") throw new Oauth2ServerErrorResponseError({
645
+ error: Oauth2ErrorCodes.InvalidRequest,
646
+ error_description: "Something went wrong. The JWT signer method is not did but the client identifier prefix is did."
647
+ });
648
+ if (!clientIdIdentifier.startsWith("did:")) throw new Oauth2ServerErrorResponseError({
649
+ error: Oauth2ErrorCodes.InvalidRequest,
650
+ error_description: "Invalid client identifier. Client id identifier must start with 'did:'"
651
+ });
652
+ const [did] = jar.signer.didUrl.split("#");
653
+ if (clientIdIdentifier !== did) throw new Oauth2ServerErrorResponseError({
654
+ error: Oauth2ErrorCodes.InvalidRequest,
655
+ error_description: `With client identifier prefix '${clientIdPrefix}' the JAR request must be signed by the same DID as the client identifier.`
656
+ });
657
+ return {
658
+ prefix: "decentralized_identifier",
659
+ identifier: clientIdIdentifier,
660
+ effective: effectiveClientId,
661
+ original,
662
+ clientMetadata: authorizationRequestPayload.client_metadata,
663
+ didUrl: jar.signer.didUrl
664
+ };
665
+ }
666
+ if (clientIdPrefix === "x509_san_dns" || clientIdPrefix === "x509_san_uri" || clientIdPrefix === "x509_hash") {
667
+ if (!jar) throw new Oauth2ServerErrorResponseError({
668
+ error: Oauth2ErrorCodes.InvalidRequest,
669
+ error_description: `Using client identifier prefix '${clientIdPrefix}' requires a signed JAR request.`
670
+ });
671
+ if (jar.signer.method !== "x5c") throw new Oauth2ServerErrorResponseError({
672
+ error: Oauth2ErrorCodes.InvalidRequest,
673
+ error_description: `Something went wrong. The JWT signer method is not x5c but the client identifier prefix is '${clientIdPrefix}'`
674
+ });
675
+ if (!options.callbacks.getX509CertificateMetadata) throw new Oauth2ServerErrorResponseError({ error: Oauth2ErrorCodes.ServerError }, { internalMessage: `Missing required 'getX509CertificateMetadata' callback for verification of '${clientIdPrefix}' client id prefix` });
676
+ if (clientIdPrefix === "x509_san_dns") {
677
+ const { sanDnsNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
678
+ if (!sanDnsNames.includes(clientIdIdentifier)) throw new Oauth2ServerErrorResponseError({
679
+ error: Oauth2ErrorCodes.InvalidRequest,
680
+ error_description: `Invalid client identifier. One of the leaf certificates san dns names [${sanDnsNames.join(", ")}] must match the client identifier '${clientIdIdentifier}'. `
681
+ });
682
+ if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
683
+ const uri = authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri;
684
+ if (!uri || new URL(uri).hostname !== clientIdIdentifier) throw new Oauth2ServerErrorResponseError({
685
+ error: Oauth2ErrorCodes.InvalidRequest,
686
+ error_description: "Invalid client identifier. The fully qualified domain name of the redirect_uri value MUST match the Client Identifier without the prefix x509_san_dns."
687
+ });
688
+ }
689
+ } else if (clientIdPrefix === "x509_san_uri") {
690
+ const { sanUriNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
691
+ if (!sanUriNames.includes(clientIdIdentifier)) throw new Oauth2ServerErrorResponseError({
692
+ error: Oauth2ErrorCodes.InvalidRequest,
693
+ error_description: `Invalid client identifier. One of the leaf certificates san uri names [${sanUriNames.join(", ")}] must match the client identifier '${clientIdIdentifier}'.`
694
+ });
695
+ if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
696
+ const uri = authorizationRequestPayload.redirect_uri || authorizationRequestPayload.response_uri;
697
+ if (!uri || uri !== clientIdIdentifier) throw new Oauth2ServerErrorResponseError({
698
+ error: Oauth2ErrorCodes.InvalidRequest,
699
+ error_description: "The redirect_uri value MUST match the Client Identifier without the prefix x509_san_uri"
700
+ });
701
+ }
702
+ } else if (clientIdPrefix === "x509_hash") {
703
+ const x509Hash = encodeToBase64Url(await options.callbacks.hash(decodeBase64(jar.signer.x5c[0]), HashAlgorithm.Sha256));
704
+ if (x509Hash !== clientIdIdentifier) throw new Oauth2ServerErrorResponseError({
705
+ error: Oauth2ErrorCodes.InvalidRequest,
706
+ error_description: `Invalid client identifier. Expected the base64url encoded sha-256 hash of the leaf x5c certificate ('${x509Hash}') to match the client identifier '${clientIdIdentifier}'.`
707
+ });
708
+ }
709
+ return {
710
+ prefix: clientIdPrefix,
711
+ identifier: clientIdIdentifier,
712
+ effective: effectiveClientId,
713
+ original,
714
+ x5c: jar.signer.x5c,
715
+ clientMetadata: authorizationRequestPayload.client_metadata
716
+ };
717
+ }
718
+ if (clientIdPrefix === "origin") return {
719
+ prefix: clientIdPrefix,
720
+ identifier: clientIdIdentifier,
721
+ effective: effectiveClientId,
722
+ original,
723
+ clientMetadata: authorizationRequestPayload.client_metadata
724
+ };
725
+ if (clientIdPrefix === "verifier_attestation") {
726
+ if (!jar) throw new Oauth2ServerErrorResponseError({
727
+ error: Oauth2ErrorCodes.InvalidRequest,
728
+ error_description: "Using client identifier prefix \"verifier_attestation\" requires a signed JAR request."
729
+ });
730
+ }
731
+ return {
732
+ prefix: clientIdPrefix,
733
+ clientMetadata: authorizationRequestPayload.client_metadata,
734
+ identifier: clientIdIdentifier,
735
+ effective: effectiveClientId,
736
+ original
737
+ };
738
+ }
1140
739
 
1141
- // src/fetch-client-metadata.ts
1142
- import { Oauth2ErrorCodes as Oauth2ErrorCodes5, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError6 } from "@openid4vc/oauth2";
1143
- import { ContentType, createZodFetcher } from "@openid4vc/utils";
740
+ //#endregion
741
+ //#region src/fetch-client-metadata.ts
1144
742
  async function fetchClientMetadata(options) {
1145
- const { fetch, clientMetadataUri } = options;
1146
- const fetcher = createZodFetcher(fetch);
1147
- const { result, response } = await fetcher(zClientMetadata, ContentType.Json, clientMetadataUri, {
1148
- method: "GET",
1149
- headers: {
1150
- Accept: ContentType.Json
1151
- }
1152
- });
1153
- if (!response.ok) {
1154
- throw new Oauth2ServerErrorResponseError6({
1155
- error_description: `Fetching client metadata from '${clientMetadataUri}' failed with status code '${response.status}'.`,
1156
- error: Oauth2ErrorCodes5.InvalidRequestUri
1157
- });
1158
- }
1159
- if (!result || !result.success) {
1160
- throw new Oauth2ServerErrorResponseError6({
1161
- error_description: `Parsing client metadata from '${clientMetadataUri}' failed.`,
1162
- error: Oauth2ErrorCodes5.InvalidRequestObject
1163
- });
1164
- }
1165
- return result.data;
743
+ const { fetch, clientMetadataUri } = options;
744
+ const { result, response } = await createZodFetcher(fetch)(zClientMetadata, ContentType.Json, clientMetadataUri, {
745
+ method: "GET",
746
+ headers: { Accept: ContentType.Json }
747
+ });
748
+ if (!response.ok) throw new Oauth2ServerErrorResponseError({
749
+ error_description: `Fetching client metadata from '${clientMetadataUri}' failed with status code '${response.status}'.`,
750
+ error: Oauth2ErrorCodes.InvalidRequestUri
751
+ });
752
+ if (!result || !result.success) throw new Oauth2ServerErrorResponseError({
753
+ error_description: `Parsing client metadata from '${clientMetadataUri}' failed.`,
754
+ error: Oauth2ErrorCodes.InvalidRequestObject
755
+ });
756
+ return result.data;
1166
757
  }
1167
758
 
1168
- // src/jar/handle-jar-request/verify-jar-request.ts
1169
- import {
1170
- Oauth2Error as Oauth2Error5,
1171
- Oauth2ErrorCodes as Oauth2ErrorCodes7,
1172
- Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError8,
1173
- decodeJwt as decodeJwt3,
1174
- jwtSignerFromJwt as jwtSignerFromJwt2,
1175
- verifyJwt,
1176
- zCompactJwe as zCompactJwe2,
1177
- zCompactJwt as zCompactJwt2
1178
- } from "@openid4vc/oauth2";
1179
- import z13 from "zod";
759
+ //#endregion
760
+ //#region src/version.ts
761
+ function parseAuthorizationRequestVersion(request) {
762
+ const requirements = [];
763
+ if (request.verifier_info) requirements.push([">=", 100]);
764
+ if (request.verifier_attestations) requirements.push(["<", 100]);
765
+ if (request.client_metadata?.vp_formats_supported?.mso_mdoc?.deviceauth_alg_values || request.client_metadata?.vp_formats_supported?.mso_mdoc?.deviceauth_alg_values) requirements.push([">=", 28]);
766
+ if (request.client_metadata?.vp_formats_supported?.mso_mdoc?.issuer_signed_alg_values || request.client_metadata?.vp_formats_supported?.mso_mdoc?.device_signed_alg_values) requirements.push(["<", 28]);
767
+ if (request.client_metadata?.vp_formats_supported) requirements.push([">=", 27]);
768
+ if (request.client_metadata?.vp_formats) requirements.push(["<", 27]);
769
+ if (request.client_id?.startsWith("openid_federation:") || request.client_id?.startsWith("decentralized_identifier:")) requirements.push([">=", 26]);
770
+ if (request.client_id?.startsWith("did:")) requirements.push(["<", 26]);
771
+ if (request.presentation_definition || request.presentation_definition_uri) requirements.push(["<", 26]);
772
+ if (request.verifier_attestations) requirements.push([">=", 26]);
773
+ if (request.client_id?.startsWith("x509_san_uri:")) requirements.push(["<", 25]);
774
+ if (request.client_id?.startsWith("x509_hash:")) requirements.push([">=", 25]);
775
+ if (request.client_id?.startsWith("web-origin:")) requirements.push(["<", 25]);
776
+ if (request.client_id?.startsWith("origin:")) requirements.push([">=", 25]);
777
+ if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "w3c_dc_api" || request.response_mode === "w3c_dc_api.jwt")) {
778
+ requirements.push(["<", 23]);
779
+ requirements.push([">=", 21]);
780
+ }
781
+ if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt")) requirements.push([">=", 23]);
782
+ if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.transaction_data || request.dcql_query)) requirements.push([">=", 23]);
783
+ if (request.transaction_data) requirements.push([">=", 22]);
784
+ if (request.client_id_scheme) requirements.push(["<", 22]);
785
+ if (request.client_id) {
786
+ const colonIndex = request.client_id.indexOf(":");
787
+ const schemePart = request.client_id.substring(0, colonIndex);
788
+ const parsedScheme = zClientIdPrefix.safeParse(schemePart);
789
+ if (parsedScheme.success && parsedScheme.data !== "did" && parsedScheme.data !== "https") requirements.push([">=", 22]);
790
+ }
791
+ if (!request.client_id) requirements.push([">=", 21]);
792
+ if (request.dcql_query) requirements.push([">=", 21]);
793
+ if (request.client_metadata_uri) requirements.push(["<", 21]);
794
+ if (isOpenid4vpAuthorizationRequestDcApi(request)) requirements.push([">=", 21]);
795
+ if (request.request_uri_method || request.wallet_nonce) requirements.push([">=", 21]);
796
+ if (request.client_id_scheme === "verifier_attestation") requirements.push([">=", 20]);
797
+ if (request.client_id_scheme === "x509_san_dns" || request.client_id_scheme === "x509_san_uri") requirements.push([">=", 19]);
798
+ const lessThanVersions = requirements.filter(([operator]) => operator === "<").map(([_, version]) => version);
799
+ const greaterThanVersions = requirements.filter(([operator]) => operator === ">=").map(([_, version]) => version);
800
+ const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 100;
801
+ const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
802
+ if (lowestRequiredVersion > highestPossibleVersion) throw new Oauth2ServerErrorResponseError({
803
+ error: Oauth2ErrorCodes.InvalidRequest,
804
+ error_description: `Could not infer openid4vp version from the openid4vp request payload. Based on specification requirements, lowest required version is ${lowestRequiredVersion} and highest possible version is ${highestPossibleVersion}`
805
+ });
806
+ return highestPossibleVersion;
807
+ }
1180
808
 
1181
- // src/jar/jar-request-object/fetch-jar-request-object.ts
1182
- import { Oauth2ErrorCodes as Oauth2ErrorCodes6, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError7 } from "@openid4vc/oauth2";
1183
- import { ContentType as ContentType2, createFetcher, objectToQueryParams as objectToQueryParams2 } from "@openid4vc/utils";
809
+ //#endregion
810
+ //#region src/jar/jar-request-object/fetch-jar-request-object.ts
811
+ /**
812
+ * Fetch a request object and parse the response.
813
+ * If you want to fetch the request object without providing wallet_metadata or wallet_nonce as defined in jar you can use the `fetchJarRequestObject` function.
814
+ *
815
+ * Returns validated request object if successful response
816
+ * Throws error otherwise
817
+ *
818
+ * @throws {ValidationError} if successful response but validation of response failed
819
+ * @throws {InvalidFetchResponseError} if no successful or 404 response
820
+ * @throws {Error} if parsing json from response fails
821
+ */
1184
822
  async function fetchJarRequestObject(options) {
1185
- const { requestUri, clientIdPrefix, method, wallet, fetch } = options;
1186
- let requestBody = wallet.metadata ? { wallet_metadata: wallet.metadata, wallet_nonce: wallet.nonce } : void 0;
1187
- if (requestBody?.wallet_metadata?.request_object_signing_alg_values_supported && clientIdPrefix === "redirect_uri") {
1188
- const { request_object_signing_alg_values_supported, ...rest } = requestBody.wallet_metadata;
1189
- requestBody = { ...requestBody, wallet_metadata: { ...rest } };
1190
- }
1191
- const response = await createFetcher(fetch)(requestUri, {
1192
- method,
1193
- body: method === "post" ? objectToQueryParams2(wallet.metadata ?? {}) : void 0,
1194
- headers: {
1195
- Accept: `${ContentType2.OAuthAuthorizationRequestJwt}, ${ContentType2.Jwt};q=0.9, text/plain`,
1196
- "Content-Type": ContentType2.XWwwFormUrlencoded
1197
- }
1198
- }).catch(() => {
1199
- throw new Oauth2ServerErrorResponseError7({
1200
- error_description: `Fetching request_object from request_uri '${requestUri}' failed`,
1201
- error: Oauth2ErrorCodes6.InvalidRequestUri
1202
- });
1203
- });
1204
- if (!response.ok) {
1205
- throw new Oauth2ServerErrorResponseError7({
1206
- error_description: `Fetching request_object from request_uri '${requestUri}' failed with status code '${response.status}'.`,
1207
- error: Oauth2ErrorCodes6.InvalidRequestUri
1208
- });
1209
- }
1210
- return await response.text();
823
+ const { requestUri, clientIdPrefix, method, wallet, fetch } = options;
824
+ let requestBody = wallet.metadata ? {
825
+ wallet_metadata: wallet.metadata,
826
+ wallet_nonce: wallet.nonce
827
+ } : void 0;
828
+ if (requestBody?.wallet_metadata?.request_object_signing_alg_values_supported && clientIdPrefix === "redirect_uri") {
829
+ const { request_object_signing_alg_values_supported,...rest } = requestBody.wallet_metadata;
830
+ requestBody = {
831
+ ...requestBody,
832
+ wallet_metadata: { ...rest }
833
+ };
834
+ }
835
+ const response = await createFetcher(fetch)(requestUri, {
836
+ method,
837
+ body: method === "post" ? objectToQueryParams(wallet.metadata ?? {}) : void 0,
838
+ headers: {
839
+ Accept: `${ContentType.OAuthAuthorizationRequestJwt}, ${ContentType.Jwt};q=0.9, text/plain`,
840
+ "Content-Type": ContentType.XWwwFormUrlencoded
841
+ }
842
+ }).catch(() => {
843
+ throw new Oauth2ServerErrorResponseError({
844
+ error_description: `Fetching request_object from request_uri '${requestUri}' failed`,
845
+ error: Oauth2ErrorCodes.InvalidRequestUri
846
+ });
847
+ });
848
+ if (!response.ok) throw new Oauth2ServerErrorResponseError({
849
+ error_description: `Fetching request_object from request_uri '${requestUri}' failed with status code '${response.status}'.`,
850
+ error: Oauth2ErrorCodes.InvalidRequestUri
851
+ });
852
+ return await response.text();
1211
853
  }
1212
854
 
1213
- // src/jar/jar-request-object/z-jar-request-object.ts
1214
- import { zJwtPayload as zJwtPayload2 } from "@openid4vc/oauth2";
1215
- import { z as z12 } from "zod";
1216
- var zJarRequestObjectPayload = z12.object({
1217
- ...zJwtPayload2.shape,
1218
- client_id: z12.string()
855
+ //#endregion
856
+ //#region src/jar/jar-request-object/z-jar-request-object.ts
857
+ const zJarRequestObjectPayload = z.object({
858
+ ...zJwtPayload.shape,
859
+ client_id: z.string()
1219
860
  }).passthrough();
1220
861
 
1221
- // src/jar/handle-jar-request/verify-jar-request.ts
1222
- var zSignedAuthorizationRequestJwtHeaderTyp = z13.literal("oauth-authz-req+jwt");
1223
- var signedAuthorizationRequestJwtHeaderTyp = zSignedAuthorizationRequestJwtHeaderTyp.value;
862
+ //#endregion
863
+ //#region src/jar/handle-jar-request/verify-jar-request.ts
864
+ const zSignedAuthorizationRequestJwtHeaderTyp = z$1.literal("oauth-authz-req+jwt");
865
+ const signedAuthorizationRequestJwtHeaderTyp = zSignedAuthorizationRequestJwtHeaderTyp.value;
866
+ /**
867
+ * Verifies a JAR (JWT Secured Authorization Request) request by validating, decrypting, and verifying signatures.
868
+ *
869
+ * @param options - The input parameters
870
+ * @param options.jarRequestParams - The JAR authorization request parameters
871
+ * @param options.callbacks - Context containing the relevant Jose crypto operations
872
+ * @returns The verified authorization request parameters and metadata
873
+ */
1224
874
  async function verifyJarRequest(options) {
1225
- const { callbacks, wallet = {} } = options;
1226
- const jarRequestParams = validateJarRequestParams(options);
1227
- const sendBy = jarRequestParams.request ? "value" : "reference";
1228
- const clientIdPrefix = jarRequestParams.client_id ? zClientIdPrefix.safeParse(jarRequestParams.client_id.split(":")[0]).data : "origin";
1229
- const method = jarRequestParams.request_uri_method ?? "get";
1230
- if (method !== "get" && method !== "post") {
1231
- throw new Oauth2ServerErrorResponseError8({
1232
- error: Oauth2ErrorCodes7.InvalidRequestUriMethod,
1233
- error_description: `Invalid request_uri_method. Must be 'get' or 'post'.`
1234
- });
1235
- }
1236
- const requestObject = jarRequestParams.request ?? await fetchJarRequestObject({
1237
- requestUri: jarRequestParams.request_uri,
1238
- clientIdPrefix,
1239
- method,
1240
- wallet,
1241
- fetch: callbacks.fetch
1242
- });
1243
- const requestObjectIsEncrypted = zCompactJwe2.safeParse(requestObject).success;
1244
- const { decryptionJwk, payload: decryptedRequestObject } = requestObjectIsEncrypted ? await decryptJarRequest({ jwe: requestObject, callbacks }) : { payload: requestObject, decryptionJwk: void 0 };
1245
- const requestIsSigned = zCompactJwt2.safeParse(decryptedRequestObject).success;
1246
- if (!requestIsSigned) {
1247
- throw new Oauth2ServerErrorResponseError8({
1248
- error: Oauth2ErrorCodes7.InvalidRequestObject,
1249
- error_description: "JAR request object is not a valid JWT."
1250
- });
1251
- }
1252
- const { authorizationRequestPayload, signer, jwt } = await verifyJarRequestObject({
1253
- decryptedRequestObject,
1254
- callbacks
1255
- });
1256
- if (!authorizationRequestPayload.client_id) {
1257
- throw new Oauth2ServerErrorResponseError8({
1258
- error: Oauth2ErrorCodes7.InvalidRequestObject,
1259
- error_description: 'Jar Request Object is missing the required "client_id" field.'
1260
- });
1261
- }
1262
- if (!isOpenid4vpResponseModeDcApi(authorizationRequestPayload.response_mode) && jarRequestParams.client_id !== authorizationRequestPayload.client_id) {
1263
- throw new Oauth2ServerErrorResponseError8({
1264
- error: Oauth2ErrorCodes7.InvalidRequest,
1265
- error_description: "client_id does not match the request object client_id."
1266
- });
1267
- }
1268
- if (jarRequestParams.client_id_scheme && jarRequestParams.client_id_scheme !== authorizationRequestPayload.client_id_scheme) {
1269
- throw new Oauth2ServerErrorResponseError8({
1270
- error: Oauth2ErrorCodes7.InvalidRequest,
1271
- error_description: "client_id_scheme does not match the request object client_id_scheme."
1272
- });
1273
- }
1274
- return {
1275
- sendBy,
1276
- jwt,
1277
- authorizationRequestPayload,
1278
- signer,
1279
- decryptionJwk
1280
- };
875
+ const { callbacks, wallet = {} } = options;
876
+ const jarRequestParams = validateJarRequestParams(options);
877
+ const sendBy = jarRequestParams.request ? "value" : "reference";
878
+ const clientIdPrefix = jarRequestParams.client_id ? zClientIdPrefix.safeParse(jarRequestParams.client_id.split(":")[0]).data : "origin";
879
+ const method = jarRequestParams.request_uri_method ?? "get";
880
+ if (method !== "get" && method !== "post") throw new Oauth2ServerErrorResponseError({
881
+ error: Oauth2ErrorCodes.InvalidRequestUriMethod,
882
+ error_description: `Invalid request_uri_method. Must be 'get' or 'post'.`
883
+ });
884
+ const requestObject = jarRequestParams.request ?? await fetchJarRequestObject({
885
+ requestUri: jarRequestParams.request_uri,
886
+ clientIdPrefix,
887
+ method,
888
+ wallet,
889
+ fetch: callbacks.fetch
890
+ });
891
+ const { decryptionJwk, payload: decryptedRequestObject } = zCompactJwe.safeParse(requestObject).success ? await decryptJarRequest({
892
+ jwe: requestObject,
893
+ callbacks
894
+ }) : {
895
+ payload: requestObject,
896
+ decryptionJwk: void 0
897
+ };
898
+ if (!zCompactJwt.safeParse(decryptedRequestObject).success) throw new Oauth2ServerErrorResponseError({
899
+ error: Oauth2ErrorCodes.InvalidRequestObject,
900
+ error_description: "JAR request object is not a valid JWT."
901
+ });
902
+ const { authorizationRequestPayload, signer, jwt } = await verifyJarRequestObject({
903
+ decryptedRequestObject,
904
+ callbacks
905
+ });
906
+ if (!authorizationRequestPayload.client_id) throw new Oauth2ServerErrorResponseError({
907
+ error: Oauth2ErrorCodes.InvalidRequestObject,
908
+ error_description: "Jar Request Object is missing the required \"client_id\" field."
909
+ });
910
+ if (!isOpenid4vpResponseModeDcApi(authorizationRequestPayload.response_mode) && jarRequestParams.client_id !== authorizationRequestPayload.client_id) throw new Oauth2ServerErrorResponseError({
911
+ error: Oauth2ErrorCodes.InvalidRequest,
912
+ error_description: "client_id does not match the request object client_id."
913
+ });
914
+ if (jarRequestParams.client_id_scheme && jarRequestParams.client_id_scheme !== authorizationRequestPayload.client_id_scheme) throw new Oauth2ServerErrorResponseError({
915
+ error: Oauth2ErrorCodes.InvalidRequest,
916
+ error_description: "client_id_scheme does not match the request object client_id_scheme."
917
+ });
918
+ return {
919
+ sendBy,
920
+ jwt,
921
+ authorizationRequestPayload,
922
+ signer,
923
+ decryptionJwk
924
+ };
1281
925
  }
1282
926
  async function decryptJarRequest(options) {
1283
- const { jwe, callbacks } = options;
1284
- const { header } = decodeJwt3({ jwt: jwe });
1285
- if (!header.kid) {
1286
- throw new Oauth2ServerErrorResponseError8({
1287
- error: Oauth2ErrorCodes7.InvalidRequestObject,
1288
- error_description: 'Jar JWE is missing the protected header field "kid".'
1289
- });
1290
- }
1291
- const decryptionResult = await callbacks.decryptJwe(jwe);
1292
- if (!decryptionResult.decrypted) {
1293
- throw new Oauth2ServerErrorResponseError8({
1294
- error: "invalid_request_object",
1295
- error_description: "Failed to decrypt jar request object."
1296
- });
1297
- }
1298
- return decryptionResult;
927
+ const { jwe, callbacks } = options;
928
+ const { header } = decodeJwt({ jwt: jwe });
929
+ if (!header.kid) throw new Oauth2ServerErrorResponseError({
930
+ error: Oauth2ErrorCodes.InvalidRequestObject,
931
+ error_description: "Jar JWE is missing the protected header field \"kid\"."
932
+ });
933
+ const decryptionResult = await callbacks.decryptJwe(jwe);
934
+ if (!decryptionResult.decrypted) throw new Oauth2ServerErrorResponseError({
935
+ error: "invalid_request_object",
936
+ error_description: "Failed to decrypt jar request object."
937
+ });
938
+ return decryptionResult;
1299
939
  }
1300
940
  async function verifyJarRequestObject(options) {
1301
- const { decryptedRequestObject, callbacks } = options;
1302
- const jwt = decodeJwt3({ jwt: decryptedRequestObject, payloadSchema: zJarRequestObjectPayload });
1303
- let jwtSigner;
1304
- const { clientIdPrefix } = getOpenid4vpClientId({
1305
- responseMode: jwt.payload.response_mode,
1306
- clientId: jwt.payload.client_id,
1307
- legacyClientIdScheme: jwt.payload.client_id_scheme
1308
- });
1309
- const clientIdToSignerMethod = {
1310
- decentralized_identifier: ["did"],
1311
- "pre-registered": ["custom", "did", "jwk"],
1312
- origin: [],
1313
- // no signing allowed
1314
- redirect_uri: [],
1315
- // no signing allowed
1316
- // Not 100% sure which one are allowed?
1317
- verifier_attestation: ["did", "federation", "jwk", "x5c", "custom"],
1318
- x509_san_dns: ["x5c"],
1319
- x509_san_uri: ["x5c"],
1320
- x509_hash: ["x5c"],
1321
- // Handled separately
1322
- openid_federation: []
1323
- };
1324
- if (clientIdPrefix === "openid_federation") {
1325
- if (!jwt.header.kid) {
1326
- throw new Oauth2Error5(
1327
- `When OpenID Federation is used for signed authorization request, the 'kid' parameter is required.`
1328
- );
1329
- }
1330
- jwtSigner = {
1331
- method: "federation",
1332
- alg: jwt.header.alg,
1333
- trustChain: jwt.payload.trust_chain,
1334
- kid: jwt.header.kid
1335
- };
1336
- } else {
1337
- jwtSigner = jwtSignerFromJwt2({ ...jwt, allowedSignerMethods: clientIdToSignerMethod[clientIdPrefix] });
1338
- }
1339
- const { signer } = await verifyJwt({
1340
- verifyJwtCallback: callbacks.verifyJwt,
1341
- compact: decryptedRequestObject,
1342
- header: jwt.header,
1343
- payload: jwt.payload,
1344
- signer: jwtSigner
1345
- });
1346
- const version = parseAuthorizationRequestVersion(jwt.payload);
1347
- if (jwt.header.typ !== "oauth-authz-req+jwt" && version >= 24) {
1348
- throw new Oauth2ServerErrorResponseError8({
1349
- error: Oauth2ErrorCodes7.InvalidRequestObject,
1350
- error_description: `Invalid Jar Request Object typ header. Expected "oauth-authz-req+jwt", received "${jwt.header.typ}".`
1351
- });
1352
- }
1353
- return {
1354
- signer,
1355
- jwt,
1356
- authorizationRequestPayload: jwt.payload
1357
- };
941
+ const { decryptedRequestObject, callbacks } = options;
942
+ const jwt = decodeJwt({
943
+ jwt: decryptedRequestObject,
944
+ payloadSchema: zJarRequestObjectPayload
945
+ });
946
+ let jwtSigner;
947
+ const { clientIdPrefix } = getOpenid4vpClientId({
948
+ responseMode: jwt.payload.response_mode,
949
+ clientId: jwt.payload.client_id,
950
+ legacyClientIdScheme: jwt.payload.client_id_scheme
951
+ });
952
+ const clientIdToSignerMethod = {
953
+ decentralized_identifier: ["did"],
954
+ "pre-registered": [
955
+ "custom",
956
+ "did",
957
+ "jwk"
958
+ ],
959
+ origin: [],
960
+ redirect_uri: [],
961
+ verifier_attestation: [
962
+ "did",
963
+ "federation",
964
+ "jwk",
965
+ "x5c",
966
+ "custom"
967
+ ],
968
+ x509_san_dns: ["x5c"],
969
+ x509_san_uri: ["x5c"],
970
+ x509_hash: ["x5c"],
971
+ openid_federation: []
972
+ };
973
+ if (clientIdPrefix === "openid_federation") {
974
+ if (!jwt.header.kid) throw new Oauth2Error(`When OpenID Federation is used for signed authorization request, the 'kid' parameter is required.`);
975
+ jwtSigner = {
976
+ method: "federation",
977
+ alg: jwt.header.alg,
978
+ trustChain: jwt.payload.trust_chain,
979
+ kid: jwt.header.kid
980
+ };
981
+ } else jwtSigner = jwtSignerFromJwt({
982
+ ...jwt,
983
+ allowedSignerMethods: clientIdToSignerMethod[clientIdPrefix]
984
+ });
985
+ const { signer } = await verifyJwt({
986
+ verifyJwtCallback: callbacks.verifyJwt,
987
+ compact: decryptedRequestObject,
988
+ header: jwt.header,
989
+ payload: jwt.payload,
990
+ signer: jwtSigner
991
+ });
992
+ const version = parseAuthorizationRequestVersion(jwt.payload);
993
+ if (jwt.header.typ !== "oauth-authz-req+jwt" && version >= 24) throw new Oauth2ServerErrorResponseError({
994
+ error: Oauth2ErrorCodes.InvalidRequestObject,
995
+ error_description: `Invalid Jar Request Object typ header. Expected "oauth-authz-req+jwt", received "${jwt.header.typ}".`
996
+ });
997
+ return {
998
+ signer,
999
+ jwt,
1000
+ authorizationRequestPayload: jwt.payload
1001
+ };
1358
1002
  }
1359
1003
 
1360
- // src/transaction-data/parse-transaction-data.ts
1361
- import { Oauth2ErrorCodes as Oauth2ErrorCodes8, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError9 } from "@openid4vc/oauth2";
1362
- import { decodeBase64 as decodeBase642, encodeToUtf8String, parseIfJson } from "@openid4vc/utils";
1363
-
1364
- // src/transaction-data/z-transaction-data.ts
1365
- import { z as z14 } from "zod";
1366
- var zTransactionEntry = z14.object({
1367
- type: z14.string(),
1368
- credential_ids: z14.array(z14.string()).nonempty(),
1369
- // SD-JWT VC specific
1370
- transaction_data_hashes_alg: z14.array(z14.string()).nonempty().optional()
1004
+ //#endregion
1005
+ //#region src/transaction-data/z-transaction-data.ts
1006
+ const zTransactionEntry = z.object({
1007
+ type: z.string(),
1008
+ credential_ids: z.array(z.string()).nonempty(),
1009
+ transaction_data_hashes_alg: z.array(z.string()).nonempty().optional()
1371
1010
  }).passthrough();
1372
- var zTransactionData = z14.array(zTransactionEntry);
1011
+ const zTransactionData = z.array(zTransactionEntry);
1373
1012
 
1374
- // src/transaction-data/parse-transaction-data.ts
1013
+ //#endregion
1014
+ //#region src/transaction-data/parse-transaction-data.ts
1375
1015
  function parseTransactionData(options) {
1376
- const { transactionData } = options;
1377
- const decoded = transactionData.map((tdEntry) => parseIfJson(encodeToUtf8String(decodeBase642(tdEntry))));
1378
- const parsedResult = zTransactionData.safeParse(decoded);
1379
- if (!parsedResult.success) {
1380
- throw new Oauth2ServerErrorResponseError9({
1381
- error: Oauth2ErrorCodes8.InvalidTransactionData,
1382
- error_description: "Failed to parse transaction data."
1383
- });
1384
- }
1385
- return parsedResult.data.map((decoded2, index) => ({
1386
- transactionData: decoded2,
1387
- encoded: transactionData[index],
1388
- transactionDataIndex: index
1389
- }));
1016
+ const { transactionData } = options;
1017
+ const decoded = transactionData.map((tdEntry) => parseIfJson(encodeToUtf8String(decodeBase64(tdEntry))));
1018
+ const parsedResult = zTransactionData.safeParse(decoded);
1019
+ if (!parsedResult.success) throw new Oauth2ServerErrorResponseError({
1020
+ error: Oauth2ErrorCodes.InvalidTransactionData,
1021
+ error_description: "Failed to parse transaction data."
1022
+ });
1023
+ return parsedResult.data.map((decoded$1, index) => ({
1024
+ transactionData: decoded$1,
1025
+ encoded: transactionData[index],
1026
+ transactionDataIndex: index
1027
+ }));
1390
1028
  }
1391
1029
 
1392
- // src/authorization-request/resolve-authorization-request.ts
1030
+ //#endregion
1031
+ //#region src/authorization-request/resolve-authorization-request.ts
1393
1032
  async function resolveOpenid4vpAuthorizationRequest(options) {
1394
- const { wallet, callbacks, origin, disableOriginValidation } = options;
1395
- let authorizationRequestPayload;
1396
- const parsed = parseWithErrorHandling4(
1397
- z15.union([zOpenid4vpAuthorizationRequestDcApi, zOpenid4vpAuthorizationRequest, zJarAuthorizationRequest]),
1398
- options.authorizationRequestPayload,
1399
- "Invalid authorization request. Could not parse openid4vp authorization request as openid4vp or jar auth request."
1400
- );
1401
- let jar;
1402
- if (isJarAuthorizationRequest(parsed)) {
1403
- jar = await verifyJarRequest({ jarRequestParams: parsed, callbacks, wallet });
1404
- const parsedJarAuthorizationRequestPayload = parseWithErrorHandling4(
1405
- z15.union([zOpenid4vpAuthorizationRequestDcApi, zOpenid4vpAuthorizationRequest]),
1406
- jar.authorizationRequestPayload,
1407
- "Invalid authorization request. Could not parse jar request payload as openid4vp auth request."
1408
- );
1409
- authorizationRequestPayload = validateOpenId4vpAuthorizationRequestPayload({
1410
- authorizationRequestPayload: parsedJarAuthorizationRequestPayload,
1411
- wallet,
1412
- jar: true,
1413
- origin,
1414
- disableOriginValidation
1415
- });
1416
- } else {
1417
- authorizationRequestPayload = validateOpenId4vpAuthorizationRequestPayload({
1418
- authorizationRequestPayload: parsed,
1419
- wallet,
1420
- jar: false,
1421
- origin,
1422
- disableOriginValidation
1423
- });
1424
- }
1425
- const version = parseAuthorizationRequestVersion(authorizationRequestPayload);
1426
- let clientMetadata = authorizationRequestPayload.client_metadata;
1427
- if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload) && !clientMetadata && authorizationRequestPayload.client_metadata_uri) {
1428
- clientMetadata = await fetchClientMetadata({ clientMetadataUri: authorizationRequestPayload.client_metadata_uri });
1429
- }
1430
- const clientMeta = await validateOpenid4vpClientId({
1431
- authorizationRequestPayload: {
1432
- ...authorizationRequestPayload,
1433
- client_metadata: clientMetadata
1434
- },
1435
- jar,
1436
- callbacks,
1437
- origin,
1438
- version
1439
- });
1440
- let pex;
1441
- let dcql;
1442
- if (authorizationRequestPayload.presentation_definition || authorizationRequestPayload.presentation_definition_uri) {
1443
- if (authorizationRequestPayload.presentation_definition_uri) {
1444
- throw new Oauth2ServerErrorResponseError10({
1445
- error: Oauth2ErrorCodes9.InvalidRequest,
1446
- error_description: "Cannot fetch presentation definition from URI. Not supported."
1447
- });
1448
- }
1449
- pex = {
1450
- presentation_definition: authorizationRequestPayload.presentation_definition,
1451
- presentation_definition_uri: authorizationRequestPayload.presentation_definition_uri
1452
- };
1453
- }
1454
- if (authorizationRequestPayload.dcql_query) {
1455
- dcql = { query: authorizationRequestPayload.dcql_query };
1456
- }
1457
- const transactionData = authorizationRequestPayload.transaction_data ? parseTransactionData({ transactionData: authorizationRequestPayload.transaction_data }) : void 0;
1458
- return {
1459
- transactionData,
1460
- authorizationRequestPayload,
1461
- jar,
1462
- client: clientMeta,
1463
- pex,
1464
- dcql,
1465
- version
1466
- };
1033
+ const { wallet, callbacks, origin, disableOriginValidation } = options;
1034
+ let authorizationRequestPayload;
1035
+ const parsed = parseWithErrorHandling(z$1.union([
1036
+ zOpenid4vpAuthorizationRequestDcApi,
1037
+ zOpenid4vpAuthorizationRequest,
1038
+ zJarAuthorizationRequest
1039
+ ]), options.authorizationRequestPayload, "Invalid authorization request. Could not parse openid4vp authorization request as openid4vp or jar auth request.");
1040
+ let jar;
1041
+ if (isJarAuthorizationRequest(parsed)) {
1042
+ jar = await verifyJarRequest({
1043
+ jarRequestParams: parsed,
1044
+ callbacks,
1045
+ wallet
1046
+ });
1047
+ authorizationRequestPayload = validateOpenId4vpAuthorizationRequestPayload({
1048
+ authorizationRequestPayload: parseWithErrorHandling(z$1.union([zOpenid4vpAuthorizationRequestDcApi, zOpenid4vpAuthorizationRequest]), jar.authorizationRequestPayload, "Invalid authorization request. Could not parse jar request payload as openid4vp auth request."),
1049
+ wallet,
1050
+ jar: true,
1051
+ origin,
1052
+ disableOriginValidation
1053
+ });
1054
+ } else authorizationRequestPayload = validateOpenId4vpAuthorizationRequestPayload({
1055
+ authorizationRequestPayload: parsed,
1056
+ wallet,
1057
+ jar: false,
1058
+ origin,
1059
+ disableOriginValidation
1060
+ });
1061
+ const version = parseAuthorizationRequestVersion(authorizationRequestPayload);
1062
+ let clientMetadata = authorizationRequestPayload.client_metadata;
1063
+ if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload) && !clientMetadata && authorizationRequestPayload.client_metadata_uri) clientMetadata = await fetchClientMetadata({ clientMetadataUri: authorizationRequestPayload.client_metadata_uri });
1064
+ const clientMeta = await validateOpenid4vpClientId({
1065
+ authorizationRequestPayload: {
1066
+ ...authorizationRequestPayload,
1067
+ client_metadata: clientMetadata
1068
+ },
1069
+ jar,
1070
+ callbacks,
1071
+ origin,
1072
+ version
1073
+ });
1074
+ let pex;
1075
+ let dcql;
1076
+ if (authorizationRequestPayload.presentation_definition || authorizationRequestPayload.presentation_definition_uri) {
1077
+ if (authorizationRequestPayload.presentation_definition_uri) throw new Oauth2ServerErrorResponseError({
1078
+ error: Oauth2ErrorCodes.InvalidRequest,
1079
+ error_description: "Cannot fetch presentation definition from URI. Not supported."
1080
+ });
1081
+ pex = {
1082
+ presentation_definition: authorizationRequestPayload.presentation_definition,
1083
+ presentation_definition_uri: authorizationRequestPayload.presentation_definition_uri
1084
+ };
1085
+ }
1086
+ if (authorizationRequestPayload.dcql_query) dcql = { query: authorizationRequestPayload.dcql_query };
1087
+ return {
1088
+ transactionData: authorizationRequestPayload.transaction_data ? parseTransactionData({ transactionData: authorizationRequestPayload.transaction_data }) : void 0,
1089
+ authorizationRequestPayload,
1090
+ jar,
1091
+ client: clientMeta,
1092
+ pex,
1093
+ dcql,
1094
+ version
1095
+ };
1467
1096
  }
1468
1097
  function validateOpenId4vpAuthorizationRequestPayload(options) {
1469
- const { authorizationRequestPayload, wallet, jar, origin, disableOriginValidation } = options;
1470
- if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
1471
- validateOpenid4vpAuthorizationRequestDcApiPayload({
1472
- params: authorizationRequestPayload,
1473
- isJarRequest: jar,
1474
- disableOriginValidation,
1475
- origin
1476
- });
1477
- return authorizationRequestPayload;
1478
- }
1479
- validateOpenid4vpAuthorizationRequestPayload({
1480
- params: authorizationRequestPayload,
1481
- walletVerificationOptions: wallet
1482
- });
1483
- return authorizationRequestPayload;
1098
+ const { authorizationRequestPayload, wallet, jar, origin, disableOriginValidation } = options;
1099
+ if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
1100
+ validateOpenid4vpAuthorizationRequestDcApiPayload({
1101
+ params: authorizationRequestPayload,
1102
+ isJarRequest: jar,
1103
+ disableOriginValidation,
1104
+ origin
1105
+ });
1106
+ return authorizationRequestPayload;
1107
+ }
1108
+ validateOpenid4vpAuthorizationRequestPayload({
1109
+ params: authorizationRequestPayload,
1110
+ walletVerificationOptions: wallet
1111
+ });
1112
+ return authorizationRequestPayload;
1484
1113
  }
1485
1114
 
1486
- // src/authorization-response/create-authorization-response.ts
1487
- import {
1488
- Oauth2Error as Oauth2Error8,
1489
- Oauth2ErrorCodes as Oauth2ErrorCodes10,
1490
- Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError11,
1491
- fetchJwks
1492
- } from "@openid4vc/oauth2";
1493
- import { dateToSeconds as dateToSeconds3, encodeToBase64Url as encodeToBase64Url2 } from "@openid4vc/utils";
1494
-
1495
- // ../utils/src/date.ts
1496
- function addSecondsToDate2(date, seconds) {
1497
- return new Date(date.getTime() + seconds * 1e3);
1115
+ //#endregion
1116
+ //#region ../utils/src/date.ts
1117
+ function addSecondsToDate$1(date, seconds) {
1118
+ return new Date(date.getTime() + seconds * 1e3);
1498
1119
  }
1499
1120
 
1500
- // src/jarm/jarm-authorization-response-create.ts
1501
- import {
1502
- Oauth2Error as Oauth2Error6,
1503
- jwtHeaderFromJwtSigner as jwtHeaderFromJwtSigner2
1504
- } from "@openid4vc/oauth2";
1121
+ //#endregion
1122
+ //#region src/jarm/jarm-authorization-response-create.ts
1505
1123
  async function createJarmAuthorizationResponse(options) {
1506
- const { jarmAuthorizationResponse, jweEncryptor, jwtSigner, callbacks } = options;
1507
- if (!jwtSigner && jweEncryptor) {
1508
- const { jwe } = await callbacks.encryptJwe(jweEncryptor, JSON.stringify(jarmAuthorizationResponse));
1509
- return { jarmAuthorizationResponseJwt: jwe };
1510
- }
1511
- if (jwtSigner && !jweEncryptor) {
1512
- const signed2 = await callbacks.signJwt(jwtSigner, {
1513
- header: jwtHeaderFromJwtSigner2(jwtSigner),
1514
- payload: jarmAuthorizationResponse
1515
- });
1516
- return { jarmAuthorizationResponseJwt: signed2.jwt };
1517
- }
1518
- if (!jwtSigner || !jweEncryptor) {
1519
- throw new Oauth2Error6("JWT signer and/or encryptor are required to create a JARM auth response.");
1520
- }
1521
- const signed = await callbacks.signJwt(jwtSigner, {
1522
- header: jwtHeaderFromJwtSigner2(jwtSigner),
1523
- payload: jarmAuthorizationResponse
1524
- });
1525
- const encrypted = await callbacks.encryptJwe(jweEncryptor, signed.jwt);
1526
- return { jarmAuthorizationResponseJwt: encrypted.jwe };
1124
+ const { jarmAuthorizationResponse, jweEncryptor, jwtSigner, callbacks } = options;
1125
+ if (!jwtSigner && jweEncryptor) {
1126
+ const { jwe } = await callbacks.encryptJwe(jweEncryptor, JSON.stringify(jarmAuthorizationResponse));
1127
+ return { jarmAuthorizationResponseJwt: jwe };
1128
+ }
1129
+ if (jwtSigner && !jweEncryptor) return { jarmAuthorizationResponseJwt: (await callbacks.signJwt(jwtSigner, {
1130
+ header: jwtHeaderFromJwtSigner(jwtSigner),
1131
+ payload: jarmAuthorizationResponse
1132
+ })).jwt };
1133
+ if (!jwtSigner || !jweEncryptor) throw new Oauth2Error("JWT signer and/or encryptor are required to create a JARM auth response.");
1134
+ const signed = await callbacks.signJwt(jwtSigner, {
1135
+ header: jwtHeaderFromJwtSigner(jwtSigner),
1136
+ payload: jarmAuthorizationResponse
1137
+ });
1138
+ return { jarmAuthorizationResponseJwt: (await callbacks.encryptJwe(jweEncryptor, signed.jwt)).jwe };
1527
1139
  }
1528
1140
 
1529
- // src/jarm/jarm-response-mode.ts
1530
- import { z as z16 } from "zod";
1531
- var jarmResponseMode = [
1532
- "jwt",
1533
- "query.jwt",
1534
- "fragment.jwt",
1535
- "form_post.jwt",
1536
- "direct_post.jwt",
1537
- "dc_api.jwt"
1141
+ //#endregion
1142
+ //#region src/jarm/jarm-extract-jwks.ts
1143
+ function extractEncryptionJwkFromJwks(jwks, { kid, supportedAlgValues }) {
1144
+ if (kid) return jwks.keys.find((jwk) => jwk.kid === kid);
1145
+ let algFiltered = jwks.keys.filter((key) => key.alg && supportedAlgValues?.includes(key.alg));
1146
+ if (algFiltered.length === 0) algFiltered = jwks.keys;
1147
+ let encFiltered = algFiltered.filter((key) => key.use === "enc");
1148
+ if (!encFiltered) encFiltered = algFiltered.filter((key) => key.use !== "sig");
1149
+ return encFiltered.length > 0 ? encFiltered[0] : jwks.keys[0];
1150
+ }
1151
+
1152
+ //#endregion
1153
+ //#region src/jarm/jarm-response-mode.ts
1154
+ const jarmResponseMode = [
1155
+ "jwt",
1156
+ "query.jwt",
1157
+ "fragment.jwt",
1158
+ "form_post.jwt",
1159
+ "direct_post.jwt",
1160
+ "dc_api.jwt"
1538
1161
  ];
1539
- var zJarmResponseMode = z16.enum(jarmResponseMode);
1540
- var isJarmResponseMode = (responseMode) => {
1541
- return jarmResponseMode.includes(responseMode);
1162
+ const zJarmResponseMode = z.enum(jarmResponseMode);
1163
+ const isJarmResponseMode = (responseMode) => {
1164
+ return jarmResponseMode.includes(responseMode);
1542
1165
  };
1543
1166
 
1544
- // src/jarm/metadata/jarm-assert-metadata-supported.ts
1545
- import { Oauth2Error as Oauth2Error7 } from "@openid4vc/oauth2";
1167
+ //#endregion
1168
+ //#region src/jarm/metadata/jarm-assert-metadata-supported.ts
1546
1169
  function assertValueSupported(options) {
1547
- const { errorMessage, supported, actual } = options;
1548
- const intersection = supported.find((value) => value === actual);
1549
- if (!intersection) {
1550
- throw new Oauth2Error7(errorMessage);
1551
- }
1552
- return intersection;
1170
+ const { errorMessage, supported, actual } = options;
1171
+ const intersection = supported.find((value) => value === actual);
1172
+ if (!intersection) throw new Oauth2Error(errorMessage);
1173
+ return intersection;
1553
1174
  }
1554
1175
  function jarmAssertMetadataSupported(options) {
1555
- const { clientMetadata, serverMetadata } = options;
1556
- const parsedClientMetadata = zJarmClientMetadataParsed.parse(clientMetadata);
1557
- if (parsedClientMetadata.type === "sign_encrypt" || parsedClientMetadata.type === "encrypt") {
1558
- if (serverMetadata.authorization_encryption_alg_values_supported) {
1559
- assertValueSupported({
1560
- supported: serverMetadata.authorization_encryption_alg_values_supported,
1561
- actual: parsedClientMetadata.client_metadata.authorization_encrypted_response_alg,
1562
- errorMessage: "Invalid authorization_encryption_alg"
1563
- });
1564
- }
1565
- if (serverMetadata.authorization_encryption_enc_values_supported) {
1566
- assertValueSupported({
1567
- supported: serverMetadata.authorization_encryption_enc_values_supported,
1568
- actual: parsedClientMetadata.client_metadata.authorization_encrypted_response_enc,
1569
- errorMessage: "Invalid authorization_encryption_enc"
1570
- });
1571
- }
1572
- }
1573
- if (serverMetadata.authorization_signing_alg_values_supported && (parsedClientMetadata.type === "sign" || parsedClientMetadata.type === "sign_encrypt")) {
1574
- assertValueSupported({
1575
- supported: serverMetadata.authorization_signing_alg_values_supported,
1576
- actual: parsedClientMetadata.client_metadata.authorization_signed_response_alg,
1577
- errorMessage: "Invalid authorization_signed_response_alg"
1578
- });
1579
- }
1580
- return parsedClientMetadata;
1176
+ const { clientMetadata, serverMetadata } = options;
1177
+ const parsedClientMetadata = zJarmClientMetadataParsed.parse(clientMetadata);
1178
+ if (parsedClientMetadata.type === "sign_encrypt" || parsedClientMetadata.type === "encrypt") {
1179
+ if (serverMetadata.authorization_encryption_alg_values_supported) assertValueSupported({
1180
+ supported: serverMetadata.authorization_encryption_alg_values_supported,
1181
+ actual: parsedClientMetadata.client_metadata.authorization_encrypted_response_alg,
1182
+ errorMessage: "Invalid authorization_encryption_alg"
1183
+ });
1184
+ if (serverMetadata.authorization_encryption_enc_values_supported) assertValueSupported({
1185
+ supported: serverMetadata.authorization_encryption_enc_values_supported,
1186
+ actual: parsedClientMetadata.client_metadata.authorization_encrypted_response_enc,
1187
+ errorMessage: "Invalid authorization_encryption_enc"
1188
+ });
1189
+ }
1190
+ if (serverMetadata.authorization_signing_alg_values_supported && (parsedClientMetadata.type === "sign" || parsedClientMetadata.type === "sign_encrypt")) assertValueSupported({
1191
+ supported: serverMetadata.authorization_signing_alg_values_supported,
1192
+ actual: parsedClientMetadata.client_metadata.authorization_signed_response_alg,
1193
+ errorMessage: "Invalid authorization_signed_response_alg"
1194
+ });
1195
+ return parsedClientMetadata;
1581
1196
  }
1582
1197
 
1583
- // src/authorization-response/create-authorization-response.ts
1198
+ //#endregion
1199
+ //#region src/authorization-response/create-authorization-response.ts
1584
1200
  async function createOpenid4vpAuthorizationResponse(options) {
1585
- const { authorizationRequestPayload, jarm, callbacks, origin } = options;
1586
- const authorizationResponsePayload = {
1587
- ...options.authorizationResponsePayload,
1588
- state: authorizationRequestPayload.state
1589
- };
1590
- const { clientIdPrefix } = getOpenid4vpClientId({
1591
- responseMode: authorizationRequestPayload.response_mode,
1592
- clientId: authorizationRequestPayload.client_id,
1593
- legacyClientIdScheme: authorizationRequestPayload.client_id_scheme,
1594
- origin
1595
- });
1596
- if (authorizationRequestPayload.response_mode && isJarmResponseMode(authorizationRequestPayload.response_mode) && !jarm) {
1597
- throw new Oauth2Error8(
1598
- `Missing jarm options for creating Jarm response with response mode '${authorizationRequestPayload.response_mode}'`
1599
- );
1600
- }
1601
- if (!jarm) {
1602
- return {
1603
- authorizationResponsePayload
1604
- };
1605
- }
1606
- if (clientIdPrefix === "openid_federation" && !options.clientMetadata) {
1607
- throw new Oauth2Error8(
1608
- "When OpenID Federation is used as the client id prefix (https/openid_federation), passing externally fetched and verified 'clientMetadata' to the 'createOpenid4vpAuthorizationResponse' is required."
1609
- );
1610
- }
1611
- const clientMetadata = options.clientMetadata ?? authorizationRequestPayload.client_metadata;
1612
- if (!clientMetadata) {
1613
- throw new Oauth2Error8("Missing client metadata in the request params to assert Jarm metadata support.");
1614
- }
1615
- let jwks;
1616
- if (clientMetadata.jwks) {
1617
- jwks = clientMetadata.jwks;
1618
- } else if (clientMetadata.jwks_uri) {
1619
- jwks = await fetchJwks(clientMetadata.jwks_uri, options.callbacks.fetch);
1620
- } else {
1621
- throw new Oauth2ServerErrorResponseError11({
1622
- error: Oauth2ErrorCodes10.InvalidRequest,
1623
- error_description: `Missing 'jwks' or 'jwks_uri' in client metadata. Cannot extract encryption JWK.`
1624
- });
1625
- }
1626
- if (clientMetadata.authorization_encrypted_response_alg || clientMetadata.authorization_encrypted_response_enc || clientMetadata.authorization_signed_response_alg) {
1627
- jarmAssertMetadataSupported({
1628
- clientMetadata,
1629
- serverMetadata: jarm.serverMetadata
1630
- });
1631
- }
1632
- const encJwk = (
1633
- // User-provided JWK takes precedence
1634
- jarm?.encryption?.jwk ?? extractEncryptionJwkFromJwks(jwks, {
1635
- supportedAlgValues: jarm.serverMetadata.authorization_encryption_alg_values_supported ?? (clientMetadata.authorization_encrypted_response_alg ? [clientMetadata.authorization_encrypted_response_alg] : void 0)
1636
- })
1637
- );
1638
- if (!encJwk) {
1639
- throw new Oauth2ServerErrorResponseError11({
1640
- error: Oauth2ErrorCodes10.InvalidRequest,
1641
- error_description: "No encryption JWK provided and could not extract encryption JWK from client metadata. Failed to create JARM response."
1642
- });
1643
- }
1644
- let enc;
1645
- if (clientMetadata.encrypted_response_enc_values_supported) {
1646
- enc = jarm.serverMetadata.authorization_encryption_enc_values_supported.find(
1647
- (enc2) => clientMetadata.encrypted_response_enc_values_supported?.includes(enc2)
1648
- ) ?? clientMetadata.encrypted_response_enc_values_supported[0];
1649
- } else {
1650
- enc = clientMetadata.authorization_encrypted_response_enc ?? "A128GCM";
1651
- }
1652
- assertValueSupported({
1653
- actual: enc,
1654
- supported: jarm.serverMetadata.authorization_encryption_enc_values_supported,
1655
- errorMessage: `Invalid 'enc' value ${enc}. Supported values are ${jarm.serverMetadata.authorization_encryption_enc_values_supported.join(", ")}`
1656
- });
1657
- const alg = encJwk.alg ?? clientMetadata.authorization_encrypted_response_alg ?? "ECDH-ES";
1658
- assertValueSupported({
1659
- actual: alg,
1660
- supported: jarm.serverMetadata.authorization_encryption_alg_values_supported,
1661
- errorMessage: `Invalid 'alg' value ${alg}. Supported values are ${jarm.serverMetadata.authorization_encryption_alg_values_supported.join(", ")}`
1662
- });
1663
- let additionalJwtPayload;
1664
- if (jarm?.jwtSigner) {
1665
- if (!jarm.authorizationServer) {
1666
- throw new Oauth2ServerErrorResponseError11({
1667
- error: Oauth2ErrorCodes10.InvalidRequest,
1668
- error_description: "Missing required iss in JARM configuration for creating OpenID4VP authorization response."
1669
- });
1670
- }
1671
- if (!jarm.audience) {
1672
- throw new Oauth2ServerErrorResponseError11({
1673
- error: Oauth2ErrorCodes10.InvalidRequest,
1674
- error_description: "Missing required aud in JARM configuration for creating OpenID4VP authorization response."
1675
- });
1676
- }
1677
- additionalJwtPayload = {
1678
- iss: jarm.authorizationServer,
1679
- aud: jarm.audience,
1680
- exp: jarm.expiresInSeconds ?? dateToSeconds3(addSecondsToDate2(/* @__PURE__ */ new Date(), 60 * 10))
1681
- // default: 10 minutes
1682
- };
1683
- }
1684
- const jarmResponsePayload = {
1685
- ...authorizationResponsePayload,
1686
- ...additionalJwtPayload
1687
- };
1688
- const result = await createJarmAuthorizationResponse({
1689
- jarmAuthorizationResponse: jarmResponsePayload,
1690
- jwtSigner: jarm?.jwtSigner,
1691
- jweEncryptor: jarm?.encryption ? {
1692
- method: "jwk",
1693
- publicJwk: encJwk,
1694
- apu: jarm.encryption.nonce ? encodeToBase64Url2(jarm.encryption.nonce) : void 0,
1695
- apv: encodeToBase64Url2(authorizationRequestPayload.nonce),
1696
- alg,
1697
- enc
1698
- } : void 0,
1699
- callbacks: {
1700
- signJwt: callbacks.signJwt,
1701
- encryptJwe: callbacks.encryptJwe
1702
- }
1703
- });
1704
- return {
1705
- authorizationResponsePayload: jarmResponsePayload,
1706
- jarm: { responseJwt: result.jarmAuthorizationResponseJwt, encryptionJwk: encJwk }
1707
- };
1201
+ const { authorizationRequestPayload, jarm, callbacks, origin } = options;
1202
+ const authorizationResponsePayload = {
1203
+ ...options.authorizationResponsePayload,
1204
+ state: authorizationRequestPayload.state
1205
+ };
1206
+ const { clientIdPrefix } = getOpenid4vpClientId({
1207
+ responseMode: authorizationRequestPayload.response_mode,
1208
+ clientId: authorizationRequestPayload.client_id,
1209
+ legacyClientIdScheme: authorizationRequestPayload.client_id_scheme,
1210
+ origin
1211
+ });
1212
+ if (authorizationRequestPayload.response_mode && isJarmResponseMode(authorizationRequestPayload.response_mode) && !jarm) throw new Oauth2Error(`Missing jarm options for creating Jarm response with response mode '${authorizationRequestPayload.response_mode}'`);
1213
+ if (!jarm) return { authorizationResponsePayload };
1214
+ if (clientIdPrefix === "openid_federation" && !options.clientMetadata) throw new Oauth2Error("When OpenID Federation is used as the client id prefix (https/openid_federation), passing externally fetched and verified 'clientMetadata' to the 'createOpenid4vpAuthorizationResponse' is required.");
1215
+ const clientMetadata = options.clientMetadata ?? authorizationRequestPayload.client_metadata;
1216
+ if (!clientMetadata) throw new Oauth2Error("Missing client metadata in the request params to assert Jarm metadata support.");
1217
+ let jwks;
1218
+ if (clientMetadata.jwks) jwks = clientMetadata.jwks;
1219
+ else if (clientMetadata.jwks_uri) jwks = await fetchJwks(clientMetadata.jwks_uri, options.callbacks.fetch);
1220
+ else throw new Oauth2ServerErrorResponseError({
1221
+ error: Oauth2ErrorCodes.InvalidRequest,
1222
+ error_description: `Missing 'jwks' or 'jwks_uri' in client metadata. Cannot extract encryption JWK.`
1223
+ });
1224
+ if (clientMetadata.authorization_encrypted_response_alg || clientMetadata.authorization_encrypted_response_enc || clientMetadata.authorization_signed_response_alg) jarmAssertMetadataSupported({
1225
+ clientMetadata,
1226
+ serverMetadata: jarm.serverMetadata
1227
+ });
1228
+ const encJwk = jarm?.encryption?.jwk ?? extractEncryptionJwkFromJwks(jwks, { supportedAlgValues: jarm.serverMetadata.authorization_encryption_alg_values_supported ?? (clientMetadata.authorization_encrypted_response_alg ? [clientMetadata.authorization_encrypted_response_alg] : void 0) });
1229
+ if (!encJwk) throw new Oauth2ServerErrorResponseError({
1230
+ error: Oauth2ErrorCodes.InvalidRequest,
1231
+ error_description: "No encryption JWK provided and could not extract encryption JWK from client metadata. Failed to create JARM response."
1232
+ });
1233
+ let enc;
1234
+ if (clientMetadata.encrypted_response_enc_values_supported) enc = jarm.serverMetadata.authorization_encryption_enc_values_supported.find((enc$1) => clientMetadata.encrypted_response_enc_values_supported?.includes(enc$1)) ?? clientMetadata.encrypted_response_enc_values_supported[0];
1235
+ else enc = clientMetadata.authorization_encrypted_response_enc ?? "A128GCM";
1236
+ assertValueSupported({
1237
+ actual: enc,
1238
+ supported: jarm.serverMetadata.authorization_encryption_enc_values_supported,
1239
+ errorMessage: `Invalid 'enc' value ${enc}. Supported values are ${jarm.serverMetadata.authorization_encryption_enc_values_supported.join(", ")}`
1240
+ });
1241
+ const alg = encJwk.alg ?? clientMetadata.authorization_encrypted_response_alg ?? "ECDH-ES";
1242
+ assertValueSupported({
1243
+ actual: alg,
1244
+ supported: jarm.serverMetadata.authorization_encryption_alg_values_supported,
1245
+ errorMessage: `Invalid 'alg' value ${alg}. Supported values are ${jarm.serverMetadata.authorization_encryption_alg_values_supported.join(", ")}`
1246
+ });
1247
+ let additionalJwtPayload;
1248
+ if (jarm?.jwtSigner) {
1249
+ if (!jarm.authorizationServer) throw new Oauth2ServerErrorResponseError({
1250
+ error: Oauth2ErrorCodes.InvalidRequest,
1251
+ error_description: "Missing required iss in JARM configuration for creating OpenID4VP authorization response."
1252
+ });
1253
+ if (!jarm.audience) throw new Oauth2ServerErrorResponseError({
1254
+ error: Oauth2ErrorCodes.InvalidRequest,
1255
+ error_description: "Missing required aud in JARM configuration for creating OpenID4VP authorization response."
1256
+ });
1257
+ additionalJwtPayload = {
1258
+ iss: jarm.authorizationServer,
1259
+ aud: jarm.audience,
1260
+ exp: jarm.expiresInSeconds ?? dateToSeconds(addSecondsToDate$1(/* @__PURE__ */ new Date(), 600))
1261
+ };
1262
+ }
1263
+ const jarmResponsePayload = {
1264
+ ...authorizationResponsePayload,
1265
+ ...additionalJwtPayload
1266
+ };
1267
+ return {
1268
+ authorizationResponsePayload: jarmResponsePayload,
1269
+ jarm: {
1270
+ responseJwt: (await createJarmAuthorizationResponse({
1271
+ jarmAuthorizationResponse: jarmResponsePayload,
1272
+ jwtSigner: jarm?.jwtSigner,
1273
+ jweEncryptor: jarm?.encryption ? {
1274
+ method: "jwk",
1275
+ publicJwk: encJwk,
1276
+ apu: jarm.encryption.nonce ? encodeToBase64Url(jarm.encryption.nonce) : void 0,
1277
+ apv: encodeToBase64Url(authorizationRequestPayload.nonce),
1278
+ alg,
1279
+ enc
1280
+ } : void 0,
1281
+ callbacks: {
1282
+ signJwt: callbacks.signJwt,
1283
+ encryptJwe: callbacks.encryptJwe
1284
+ }
1285
+ })).jarmAuthorizationResponseJwt,
1286
+ encryptionJwk: encJwk
1287
+ }
1288
+ };
1708
1289
  }
1709
1290
 
1710
- // src/authorization-response/submit-authorization-response.ts
1711
- import { Oauth2Error as Oauth2Error10 } from "@openid4vc/oauth2";
1712
- import { ContentType as ContentType4, createFetcher as createFetcher3 } from "@openid4vc/utils";
1713
- import { objectToQueryParams as objectToQueryParams3 } from "@openid4vc/utils";
1714
-
1715
- // src/jarm/jarm-authorizatino-response-send.ts
1716
- import { Oauth2Error as Oauth2Error9 } from "@openid4vc/oauth2";
1717
- import { ContentType as ContentType3, URL as URL4, createFetcher as createFetcher2 } from "@openid4vc/utils";
1718
- var jarmAuthorizationResponseSend = (options) => {
1719
- const { authorizationRequestPayload, jarmAuthorizationResponseJwt, callbacks } = options;
1720
- const responseEndpoint = authorizationRequestPayload.response_uri ?? authorizationRequestPayload.redirect_uri;
1721
- if (!responseEndpoint) {
1722
- throw new Oauth2Error9(`Either 'response_uri' or 'redirect_uri' MUST be present in the authorization request`);
1723
- }
1724
- const responseEndpointUrl = new URL4(responseEndpoint);
1725
- return handleDirectPostJwt(responseEndpointUrl, jarmAuthorizationResponseJwt, callbacks);
1726
- };
1727
- async function handleDirectPostJwt(responseEndpoint, responseJwt, callbacks) {
1728
- const response = await createFetcher2(callbacks.fetch)(responseEndpoint, {
1729
- method: "POST",
1730
- headers: { "Content-Type": ContentType3.XWwwFormUrlencoded },
1731
- body: `response=${responseJwt}`
1732
- });
1733
- return {
1734
- responseMode: "direct_post.jwt",
1735
- response
1736
- };
1737
- }
1291
+ //#endregion
1292
+ //#region src/models/z-pex.ts
1293
+ const zPexPresentationDefinition = z.record(z.any());
1294
+ const zPexPresentationSubmission = z.record(z.any());
1295
+
1296
+ //#endregion
1297
+ //#region src/vp-token/z-vp-token.ts
1298
+ const zVpTokenPresentationEntry = z.union([z.string(), z.record(z.any())], { message: "vp_token presentation entry must be string or object" });
1299
+ const zVpTokenPex = z.union([zVpTokenPresentationEntry, z.array(zVpTokenPresentationEntry).nonempty("Must have at least entry in vp_token array")], { message: "pex vp_token must be a string, object or non-empty array of strings and objects" });
1300
+ const zVpTokenDcql = z.record(z.union([z.array(zVpTokenPresentationEntry).nonempty(), zVpTokenPresentationEntry]), { message: "dcql vp_token must be an object with keys referencing the dcql credential query id, and values a non-empty array of strings and objects, or string, or object" });
1301
+ const zVpToken = zVpTokenDcql.or(zVpTokenPex);
1302
+
1303
+ //#endregion
1304
+ //#region src/authorization-response/z-authorization-response.ts
1305
+ const zOpenid4vpAuthorizationResponse = z.object({
1306
+ state: z.string().optional(),
1307
+ id_token: z.string().optional(),
1308
+ vp_token: zVpToken,
1309
+ presentation_submission: zPexPresentationSubmission.or(zStringToJson).optional(),
1310
+ refresh_token: z.string().optional(),
1311
+ token_type: z.string().optional(),
1312
+ access_token: z.string().optional(),
1313
+ expires_in: z.coerce.number().optional()
1314
+ }).passthrough();
1738
1315
 
1739
- // src/authorization-response/submit-authorization-response.ts
1740
- async function submitOpenid4vpAuthorizationResponse(options) {
1741
- const { authorizationRequestPayload, authorizationResponsePayload, jarm, callbacks } = options;
1742
- const url = authorizationRequestPayload.response_uri;
1743
- if (jarm) {
1744
- return jarmAuthorizationResponseSend({
1745
- authorizationRequestPayload,
1746
- jarmAuthorizationResponseJwt: jarm.responseJwt,
1747
- callbacks
1748
- });
1749
- }
1750
- if (!url) {
1751
- throw new Oauth2Error10(
1752
- "Failed to submit OpenId4Vp Authorization Response. No redirect_uri or response_uri provided."
1753
- );
1754
- }
1755
- const fetch = createFetcher3(callbacks.fetch);
1756
- const encodedResponse = objectToQueryParams3(authorizationResponsePayload);
1757
- const submissionResponse = await fetch(url, {
1758
- method: "POST",
1759
- body: encodedResponse.toString(),
1760
- headers: {
1761
- "Content-Type": ContentType4.XWwwFormUrlencoded
1762
- }
1763
- });
1764
- return {
1765
- responseMode: "direct_post",
1766
- response: submissionResponse
1767
- };
1316
+ //#endregion
1317
+ //#region src/authorization-response/parse-authorization-response-payload.ts
1318
+ function parseOpenid4VpAuthorizationResponsePayload(payload) {
1319
+ return parseWithErrorHandling(zOpenid4vpAuthorizationResponse, payload, "Failed to parse openid4vp authorization response.");
1768
1320
  }
1769
1321
 
1770
- // src/authorization-response/validate-authorization-response.ts
1771
- import { Oauth2Error as Oauth2Error11 } from "@openid4vc/oauth2";
1322
+ //#endregion
1323
+ //#region src/jarm/jarm-authorization-response/z-jarm-authorization-response.ts
1324
+ const zJarmHeader = z.object({
1325
+ ...zJwtHeader.shape,
1326
+ apu: z.string().optional(),
1327
+ apv: z.string().optional()
1328
+ });
1329
+ const zJarmAuthorizationResponse = z.object({
1330
+ ...zJwtPayload.shape,
1331
+ ...zJwtPayload.pick({
1332
+ iss: true,
1333
+ aud: true,
1334
+ exp: true
1335
+ }).required().shape,
1336
+ state: z.optional(z.string())
1337
+ }).passthrough();
1338
+ const zJarmAuthorizationResponseEncryptedOnly = z.object({
1339
+ ...zJwtPayload.shape,
1340
+ state: z.optional(z.string())
1341
+ }).passthrough();
1772
1342
 
1773
- // src/vp-token/parse-vp-token.ts
1774
- import { parseIfJson as parseIfJson2, parseWithErrorHandling as parseWithErrorHandling5 } from "@openid4vc/utils";
1343
+ //#endregion
1344
+ //#region src/jarm/jarm-authorization-response/jarm-validate-authorization-response.ts
1345
+ const jarmAuthorizationResponseValidate = (options) => {
1346
+ const { expectedClientId, authorizationResponse } = options;
1347
+ if (!zJarmAuthorizationResponse.safeParse(authorizationResponse).success) return;
1348
+ if (expectedClientId !== authorizationResponse.aud) throw new Oauth2Error(`Invalid 'aud' claim in JARM authorization response. Expected '${expectedClientId}' received '${JSON.stringify(authorizationResponse.aud)}'.`);
1349
+ if (authorizationResponse.exp !== void 0 && authorizationResponse.exp < dateToSeconds()) throw new Oauth2Error("Jarm auth response is expired.");
1350
+ };
1775
1351
 
1776
- // src/vp-token/z-vp-token.ts
1777
- import { z as z17 } from "zod";
1778
- var zVpTokenPresentationEntry = z17.union([z17.string(), z17.record(z17.any())], {
1779
- message: "vp_token presentation entry must be string or object"
1780
- });
1781
- var zVpTokenPex = z17.union(
1782
- [
1783
- zVpTokenPresentationEntry,
1784
- z17.array(zVpTokenPresentationEntry).nonempty("Must have at least entry in vp_token array")
1785
- ],
1786
- {
1787
- message: "pex vp_token must be a string, object or non-empty array of strings and objects"
1788
- }
1789
- );
1790
- var zVpTokenDcql = z17.record(
1791
- z17.union([z17.array(zVpTokenPresentationEntry).nonempty(), zVpTokenPresentationEntry]),
1792
- {
1793
- message: "dcql vp_token must be an object with keys referencing the dcql credential query id, and values a non-empty array of strings and objects, or string, or object"
1794
- }
1795
- );
1796
- var zVpToken = zVpTokenDcql.or(zVpTokenPex);
1352
+ //#endregion
1353
+ //#region src/jarm/jarm-authorization-response/verify-jarm-authorization-response.ts
1354
+ let JarmMode = /* @__PURE__ */ function(JarmMode$1) {
1355
+ JarmMode$1["Signed"] = "Signed";
1356
+ JarmMode$1["Encrypted"] = "Encrypted";
1357
+ JarmMode$1["SignedEncrypted"] = "SignedEncrypted";
1358
+ return JarmMode$1;
1359
+ }({});
1360
+ /**
1361
+ * The client decrypts the JWT using the default key for the respective issuer or,
1362
+ * if applicable, determined by the kid JWT header parameter.
1363
+ * The key might be a private key, where the corresponding public key is registered
1364
+ * with the expected issuer of the response ("use":"enc" via the client's metadata jwks or jwks_uri)
1365
+ * or a key derived from its client secret (see Section 2.2).
1366
+ */
1367
+ const decryptJarmAuthorizationResponseJwt = async (options) => {
1368
+ const { jarmAuthorizationResponseJwt, callbacks, authorizationRequestPayload } = options;
1369
+ let encryptionJwk;
1370
+ const { header } = decodeJwtHeader({ jwt: jarmAuthorizationResponseJwt });
1371
+ if (authorizationRequestPayload.client_metadata?.jwks) encryptionJwk = extractEncryptionJwkFromJwks(authorizationRequestPayload.client_metadata.jwks, {
1372
+ kid: header.kid,
1373
+ supportedAlgValues: authorizationRequestPayload.client_metadata.authorization_encrypted_response_alg ? [authorizationRequestPayload.client_metadata.authorization_encrypted_response_alg] : void 0
1374
+ });
1375
+ const result = await callbacks.decryptJwe(jarmAuthorizationResponseJwt, { jwk: encryptionJwk });
1376
+ if (!result.decrypted) throw new Oauth2Error("Failed to decrypt jarm auth response.");
1377
+ return {
1378
+ decryptionJwk: result.decryptionJwk,
1379
+ payload: result.payload
1380
+ };
1381
+ };
1382
+ /**
1383
+ * Validate a JARM direct_post.jwt compliant authentication response
1384
+ * * The decryption key should be resolvable using the the protected header's 'kid' field
1385
+ * * The signature verification jwk should be resolvable using the jws protected header's 'kid' field and the payload's 'iss' field.
1386
+ */
1387
+ async function verifyJarmAuthorizationResponse(options) {
1388
+ const { jarmAuthorizationResponseJwt, callbacks, expectedClientId, authorizationRequestPayload } = options;
1389
+ const requestDataIsEncrypted = zCompactJwe.safeParse(jarmAuthorizationResponseJwt).success;
1390
+ const decryptedRequestData = requestDataIsEncrypted ? await decryptJarmAuthorizationResponseJwt({
1391
+ jarmAuthorizationResponseJwt,
1392
+ callbacks,
1393
+ authorizationRequestPayload
1394
+ }) : {
1395
+ payload: jarmAuthorizationResponseJwt,
1396
+ decryptionJwk: void 0
1397
+ };
1398
+ const responseIsSigned = zCompactJwt.safeParse(decryptedRequestData.payload).success;
1399
+ if (!requestDataIsEncrypted && !responseIsSigned) throw new Oauth2Error("Jarm Auth Response must be either encrypted, signed, or signed and encrypted.");
1400
+ let jarmAuthorizationResponse;
1401
+ if (responseIsSigned) {
1402
+ const { header: jwsProtectedHeader, payload: jwsPayload } = decodeJwt({
1403
+ jwt: decryptedRequestData.payload,
1404
+ headerSchema: z$1.object({
1405
+ ...zJwtHeader.shape,
1406
+ kid: z$1.string()
1407
+ })
1408
+ });
1409
+ const response = zJarmAuthorizationResponse.parse(jwsPayload);
1410
+ const jwtSigner = jwtSignerFromJwt({
1411
+ header: jwsProtectedHeader,
1412
+ payload: jwsPayload
1413
+ });
1414
+ if (!(await options.callbacks.verifyJwt(jwtSigner, {
1415
+ compact: decryptedRequestData.payload,
1416
+ header: jwsProtectedHeader,
1417
+ payload: jwsPayload
1418
+ })).verified) throw new Oauth2Error("Jarm Auth Response is not valid.");
1419
+ jarmAuthorizationResponse = response;
1420
+ } else {
1421
+ const jsonRequestData = stringToJsonWithErrorHandling(decryptedRequestData.payload, "Unable to parse decrypted JARM JWE body to JSON");
1422
+ jarmAuthorizationResponse = zJarmAuthorizationResponseEncryptedOnly.parse(jsonRequestData);
1423
+ }
1424
+ jarmAuthorizationResponseValidate({
1425
+ expectedClientId,
1426
+ authorizationResponse: jarmAuthorizationResponse
1427
+ });
1428
+ const type = requestDataIsEncrypted && responseIsSigned ? JarmMode.SignedEncrypted : requestDataIsEncrypted ? JarmMode.Encrypted : JarmMode.Signed;
1429
+ const issuer = jarmAuthorizationResponse.iss;
1430
+ return {
1431
+ jarmAuthorizationResponse,
1432
+ type,
1433
+ issuer,
1434
+ decryptionJwk: decryptedRequestData.decryptionJwk
1435
+ };
1436
+ }
1797
1437
 
1798
- // src/vp-token/parse-vp-token.ts
1438
+ //#endregion
1439
+ //#region src/vp-token/parse-vp-token.ts
1799
1440
  function parsePexVpToken(vpToken) {
1800
- const parsedVpToken = parseWithErrorHandling5(
1801
- zVpTokenPex,
1802
- parseIfJson2(vpToken),
1803
- "Could not parse presentation exchange vp_token. Expected a string or an array of strings"
1804
- );
1805
- return Array.isArray(parsedVpToken) ? parsedVpToken : [parsedVpToken];
1441
+ const parsedVpToken = parseWithErrorHandling(zVpTokenPex, parseIfJson(vpToken), "Could not parse presentation exchange vp_token. Expected a string or an array of strings");
1442
+ return Array.isArray(parsedVpToken) ? parsedVpToken : [parsedVpToken];
1806
1443
  }
1807
1444
  function parseDcqlVpToken(vpToken) {
1808
- const parsedVpToken = parseWithErrorHandling5(
1809
- zVpTokenDcql,
1810
- parseIfJson2(vpToken),
1811
- "Could not parse dcql vp_token. Expected an object where the values are encoded presentations"
1812
- );
1813
- return Object.fromEntries(
1814
- Object.entries(parsedVpToken).map(([queryId, presentations]) => [
1815
- queryId,
1816
- Array.isArray(presentations) ? presentations : [presentations]
1817
- ])
1818
- );
1445
+ const parsedVpToken = parseWithErrorHandling(zVpTokenDcql, parseIfJson(vpToken), "Could not parse dcql vp_token. Expected an object where the values are encoded presentations");
1446
+ return Object.fromEntries(Object.entries(parsedVpToken).map(([queryId, presentations]) => [queryId, Array.isArray(presentations) ? presentations : [presentations]]));
1819
1447
  }
1820
1448
 
1821
- // src/authorization-response/validate-authorization-response.ts
1449
+ //#endregion
1450
+ //#region src/authorization-response/validate-authorization-response.ts
1451
+ /**
1452
+ * The following steps need to be performed outside of this library
1453
+ * - verifying the presentations
1454
+ * - validating the presentations against the presentation definition
1455
+ * - checking the revocation status of the presentations
1456
+ * - checking the nonce of the presentations matches the nonce of the request (for mdoc's)
1457
+ */
1822
1458
  function validateOpenid4vpAuthorizationResponsePayload(options) {
1823
- const { authorizationRequestPayload, authorizationResponsePayload } = options;
1824
- if (authorizationRequestPayload.state && authorizationRequestPayload.state !== authorizationResponsePayload.state) {
1825
- throw new Oauth2Error11("OpenId4Vp Authorization Response state mismatch.");
1826
- }
1827
- if (authorizationResponsePayload.id_token) {
1828
- throw new Oauth2Error11("OpenId4Vp Authorization Response id_token is not supported.");
1829
- }
1830
- if (authorizationResponsePayload.presentation_submission) {
1831
- if (!authorizationRequestPayload.presentation_definition) {
1832
- throw new Oauth2Error11("OpenId4Vp Authorization Request is missing the required presentation_definition.");
1833
- }
1834
- return {
1835
- type: "pex",
1836
- pex: authorizationRequestPayload.scope ? {
1837
- scope: authorizationRequestPayload.scope,
1838
- presentationSubmission: authorizationResponsePayload.presentation_submission,
1839
- presentations: parsePexVpToken(authorizationResponsePayload.vp_token)
1840
- } : {
1841
- presentationDefinition: authorizationRequestPayload.presentation_definition,
1842
- presentationSubmission: authorizationResponsePayload.presentation_submission,
1843
- presentations: parsePexVpToken(authorizationResponsePayload.vp_token)
1844
- }
1845
- };
1846
- }
1847
- if (authorizationRequestPayload.dcql_query) {
1848
- const presentations = parseDcqlVpToken(authorizationResponsePayload.vp_token);
1849
- return {
1850
- type: "dcql",
1851
- dcql: authorizationRequestPayload.scope ? {
1852
- scope: authorizationRequestPayload.scope,
1853
- presentations
1854
- } : {
1855
- query: authorizationRequestPayload.dcql_query,
1856
- presentations
1857
- }
1858
- };
1859
- }
1860
- throw new Oauth2Error11(
1861
- "Invalid OpenId4Vp Authorization Response. Response neither contains a presentation_submission nor request contains a dcql_query."
1862
- );
1459
+ const { authorizationRequestPayload, authorizationResponsePayload } = options;
1460
+ if (authorizationRequestPayload.state && authorizationRequestPayload.state !== authorizationResponsePayload.state) throw new Oauth2Error("OpenId4Vp Authorization Response state mismatch.");
1461
+ if (authorizationResponsePayload.id_token) throw new Oauth2Error("OpenId4Vp Authorization Response id_token is not supported.");
1462
+ if (authorizationResponsePayload.presentation_submission) {
1463
+ if (!authorizationRequestPayload.presentation_definition) throw new Oauth2Error("OpenId4Vp Authorization Request is missing the required presentation_definition.");
1464
+ return {
1465
+ type: "pex",
1466
+ pex: authorizationRequestPayload.scope ? {
1467
+ scope: authorizationRequestPayload.scope,
1468
+ presentationSubmission: authorizationResponsePayload.presentation_submission,
1469
+ presentations: parsePexVpToken(authorizationResponsePayload.vp_token)
1470
+ } : {
1471
+ presentationDefinition: authorizationRequestPayload.presentation_definition,
1472
+ presentationSubmission: authorizationResponsePayload.presentation_submission,
1473
+ presentations: parsePexVpToken(authorizationResponsePayload.vp_token)
1474
+ }
1475
+ };
1476
+ }
1477
+ if (authorizationRequestPayload.dcql_query) {
1478
+ const presentations = parseDcqlVpToken(authorizationResponsePayload.vp_token);
1479
+ return {
1480
+ type: "dcql",
1481
+ dcql: authorizationRequestPayload.scope ? {
1482
+ scope: authorizationRequestPayload.scope,
1483
+ presentations
1484
+ } : {
1485
+ query: authorizationRequestPayload.dcql_query,
1486
+ presentations
1487
+ }
1488
+ };
1489
+ }
1490
+ throw new Oauth2Error("Invalid OpenId4Vp Authorization Response. Response neither contains a presentation_submission nor request contains a dcql_query.");
1863
1491
  }
1864
1492
 
1865
- // src/authorization-response/parse-authorization-response.ts
1866
- import { Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError12 } from "@openid4vc/oauth2";
1867
-
1868
- // src/authorization-response/parse-authorization-response-payload.ts
1869
- import { parseWithErrorHandling as parseWithErrorHandling6 } from "@openid4vc/utils";
1870
-
1871
- // src/authorization-response/z-authorization-response.ts
1872
- import { zStringToJson as zStringToJson2 } from "@openid4vc/utils";
1873
- import { z as z19 } from "zod";
1874
-
1875
- // src/models/z-pex.ts
1876
- import { z as z18 } from "zod";
1877
- var zPexPresentationDefinition = z18.record(z18.any());
1878
- var zPexPresentationSubmission = z18.record(z18.any());
1879
-
1880
- // src/authorization-response/z-authorization-response.ts
1881
- var zOpenid4vpAuthorizationResponse = z19.object({
1882
- state: z19.string().optional(),
1883
- id_token: z19.string().optional(),
1884
- vp_token: zVpToken,
1885
- presentation_submission: zPexPresentationSubmission.or(zStringToJson2).optional(),
1886
- refresh_token: z19.string().optional(),
1887
- token_type: z19.string().optional(),
1888
- access_token: z19.string().optional(),
1889
- expires_in: z19.coerce.number().optional()
1890
- }).passthrough();
1493
+ //#endregion
1494
+ //#region src/authorization-response/parse-jarm-authorization-response.ts
1495
+ async function parseJarmAuthorizationResponse(options) {
1496
+ const { jarmResponseJwt, callbacks, authorizationRequestPayload, expectedClientId } = options;
1497
+ const jarmAuthorizationResponseJwt = parseWithErrorHandling(z$1.union([zCompactJwt, zCompactJwe]), jarmResponseJwt, "Invalid jarm authorization response jwt.");
1498
+ const verifiedJarmResponse = await verifyJarmAuthorizationResponse({
1499
+ jarmAuthorizationResponseJwt,
1500
+ callbacks,
1501
+ expectedClientId,
1502
+ authorizationRequestPayload
1503
+ });
1504
+ const { header: jarmHeader } = decodeJwtHeader({
1505
+ jwt: jarmAuthorizationResponseJwt,
1506
+ headerSchema: zJarmHeader
1507
+ });
1508
+ const authorizationResponsePayload = parseOpenid4VpAuthorizationResponsePayload(verifiedJarmResponse.jarmAuthorizationResponse);
1509
+ const validateOpenId4vpResponse = validateOpenid4vpAuthorizationResponsePayload({
1510
+ authorizationRequestPayload,
1511
+ authorizationResponsePayload
1512
+ });
1513
+ if (!authorizationRequestPayload.response_mode || !isJarmResponseMode(authorizationRequestPayload.response_mode)) throw new Oauth2Error(`Invalid response mode for jarm response. Response mode: '${authorizationRequestPayload.response_mode ?? "fragment"}'`);
1514
+ return {
1515
+ ...validateOpenId4vpResponse,
1516
+ jarm: {
1517
+ ...verifiedJarmResponse,
1518
+ jarmHeader
1519
+ },
1520
+ expectedNonce: authorizationRequestPayload.nonce,
1521
+ authorizationResponsePayload
1522
+ };
1523
+ }
1891
1524
 
1892
- // src/authorization-response/parse-authorization-response-payload.ts
1893
- function parseOpenid4VpAuthorizationResponsePayload(payload) {
1894
- return parseWithErrorHandling6(
1895
- zOpenid4vpAuthorizationResponse,
1896
- payload,
1897
- "Failed to parse openid4vp authorization response."
1898
- );
1525
+ //#endregion
1526
+ //#region src/authorization-response/parse-authorization-response.ts
1527
+ async function parseOpenid4vpAuthorizationResponse(options) {
1528
+ const { authorizationResponse, callbacks, authorizationRequestPayload, origin } = options;
1529
+ const expectedClientId = getOpenid4vpClientId({
1530
+ origin,
1531
+ responseMode: authorizationRequestPayload.response_mode,
1532
+ clientId: authorizationRequestPayload.client_id,
1533
+ legacyClientIdScheme: authorizationRequestPayload.client_id_scheme
1534
+ });
1535
+ if (authorizationResponse.response) return parseJarmAuthorizationResponse({
1536
+ jarmResponseJwt: authorizationResponse.response,
1537
+ callbacks,
1538
+ authorizationRequestPayload,
1539
+ expectedClientId: expectedClientId.effectiveClientId
1540
+ });
1541
+ const authorizationResponsePayload = parseOpenid4VpAuthorizationResponsePayload(authorizationResponse);
1542
+ const validatedOpenId4vpResponse = validateOpenid4vpAuthorizationResponsePayload({
1543
+ authorizationRequestPayload,
1544
+ authorizationResponsePayload
1545
+ });
1546
+ if (authorizationRequestPayload.response_mode && isJarmResponseMode(authorizationRequestPayload.response_mode)) throw new Oauth2ServerErrorResponseError({
1547
+ error: "invalid_request",
1548
+ error_description: "Invalid response mode for openid4vp response. Expected jarm response."
1549
+ }, { status: 400 });
1550
+ return {
1551
+ ...validatedOpenId4vpResponse,
1552
+ expectedNonce: authorizationRequestPayload.nonce,
1553
+ authorizationResponsePayload,
1554
+ jarm: void 0
1555
+ };
1899
1556
  }
1900
1557
 
1901
- // src/authorization-response/parse-jarm-authorization-response.ts
1902
- import { Oauth2Error as Oauth2Error12, decodeJwtHeader as decodeJwtHeader2, zCompactJwe as zCompactJwe3, zCompactJwt as zCompactJwt3 } from "@openid4vc/oauth2";
1903
- import { parseWithErrorHandling as parseWithErrorHandling7 } from "@openid4vc/utils";
1904
- import z20 from "zod";
1905
- async function parseJarmAuthorizationResponse(options) {
1906
- const { jarmResponseJwt, callbacks, authorizationRequestPayload, expectedClientId } = options;
1907
- const jarmAuthorizationResponseJwt = parseWithErrorHandling7(
1908
- z20.union([zCompactJwt3, zCompactJwe3]),
1909
- jarmResponseJwt,
1910
- "Invalid jarm authorization response jwt."
1911
- );
1912
- const verifiedJarmResponse = await verifyJarmAuthorizationResponse({
1913
- jarmAuthorizationResponseJwt,
1914
- callbacks,
1915
- expectedClientId,
1916
- authorizationRequestPayload
1917
- });
1918
- const { header: jarmHeader } = decodeJwtHeader2({
1919
- jwt: jarmAuthorizationResponseJwt,
1920
- headerSchema: zJarmHeader
1921
- });
1922
- const authorizationResponsePayload = parseOpenid4VpAuthorizationResponsePayload(
1923
- verifiedJarmResponse.jarmAuthorizationResponse
1924
- );
1925
- const validateOpenId4vpResponse = validateOpenid4vpAuthorizationResponsePayload({
1926
- authorizationRequestPayload,
1927
- authorizationResponsePayload
1928
- });
1929
- if (!authorizationRequestPayload.response_mode || !isJarmResponseMode(authorizationRequestPayload.response_mode)) {
1930
- throw new Oauth2Error12(
1931
- `Invalid response mode for jarm response. Response mode: '${authorizationRequestPayload.response_mode ?? "fragment"}'`
1932
- );
1933
- }
1934
- return {
1935
- ...validateOpenId4vpResponse,
1936
- jarm: { ...verifiedJarmResponse, jarmHeader },
1937
- expectedNonce: authorizationRequestPayload.nonce,
1938
- authorizationResponsePayload
1939
- };
1558
+ //#endregion
1559
+ //#region src/jarm/jarm-authorizatino-response-send.ts
1560
+ const jarmAuthorizationResponseSend = (options) => {
1561
+ const { authorizationRequestPayload, jarmAuthorizationResponseJwt, callbacks } = options;
1562
+ const responseEndpoint = authorizationRequestPayload.response_uri ?? authorizationRequestPayload.redirect_uri;
1563
+ if (!responseEndpoint) throw new Oauth2Error(`Either 'response_uri' or 'redirect_uri' MUST be present in the authorization request`);
1564
+ return handleDirectPostJwt(new URL(responseEndpoint), jarmAuthorizationResponseJwt, callbacks);
1565
+ };
1566
+ async function handleDirectPostJwt(responseEndpoint, responseJwt, callbacks) {
1567
+ return {
1568
+ responseMode: "direct_post.jwt",
1569
+ response: await createFetcher(callbacks.fetch)(responseEndpoint, {
1570
+ method: "POST",
1571
+ headers: { "Content-Type": ContentType.XWwwFormUrlencoded },
1572
+ body: `response=${responseJwt}`
1573
+ })
1574
+ };
1940
1575
  }
1941
1576
 
1942
- // src/authorization-response/parse-authorization-response.ts
1943
- async function parseOpenid4vpAuthorizationResponse(options) {
1944
- const { authorizationResponse, callbacks, authorizationRequestPayload, origin } = options;
1945
- const expectedClientId = getOpenid4vpClientId({
1946
- origin,
1947
- responseMode: authorizationRequestPayload.response_mode,
1948
- clientId: authorizationRequestPayload.client_id,
1949
- legacyClientIdScheme: authorizationRequestPayload.client_id_scheme
1950
- });
1951
- if (authorizationResponse.response) {
1952
- return parseJarmAuthorizationResponse({
1953
- jarmResponseJwt: authorizationResponse.response,
1954
- callbacks,
1955
- authorizationRequestPayload,
1956
- expectedClientId: expectedClientId.effectiveClientId
1957
- });
1958
- }
1959
- const authorizationResponsePayload = parseOpenid4VpAuthorizationResponsePayload(authorizationResponse);
1960
- const validatedOpenId4vpResponse = validateOpenid4vpAuthorizationResponsePayload({
1961
- authorizationRequestPayload,
1962
- authorizationResponsePayload
1963
- });
1964
- if (authorizationRequestPayload.response_mode && isJarmResponseMode(authorizationRequestPayload.response_mode)) {
1965
- throw new Oauth2ServerErrorResponseError12(
1966
- {
1967
- error: "invalid_request",
1968
- error_description: "Invalid response mode for openid4vp response. Expected jarm response."
1969
- },
1970
- {
1971
- status: 400
1972
- }
1973
- );
1974
- }
1975
- return {
1976
- ...validatedOpenId4vpResponse,
1977
- expectedNonce: authorizationRequestPayload.nonce,
1978
- authorizationResponsePayload,
1979
- jarm: void 0
1980
- };
1577
+ //#endregion
1578
+ //#region src/authorization-response/submit-authorization-response.ts
1579
+ async function submitOpenid4vpAuthorizationResponse(options) {
1580
+ const { authorizationRequestPayload, authorizationResponsePayload, jarm, callbacks } = options;
1581
+ const url = authorizationRequestPayload.response_uri;
1582
+ if (jarm) return jarmAuthorizationResponseSend({
1583
+ authorizationRequestPayload,
1584
+ jarmAuthorizationResponseJwt: jarm.responseJwt,
1585
+ callbacks
1586
+ });
1587
+ if (!url) throw new Oauth2Error("Failed to submit OpenId4Vp Authorization Response. No redirect_uri or response_uri provided.");
1588
+ return {
1589
+ responseMode: "direct_post",
1590
+ response: await createFetcher(callbacks.fetch)(url, {
1591
+ method: "POST",
1592
+ body: objectToQueryParams(authorizationResponsePayload).toString(),
1593
+ headers: { "Content-Type": ContentType.XWwwFormUrlencoded }
1594
+ })
1595
+ };
1981
1596
  }
1982
1597
 
1983
- // src/Openid4vpClient.ts
1598
+ //#endregion
1599
+ //#region src/models/z-credential-formats.ts
1600
+ const zCredentialFormat = z.enum([
1601
+ "jwt_vc_json",
1602
+ "ldp_vc",
1603
+ "mso_mdoc",
1604
+ "dc+sd-jwt",
1605
+ "vc+sd-jwt"
1606
+ ]);
1607
+
1608
+ //#endregion
1609
+ //#region src/models/z-proof-formats.ts
1610
+ const zProofFormat = z.enum([
1611
+ "jwt_vp_json",
1612
+ "ldc_vp",
1613
+ "ac_vp",
1614
+ "dc+sd-jwt",
1615
+ "vc+sd-jwt",
1616
+ "mso_mdoc"
1617
+ ]);
1618
+
1619
+ //#endregion
1620
+ //#region src/models/z-wallet-metadata.ts
1621
+ const zWalletMetadata = z.object({
1622
+ presentation_definition_uri_supported: z.optional(z.boolean()),
1623
+ vp_formats_supported: z.optional(zVpFormatsSupported.or(zLegacyVpFormats)),
1624
+ client_id_schemes_supported: z.optional(z.array(zClientIdPrefix.exclude(["decentralized_identifier", "openid_federation"]))),
1625
+ client_id_prefixes_supported: z.optional(z.array(zUniformClientIdPrefix)),
1626
+ request_object_signing_alg_values_supported: z.optional(z.array(z.string())),
1627
+ authorization_encryption_alg_values_supported: z.optional(z.array(z.string())),
1628
+ authorization_encryption_enc_values_supported: z.optional(z.array(z.string()))
1629
+ });
1630
+
1631
+ //#endregion
1632
+ //#region src/Openid4vpClient.ts
1984
1633
  var Openid4vpClient = class {
1985
- constructor(options) {
1986
- this.options = options;
1987
- }
1988
- parseOpenid4vpAuthorizationRequest(options) {
1989
- return parseOpenid4vpAuthorizationRequest(options);
1990
- }
1991
- async resolveOpenId4vpAuthorizationRequest(options) {
1992
- return resolveOpenid4vpAuthorizationRequest({ ...options, callbacks: this.options.callbacks });
1993
- }
1994
- async createOpenid4vpAuthorizationResponse(options) {
1995
- return createOpenid4vpAuthorizationResponse({ ...options, callbacks: this.options.callbacks });
1996
- }
1997
- async submitOpenid4vpAuthorizationResponse(options) {
1998
- return submitOpenid4vpAuthorizationResponse({ ...options, callbacks: this.options.callbacks });
1999
- }
1634
+ constructor(options) {
1635
+ this.options = options;
1636
+ }
1637
+ parseOpenid4vpAuthorizationRequest(options) {
1638
+ return parseOpenid4vpAuthorizationRequest(options);
1639
+ }
1640
+ async resolveOpenId4vpAuthorizationRequest(options) {
1641
+ return resolveOpenid4vpAuthorizationRequest({
1642
+ ...options,
1643
+ callbacks: this.options.callbacks
1644
+ });
1645
+ }
1646
+ async createOpenid4vpAuthorizationResponse(options) {
1647
+ return createOpenid4vpAuthorizationResponse({
1648
+ ...options,
1649
+ callbacks: this.options.callbacks
1650
+ });
1651
+ }
1652
+ async submitOpenid4vpAuthorizationResponse(options) {
1653
+ return submitOpenid4vpAuthorizationResponse({
1654
+ ...options,
1655
+ callbacks: this.options.callbacks
1656
+ });
1657
+ }
2000
1658
  };
2001
1659
 
2002
- // src/transaction-data/verify-transaction-data.ts
2003
- import {
2004
- HashAlgorithm as HashAlgorithm2,
2005
- Oauth2ErrorCodes as Oauth2ErrorCodes11,
2006
- Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError13
2007
- } from "@openid4vc/oauth2";
2008
- import { decodeUtf8String, encodeToBase64Url as encodeToBase64Url3 } from "@openid4vc/utils";
1660
+ //#endregion
1661
+ //#region src/transaction-data/verify-transaction-data.ts
2009
1662
  async function verifyTransactionData(options) {
2010
- const parsedTransactionData = parseTransactionData({
2011
- transactionData: options.transactionData
2012
- });
2013
- const matchedEntries = [];
2014
- for (const parsedEntry of parsedTransactionData) {
2015
- const matchedEntry = await verifyTransactionDataEntry({
2016
- entry: parsedEntry,
2017
- callbacks: options.callbacks,
2018
- credentials: options.credentials
2019
- });
2020
- matchedEntries.push(matchedEntry);
2021
- }
2022
- return matchedEntries;
1663
+ const parsedTransactionData = parseTransactionData({ transactionData: options.transactionData });
1664
+ const matchedEntries = [];
1665
+ for (const parsedEntry of parsedTransactionData) {
1666
+ const matchedEntry = await verifyTransactionDataEntry({
1667
+ entry: parsedEntry,
1668
+ callbacks: options.callbacks,
1669
+ credentials: options.credentials
1670
+ });
1671
+ matchedEntries.push(matchedEntry);
1672
+ }
1673
+ return matchedEntries;
2023
1674
  }
2024
- async function verifyTransactionDataEntry({
2025
- entry,
2026
- credentials,
2027
- callbacks
2028
- }) {
2029
- const allowedAlgs = entry.transactionData.transaction_data_hashes_alg ?? ["sha-256"];
2030
- const supportedAlgs = allowedAlgs.filter(
2031
- (alg) => Object.values(HashAlgorithm2).includes(alg)
2032
- );
2033
- const hashes = {};
2034
- for (const alg of supportedAlgs) {
2035
- hashes[alg] = encodeToBase64Url3(await callbacks.hash(decodeUtf8String(entry.encoded), alg));
2036
- }
2037
- for (const credentialId of entry.transactionData.credential_ids) {
2038
- const transactionDataHashesCredentials = credentials[credentialId];
2039
- if (!transactionDataHashesCredentials) continue;
2040
- const presentations = [];
2041
- for (const transactionDataHashesCredential of transactionDataHashesCredentials) {
2042
- const alg = transactionDataHashesCredential.transaction_data_hashes_alg ?? "sha-256";
2043
- const hash = hashes[alg];
2044
- const presentationIndex = transactionDataHashesCredentials.indexOf(transactionDataHashesCredential);
2045
- if (!allowedAlgs.includes(alg)) {
2046
- throw new Oauth2ServerErrorResponseError13({
2047
- error: Oauth2ErrorCodes11.InvalidTransactionData,
2048
- error_description: `Transaction data entry with index ${entry.transactionDataIndex} for presentation ${credentialId} with index ${presentationIndex} is hashed using alg '${alg}'. However transaction data only allows alg values ${allowedAlgs.join(", ")}.`
2049
- });
2050
- }
2051
- if (!hash) {
2052
- throw new Oauth2ServerErrorResponseError13({
2053
- error: Oauth2ErrorCodes11.InvalidTransactionData,
2054
- error_description: `Transaction data entry with index ${entry.transactionDataIndex} for presentation ${credentialId} with index ${presentationIndex} is hashed using unsupported alg '${alg}'. This library only supports verification of transaction data hashes using alg values ${Object.values(HashAlgorithm2).join(", ")}. Either verify the hashes outside of this library, or limit the allowed alg values to the ones supported by this library.`
2055
- });
2056
- }
2057
- const credentialHashIndex = transactionDataHashesCredential.transaction_data_hashes.indexOf(hash);
2058
- if (credentialHashIndex === -1) {
2059
- throw new Oauth2ServerErrorResponseError13({
2060
- error: Oauth2ErrorCodes11.InvalidTransactionData,
2061
- error_description: `Transaction data entry with index ${entry.transactionDataIndex} for presentation ${credentialId} with index ${presentationIndex} does not have a matching hash in the transaction_data_hashes`
2062
- });
2063
- }
2064
- presentations.push({
2065
- credentialHashIndex,
2066
- hash,
2067
- hashAlg: alg,
2068
- presentationIndex
2069
- });
2070
- }
2071
- return {
2072
- transactionDataEntry: entry,
2073
- credentialId,
2074
- presentations
2075
- };
2076
- }
2077
- throw new Oauth2ServerErrorResponseError13({
2078
- error: Oauth2ErrorCodes11.InvalidTransactionData,
2079
- error_description: `Transaction data entry with index ${entry.transactionDataIndex} does not have a matching hash in any of the submitted credentials`
2080
- });
1675
+ async function verifyTransactionDataEntry({ entry, credentials, callbacks }) {
1676
+ const allowedAlgs = entry.transactionData.transaction_data_hashes_alg ?? ["sha-256"];
1677
+ const supportedAlgs = allowedAlgs.filter((alg) => Object.values(HashAlgorithm).includes(alg));
1678
+ const hashes = {};
1679
+ for (const alg of supportedAlgs) hashes[alg] = encodeToBase64Url(await callbacks.hash(decodeUtf8String(entry.encoded), alg));
1680
+ for (const credentialId of entry.transactionData.credential_ids) {
1681
+ const transactionDataHashesCredentials = credentials[credentialId];
1682
+ if (!transactionDataHashesCredentials) continue;
1683
+ const presentations = [];
1684
+ for (const transactionDataHashesCredential of transactionDataHashesCredentials) {
1685
+ const alg = transactionDataHashesCredential.transaction_data_hashes_alg ?? "sha-256";
1686
+ const hash = hashes[alg];
1687
+ const presentationIndex = transactionDataHashesCredentials.indexOf(transactionDataHashesCredential);
1688
+ if (!allowedAlgs.includes(alg)) throw new Oauth2ServerErrorResponseError({
1689
+ error: Oauth2ErrorCodes.InvalidTransactionData,
1690
+ error_description: `Transaction data entry with index ${entry.transactionDataIndex} for presentation ${credentialId} with index ${presentationIndex} is hashed using alg '${alg}'. However transaction data only allows alg values ${allowedAlgs.join(", ")}.`
1691
+ });
1692
+ if (!hash) throw new Oauth2ServerErrorResponseError({
1693
+ error: Oauth2ErrorCodes.InvalidTransactionData,
1694
+ error_description: `Transaction data entry with index ${entry.transactionDataIndex} for presentation ${credentialId} with index ${presentationIndex} is hashed using unsupported alg '${alg}'. This library only supports verification of transaction data hashes using alg values ${Object.values(HashAlgorithm).join(", ")}. Either verify the hashes outside of this library, or limit the allowed alg values to the ones supported by this library.`
1695
+ });
1696
+ const credentialHashIndex = transactionDataHashesCredential.transaction_data_hashes.indexOf(hash);
1697
+ if (credentialHashIndex === -1) throw new Oauth2ServerErrorResponseError({
1698
+ error: Oauth2ErrorCodes.InvalidTransactionData,
1699
+ error_description: `Transaction data entry with index ${entry.transactionDataIndex} for presentation ${credentialId} with index ${presentationIndex} does not have a matching hash in the transaction_data_hashes`
1700
+ });
1701
+ presentations.push({
1702
+ credentialHashIndex,
1703
+ hash,
1704
+ hashAlg: alg,
1705
+ presentationIndex
1706
+ });
1707
+ }
1708
+ return {
1709
+ transactionDataEntry: entry,
1710
+ credentialId,
1711
+ presentations
1712
+ };
1713
+ }
1714
+ throw new Oauth2ServerErrorResponseError({
1715
+ error: Oauth2ErrorCodes.InvalidTransactionData,
1716
+ error_description: `Transaction data entry with index ${entry.transactionDataIndex} does not have a matching hash in any of the submitted credentials`
1717
+ });
2081
1718
  }
2082
1719
 
2083
- // src/Openid4vpVerifier.ts
1720
+ //#endregion
1721
+ //#region src/Openid4vpVerifier.ts
2084
1722
  var Openid4vpVerifier = class {
2085
- constructor(options) {
2086
- this.options = options;
2087
- }
2088
- async createOpenId4vpAuthorizationRequest(options) {
2089
- return createOpenid4vpAuthorizationRequest({ ...options, callbacks: this.options.callbacks });
2090
- }
2091
- parseOpenid4vpAuthorizationRequestPayload(options) {
2092
- return parseOpenid4vpAuthorizationRequest(options);
2093
- }
2094
- parseOpenid4vpAuthorizationResponse(options) {
2095
- return parseOpenid4vpAuthorizationResponse(options);
2096
- }
2097
- validateOpenid4vpAuthorizationResponsePayload(options) {
2098
- return validateOpenid4vpAuthorizationResponsePayload(options);
2099
- }
2100
- parsePexVpToken(vpToken) {
2101
- return parsePexVpToken(vpToken);
2102
- }
2103
- parseDcqlVpToken(vpToken) {
2104
- return parseDcqlVpToken(vpToken);
2105
- }
2106
- parseTransactionData(options) {
2107
- return parseTransactionData(options);
2108
- }
2109
- /**
2110
- * Verify transaction data against submitted credentials.
2111
- *
2112
- * NOTE: this expects transaction data based authorization based on hashes. This is the method defined
2113
- * for SD-JWT VC, but for mDOCs it's much more generic. If you're using transaction data with mDOCs based
2114
- * on hashes, you can extract the values from the DeviceResponse, otherwise you must verify the transaction data
2115
- * manually.
2116
- */
2117
- verifyTransactionData(options) {
2118
- return verifyTransactionData({
2119
- ...options,
2120
- callbacks: this.options.callbacks
2121
- });
2122
- }
1723
+ constructor(options) {
1724
+ this.options = options;
1725
+ }
1726
+ async createOpenId4vpAuthorizationRequest(options) {
1727
+ return createOpenid4vpAuthorizationRequest({
1728
+ ...options,
1729
+ callbacks: this.options.callbacks
1730
+ });
1731
+ }
1732
+ parseOpenid4vpAuthorizationRequestPayload(options) {
1733
+ return parseOpenid4vpAuthorizationRequest(options);
1734
+ }
1735
+ parseOpenid4vpAuthorizationResponse(options) {
1736
+ return parseOpenid4vpAuthorizationResponse(options);
1737
+ }
1738
+ validateOpenid4vpAuthorizationResponsePayload(options) {
1739
+ return validateOpenid4vpAuthorizationResponsePayload(options);
1740
+ }
1741
+ parsePexVpToken(vpToken) {
1742
+ return parsePexVpToken(vpToken);
1743
+ }
1744
+ parseDcqlVpToken(vpToken) {
1745
+ return parseDcqlVpToken(vpToken);
1746
+ }
1747
+ parseTransactionData(options) {
1748
+ return parseTransactionData(options);
1749
+ }
1750
+ /**
1751
+ * Verify transaction data against submitted credentials.
1752
+ *
1753
+ * NOTE: this expects transaction data based authorization based on hashes. This is the method defined
1754
+ * for SD-JWT VC, but for mDOCs it's much more generic. If you're using transaction data with mDOCs based
1755
+ * on hashes, you can extract the values from the DeviceResponse, otherwise you must verify the transaction data
1756
+ * manually.
1757
+ */
1758
+ verifyTransactionData(options) {
1759
+ return verifyTransactionData({
1760
+ ...options,
1761
+ callbacks: this.options.callbacks
1762
+ });
1763
+ }
2123
1764
  };
2124
1765
 
2125
- // src/models/z-credential-formats.ts
2126
- import { z as z21 } from "zod";
2127
- var zCredentialFormat = z21.enum(["jwt_vc_json", "ldp_vc", "mso_mdoc", "dc+sd-jwt", "vc+sd-jwt"]);
2128
-
2129
- // src/models/z-proof-formats.ts
2130
- import { z as z22 } from "zod";
2131
- var zProofFormat = z22.enum(["jwt_vp_json", "ldc_vp", "ac_vp", "dc+sd-jwt", "vc+sd-jwt", "mso_mdoc"]);
2132
-
2133
- // src/models/z-wallet-metadata.ts
2134
- import { z as z23 } from "zod";
2135
- var zWalletMetadata = z23.object({
2136
- presentation_definition_uri_supported: z23.optional(z23.boolean()),
2137
- // Up until draft 26 the legacy format was used
2138
- vp_formats_supported: z23.optional(zVpFormatsSupported.or(zLegacyVpFormats)),
2139
- client_id_schemes_supported: z23.optional(
2140
- // client_id_schemes_supported was from before decentralized_identifier and openid_federation were defined
2141
- z23.array(zClientIdPrefix.exclude(["decentralized_identifier", "openid_federation"]))
2142
- ),
2143
- client_id_prefixes_supported: z23.optional(z23.array(zUniformClientIdPrefix)),
2144
- request_object_signing_alg_values_supported: z23.optional(z23.array(z23.string())),
2145
- authorization_encryption_alg_values_supported: z23.optional(z23.array(z23.string())),
2146
- authorization_encryption_enc_values_supported: z23.optional(z23.array(z23.string()))
2147
- });
2148
- export {
2149
- JarmMode,
2150
- Openid4vpClient,
2151
- Openid4vpVerifier,
2152
- createOpenid4vpAuthorizationRequest,
2153
- createOpenid4vpAuthorizationResponse,
2154
- extractEncryptionJwkFromJwks,
2155
- getOpenid4vpClientId,
2156
- isJarmResponseMode,
2157
- isOpenid4vpAuthorizationRequestDcApi,
2158
- parseAuthorizationRequestVersion,
2159
- parseDcqlVpToken,
2160
- parseJarmAuthorizationResponse,
2161
- parseOpenid4VpAuthorizationResponsePayload,
2162
- parseOpenid4vpAuthorizationRequest,
2163
- parseOpenid4vpAuthorizationResponse,
2164
- parsePexVpToken,
2165
- parseTransactionData,
2166
- resolveOpenid4vpAuthorizationRequest,
2167
- submitOpenid4vpAuthorizationResponse,
2168
- validateOpenid4vpAuthorizationRequestPayload,
2169
- validateOpenid4vpAuthorizationResponsePayload,
2170
- verifyJarmAuthorizationResponse,
2171
- zClientIdPrefix,
2172
- zClientMetadata,
2173
- zCredentialFormat,
2174
- zJarmClientMetadata,
2175
- zOpenid4vpAuthorizationResponse,
2176
- zProofFormat,
2177
- zVerifierAttestations,
2178
- zWalletMetadata
2179
- };
1766
+ //#endregion
1767
+ export { JarmMode, Openid4vpClient, Openid4vpVerifier, createOpenid4vpAuthorizationRequest, createOpenid4vpAuthorizationResponse, extractEncryptionJwkFromJwks, getOpenid4vpClientId, isJarmResponseMode, isOpenid4vpAuthorizationRequestDcApi, parseAuthorizationRequestVersion, parseDcqlVpToken, parseJarmAuthorizationResponse, parseOpenid4VpAuthorizationResponsePayload, parseOpenid4vpAuthorizationRequest, parseOpenid4vpAuthorizationResponse, parsePexVpToken, parseTransactionData, resolveOpenid4vpAuthorizationRequest, submitOpenid4vpAuthorizationResponse, validateOpenid4vpAuthorizationRequestPayload, validateOpenid4vpAuthorizationResponsePayload, verifyJarmAuthorizationResponse, zClientIdPrefix, zClientMetadata, zCredentialFormat, zJarmClientMetadata, zOpenid4vpAuthorizationResponse, zProofFormat, zVerifierAttestations, zWalletMetadata };
2180
1768
  //# sourceMappingURL=index.mjs.map