@openid4vc/openid4vp 0.3.0-alpha-20250315153126 → 0.3.0-alpha-20250318163628
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.d.mts +5274 -5264
- package/dist/index.d.ts +5274 -5264
- package/dist/index.js +666 -636
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +733 -704
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,58 +1,50 @@
|
|
|
1
|
-
// src/client-identifier-scheme/
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
"pre-registered",
|
|
5
|
-
"redirect_uri",
|
|
6
|
-
"https",
|
|
7
|
-
"verifier_attestation",
|
|
8
|
-
"did",
|
|
9
|
-
"x509_san_dns",
|
|
10
|
-
"x509_san_uri",
|
|
11
|
-
"web-origin"
|
|
12
|
-
]);
|
|
1
|
+
// src/client-identifier-scheme/parse-client-identifier-scheme.ts
|
|
2
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes2, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError2, getGlobalConfig } from "@openid4vc/oauth2";
|
|
3
|
+
import { URL as URL2 } from "@openid4vc/utils";
|
|
13
4
|
|
|
14
|
-
// src/
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from "@openid4vc/oauth2";
|
|
23
|
-
import
|
|
5
|
+
// src/authorization-request/z-authorization-request-dc-api.ts
|
|
6
|
+
import { z as z5 } from "zod";
|
|
7
|
+
|
|
8
|
+
// src/authorization-request/z-authorization-request.ts
|
|
9
|
+
import { URL, zHttpsUrl as zHttpsUrl2 } from "@openid4vc/utils";
|
|
10
|
+
import { z as z4 } from "zod";
|
|
11
|
+
|
|
12
|
+
// src/models/z-client-metadata.ts
|
|
13
|
+
import { zJwkSet } from "@openid4vc/oauth2";
|
|
14
|
+
import { zHttpsUrl } from "@openid4vc/utils";
|
|
15
|
+
import { z as z3 } from "zod";
|
|
24
16
|
|
|
25
17
|
// src/jarm/metadata/z-jarm-client-metadata.ts
|
|
26
18
|
import { Oauth2Error, zAlgValueNotNone } from "@openid4vc/oauth2";
|
|
27
19
|
import { parseWithErrorHandling } from "@openid4vc/utils";
|
|
28
|
-
import { z
|
|
29
|
-
var zJarmSignOnlyClientMetadata =
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
var zJarmSignOnlyClientMetadata = z.object({
|
|
30
22
|
authorization_signed_response_alg: zAlgValueNotNone,
|
|
31
|
-
authorization_encrypted_response_alg:
|
|
32
|
-
authorization_encrypted_response_enc:
|
|
23
|
+
authorization_encrypted_response_alg: z.optional(z.never()),
|
|
24
|
+
authorization_encrypted_response_enc: z.optional(z.never())
|
|
33
25
|
});
|
|
34
|
-
var zJarmEncryptOnlyClientMetadata =
|
|
35
|
-
authorization_signed_response_alg:
|
|
36
|
-
authorization_encrypted_response_alg:
|
|
37
|
-
authorization_encrypted_response_enc:
|
|
26
|
+
var zJarmEncryptOnlyClientMetadata = z.object({
|
|
27
|
+
authorization_signed_response_alg: z.optional(z.never()),
|
|
28
|
+
authorization_encrypted_response_alg: z.string(),
|
|
29
|
+
authorization_encrypted_response_enc: z.optional(z.string())
|
|
38
30
|
});
|
|
39
|
-
var zJarmSignEncryptClientMetadata =
|
|
31
|
+
var zJarmSignEncryptClientMetadata = z.object({
|
|
40
32
|
authorization_signed_response_alg: zJarmSignOnlyClientMetadata.shape.authorization_signed_response_alg,
|
|
41
33
|
authorization_encrypted_response_alg: zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_alg,
|
|
42
34
|
authorization_encrypted_response_enc: zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_enc
|
|
43
35
|
});
|
|
44
|
-
var zJarmClientMetadata =
|
|
45
|
-
authorization_signed_response_alg:
|
|
46
|
-
authorization_encrypted_response_alg:
|
|
36
|
+
var zJarmClientMetadata = z.object({
|
|
37
|
+
authorization_signed_response_alg: z.optional(zJarmSignOnlyClientMetadata.shape.authorization_signed_response_alg),
|
|
38
|
+
authorization_encrypted_response_alg: z.optional(
|
|
47
39
|
zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_alg
|
|
48
40
|
),
|
|
49
|
-
authorization_encrypted_response_enc:
|
|
41
|
+
authorization_encrypted_response_enc: z.optional(
|
|
50
42
|
zJarmEncryptOnlyClientMetadata.shape.authorization_encrypted_response_enc
|
|
51
43
|
)
|
|
52
44
|
});
|
|
53
45
|
var zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata) => {
|
|
54
46
|
const parsedClientMeta = parseWithErrorHandling(
|
|
55
|
-
|
|
47
|
+
z.union([zJarmEncryptOnlyClientMetadata, zJarmSignOnlyClientMetadata, zJarmSignEncryptClientMetadata]),
|
|
56
48
|
client_metadata,
|
|
57
49
|
"Invalid jarm client metadata."
|
|
58
50
|
);
|
|
@@ -89,766 +81,799 @@ var zJarmClientMetadataParsed = zJarmClientMetadata.transform((client_metadata)
|
|
|
89
81
|
throw new Oauth2Error("Invalid jarm client metadata. Failed to parse.");
|
|
90
82
|
});
|
|
91
83
|
|
|
92
|
-
// src/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
clientMetadata.jwks.keys?.[0];
|
|
101
|
-
return { encJwk, sigJwk };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// src/jarm/jarm-authorization-response/jarm-validate-authorization-response.ts
|
|
105
|
-
import { Oauth2Error as Oauth2Error2 } from "@openid4vc/oauth2";
|
|
106
|
-
import { dateToSeconds } from "@openid4vc/utils";
|
|
84
|
+
// src/models/z-vp-formats-supported.ts
|
|
85
|
+
import { z as z2 } from "zod";
|
|
86
|
+
var zVpFormatsSupported = z2.record(
|
|
87
|
+
z2.string(),
|
|
88
|
+
z2.object({
|
|
89
|
+
alg_values_supported: z2.optional(z2.array(z2.string()))
|
|
90
|
+
}).passthrough()
|
|
91
|
+
);
|
|
107
92
|
|
|
108
|
-
// src/
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
* aud: The client_id of the client the response is intended for
|
|
116
|
-
* exp: The expiration time of the JWT. A maximum JWT lifetime of 10 minutes is RECOMMENDED.
|
|
117
|
-
*/
|
|
118
|
-
...zJwtPayload.shape,
|
|
119
|
-
...zJwtPayload.pick({ iss: true, aud: true, exp: true }).required().shape,
|
|
120
|
-
state: z3.optional(z3.string())
|
|
121
|
-
}).passthrough();
|
|
122
|
-
var zJarmAuthorizationResponseEncryptedOnly = z3.object({
|
|
123
|
-
...zJwtPayload.shape,
|
|
124
|
-
state: z3.optional(z3.string())
|
|
93
|
+
// src/models/z-client-metadata.ts
|
|
94
|
+
var zClientMetadata = z3.object({
|
|
95
|
+
jwks: z3.optional(zJwkSet),
|
|
96
|
+
vp_formats: z3.optional(zVpFormatsSupported),
|
|
97
|
+
...zJarmClientMetadata.shape,
|
|
98
|
+
logo_uri: zHttpsUrl.optional(),
|
|
99
|
+
client_name: z3.string().optional()
|
|
125
100
|
}).passthrough();
|
|
126
101
|
|
|
127
|
-
// src/
|
|
128
|
-
var
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
`Invalid 'aud' claim in JARM authorization response. Expected '${expectedClientId}' received '${JSON.stringify(authorizationResponse.aud)}'.`
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
if (authorizationResponse.exp !== void 0 && authorizationResponse.exp < dateToSeconds()) {
|
|
139
|
-
throw new Oauth2Error2("Jarm auth response is expired.");
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
// src/jarm/jarm-authorization-response/verify-jarm-authorization-response.ts
|
|
144
|
-
var decryptJarmAuthorizationResponseJwt = async (options) => {
|
|
145
|
-
const { jarmAuthorizationResponseJwt, callbacks, authorizationRequestPayload } = options;
|
|
146
|
-
const encryptionJwk = authorizationRequestPayload.client_metadata?.jwks ? extractJwksFromClientMetadata({
|
|
147
|
-
...authorizationRequestPayload.client_metadata,
|
|
148
|
-
jwks: authorizationRequestPayload.client_metadata.jwks
|
|
149
|
-
}).encJwk : void 0;
|
|
150
|
-
const result = await callbacks.decryptJwe(jarmAuthorizationResponseJwt, { jwk: encryptionJwk });
|
|
151
|
-
if (!result.decrypted) {
|
|
152
|
-
throw new Oauth2Error3("Failed to decrypt jarm auth response.");
|
|
153
|
-
}
|
|
154
|
-
return result.payload;
|
|
155
|
-
};
|
|
156
|
-
async function verifyJarmAuthorizationResponse(options) {
|
|
157
|
-
const { jarmAuthorizationResponseJwt, callbacks, expectedClientId, authorizationRequestPayload } = options;
|
|
158
|
-
const requestDataIsEncrypted = zCompactJwe.safeParse(jarmAuthorizationResponseJwt).success;
|
|
159
|
-
const decryptedRequestData = requestDataIsEncrypted ? await decryptJarmAuthorizationResponseJwt({
|
|
160
|
-
jarmAuthorizationResponseJwt,
|
|
161
|
-
callbacks,
|
|
162
|
-
authorizationRequestPayload
|
|
163
|
-
}) : jarmAuthorizationResponseJwt;
|
|
164
|
-
const responseIsSigned = zCompactJwt.safeParse(decryptedRequestData).success;
|
|
165
|
-
if (!requestDataIsEncrypted && !responseIsSigned) {
|
|
166
|
-
throw new Oauth2Error3("Jarm Auth Response must be either encrypted, signed, or signed and encrypted.");
|
|
167
|
-
}
|
|
168
|
-
let jarmAuthorizationResponse;
|
|
169
|
-
if (responseIsSigned) {
|
|
170
|
-
const { header: jwsProtectedHeader, payload: jwsPayload } = decodeJwt({
|
|
171
|
-
jwt: decryptedRequestData,
|
|
172
|
-
headerSchema: z4.object({ ...zJwtHeader2.shape, kid: z4.string() })
|
|
173
|
-
});
|
|
174
|
-
const response = zJarmAuthorizationResponse.parse(jwsPayload);
|
|
175
|
-
const jwtSigner = jwtSignerFromJwt({ header: jwsProtectedHeader, payload: jwsPayload });
|
|
176
|
-
const verificationResult = await options.callbacks.verifyJwt(jwtSigner, {
|
|
177
|
-
compact: decryptedRequestData,
|
|
178
|
-
header: jwsProtectedHeader,
|
|
179
|
-
payload: jwsPayload
|
|
102
|
+
// src/authorization-request/z-authorization-request.ts
|
|
103
|
+
var zStringToJson = z4.string().transform((string, ctx) => {
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(string);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
ctx.addIssue({
|
|
108
|
+
code: "custom",
|
|
109
|
+
message: "Expected a JSON string, but could not parse the string to JSON"
|
|
180
110
|
});
|
|
181
|
-
|
|
182
|
-
throw new Oauth2Error3("Jarm Auth Response is not valid.");
|
|
183
|
-
}
|
|
184
|
-
jarmAuthorizationResponse = response;
|
|
185
|
-
} else {
|
|
186
|
-
const jsonRequestData = JSON.parse(decryptedRequestData);
|
|
187
|
-
jarmAuthorizationResponse = zJarmAuthorizationResponseEncryptedOnly.parse(jsonRequestData);
|
|
111
|
+
return z4.NEVER;
|
|
188
112
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
113
|
+
});
|
|
114
|
+
var zOpenid4vpAuthorizationRequest = z4.object({
|
|
115
|
+
response_type: z4.literal("vp_token"),
|
|
116
|
+
client_id: z4.string(),
|
|
117
|
+
redirect_uri: zHttpsUrl2.optional(),
|
|
118
|
+
response_uri: zHttpsUrl2.optional(),
|
|
119
|
+
request_uri: zHttpsUrl2.optional(),
|
|
120
|
+
request_uri_method: z4.optional(z4.string()),
|
|
121
|
+
response_mode: z4.enum(["direct_post", "direct_post.jwt"]).optional(),
|
|
122
|
+
nonce: z4.string(),
|
|
123
|
+
wallet_nonce: z4.string().optional(),
|
|
124
|
+
scope: z4.string().optional(),
|
|
125
|
+
presentation_definition: z4.record(z4.any()).or(zStringToJson).optional(),
|
|
126
|
+
presentation_definition_uri: zHttpsUrl2.optional(),
|
|
127
|
+
dcql_query: z4.record(z4.any()).or(zStringToJson).optional(),
|
|
128
|
+
client_metadata: zClientMetadata.optional(),
|
|
129
|
+
client_metadata_uri: zHttpsUrl2.optional(),
|
|
130
|
+
state: z4.string().optional(),
|
|
131
|
+
transaction_data: z4.array(z4.string().base64url()).optional(),
|
|
132
|
+
trust_chain: z4.unknown().optional(),
|
|
133
|
+
client_id_scheme: z4.enum([
|
|
134
|
+
"pre-registered",
|
|
135
|
+
"redirect_uri",
|
|
136
|
+
"entity_id",
|
|
137
|
+
"did",
|
|
138
|
+
"verifier_attestation",
|
|
139
|
+
"x509_san_dns",
|
|
140
|
+
"x509_san_uri"
|
|
141
|
+
]).optional()
|
|
142
|
+
}).passthrough();
|
|
143
|
+
var zOpenid4vpAuthorizationRequestFromUriParams = z4.string().url().transform((url) => Object.fromEntries(new URL(url).searchParams)).pipe(
|
|
144
|
+
z4.object({
|
|
145
|
+
presentation_definition: zStringToJson.optional(),
|
|
146
|
+
client_metadata: zStringToJson.optional(),
|
|
147
|
+
dcql_query: zStringToJson.optional(),
|
|
148
|
+
transaction_data: zStringToJson.optional()
|
|
149
|
+
}).passthrough()
|
|
150
|
+
);
|
|
201
151
|
|
|
202
|
-
// src/
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
152
|
+
// src/authorization-request/z-authorization-request-dc-api.ts
|
|
153
|
+
var zOpenid4vpAuthorizationRequestDcApi = zOpenid4vpAuthorizationRequest.pick({
|
|
154
|
+
client_id: true,
|
|
155
|
+
response_type: true,
|
|
156
|
+
response_mode: true,
|
|
157
|
+
nonce: true,
|
|
158
|
+
presentation_definition: true,
|
|
159
|
+
client_metadata: true,
|
|
160
|
+
transaction_data: true,
|
|
161
|
+
dcql_query: true,
|
|
162
|
+
trust_chain: true
|
|
163
|
+
}).extend({
|
|
164
|
+
client_id: z5.optional(z5.string()),
|
|
165
|
+
expected_origins: z5.array(z5.string()).optional(),
|
|
166
|
+
response_mode: z5.enum(["dc_api", "dc_api.jwt", "w3c_dc_api.jwt", "w3c_dc_api"]),
|
|
167
|
+
client_id_scheme: z5.enum([
|
|
168
|
+
"pre-registered",
|
|
169
|
+
"redirect_uri",
|
|
170
|
+
"entity_id",
|
|
171
|
+
"did",
|
|
172
|
+
"verifier_attestation",
|
|
173
|
+
"x509_san_dns",
|
|
174
|
+
"x509_san_uri"
|
|
175
|
+
]).optional()
|
|
176
|
+
}).strip();
|
|
177
|
+
function isOpenid4vpAuthorizationRequestDcApi(request) {
|
|
178
|
+
return request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt" || request.response_mode === "w3c_dc_api.jwt" || request.response_mode === "w3c_dc_api";
|
|
223
179
|
}
|
|
224
180
|
|
|
225
|
-
// src/
|
|
181
|
+
// src/version.ts
|
|
226
182
|
import { Oauth2ErrorCodes, Oauth2ServerErrorResponseError } from "@openid4vc/oauth2";
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
183
|
+
|
|
184
|
+
// src/client-identifier-scheme/z-client-id-scheme.ts
|
|
185
|
+
import { z as z6 } from "zod";
|
|
186
|
+
var zClientIdScheme = z6.enum([
|
|
187
|
+
"pre-registered",
|
|
188
|
+
"redirect_uri",
|
|
189
|
+
"https",
|
|
190
|
+
"verifier_attestation",
|
|
191
|
+
"did",
|
|
192
|
+
"x509_san_dns",
|
|
193
|
+
"x509_san_uri",
|
|
194
|
+
"web-origin"
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
// src/version.ts
|
|
198
|
+
function parseAuthorizationRequestVersion(request) {
|
|
199
|
+
const requirements = [];
|
|
200
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.response_mode === "w3c_dc_api" || request.response_mode === "w3c_dc_api.jwt")) {
|
|
201
|
+
requirements.push(["<", 23]);
|
|
202
|
+
requirements.push([">=", 21]);
|
|
235
203
|
}
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
239
|
-
error_description: `The 'response_mode' parameter MUST be 'direct_post' or 'direct_post.jwt' when 'response_uri' is provided. Current: ${params.response_mode}`
|
|
240
|
-
});
|
|
204
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt") {
|
|
205
|
+
requirements.push([">=", 23]);
|
|
241
206
|
}
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
).length > 1) {
|
|
245
|
-
throw new Oauth2ServerErrorResponseError({
|
|
246
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
247
|
-
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."
|
|
248
|
-
});
|
|
207
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request) && (request.transaction_data || request.dcql_query)) {
|
|
208
|
+
requirements.push([">=", 23]);
|
|
249
209
|
}
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
253
|
-
error_description: 'The "request_uri_method" parameter MUST NOT be present in the authorization request if the "request_uri" parameter is not present.'
|
|
254
|
-
});
|
|
210
|
+
if (request.dcql_query) {
|
|
211
|
+
requirements.push([">=", 22]);
|
|
255
212
|
}
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
error: Oauth2ErrorCodes.InvalidRequestUriMethod,
|
|
259
|
-
error_description: `The 'request_uri_method' parameter MUST be 'GET' or 'POST'. Current: ${params.request_uri_method}`
|
|
260
|
-
});
|
|
213
|
+
if (request.transaction_data) {
|
|
214
|
+
requirements.push([">=", 22]);
|
|
261
215
|
}
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
265
|
-
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://.'
|
|
266
|
-
});
|
|
216
|
+
if (request.client_id_scheme) {
|
|
217
|
+
requirements.push(["<", 22]);
|
|
267
218
|
}
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
219
|
+
if (request.client_id) {
|
|
220
|
+
const colonIndex = request.client_id.indexOf(":");
|
|
221
|
+
const schemePart = request.client_id.substring(0, colonIndex);
|
|
222
|
+
const parsedScheme = zClientIdScheme.safeParse(schemePart);
|
|
223
|
+
if (parsedScheme.success && parsedScheme.data !== "did" && parsedScheme.data !== "https") {
|
|
224
|
+
requirements.push([">=", 22]);
|
|
225
|
+
}
|
|
273
226
|
}
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
error: Oauth2ErrorCodes.InvalidRequest,
|
|
277
|
-
error_description: 'The "wallet_nonce" parameter MUST match the "expectedNonce" parameter when the "expectedNonce" parameter is provided.'
|
|
278
|
-
});
|
|
227
|
+
if (!request.client_id) {
|
|
228
|
+
requirements.push([">=", 21]);
|
|
279
229
|
}
|
|
280
|
-
if (
|
|
230
|
+
if ("client_metadata_uri" in request) {
|
|
231
|
+
requirements.push(["<", 21]);
|
|
232
|
+
}
|
|
233
|
+
if (isOpenid4vpAuthorizationRequestDcApi(request)) {
|
|
234
|
+
requirements.push([">=", 21]);
|
|
235
|
+
}
|
|
236
|
+
if ("request_uri_method" in request || "wallet_nonce" in request) {
|
|
237
|
+
requirements.push([">=", 21]);
|
|
238
|
+
}
|
|
239
|
+
if (request.client_id_scheme === "verifier_attestation") {
|
|
240
|
+
requirements.push([">=", 20]);
|
|
241
|
+
}
|
|
242
|
+
if (request.client_id_scheme === "x509_san_dns" || request.client_id_scheme === "x509_san_uri") {
|
|
243
|
+
requirements.push([">=", 19]);
|
|
244
|
+
}
|
|
245
|
+
const lessThanVersions = requirements.filter(([operator]) => operator === "<").map(([_, version]) => version);
|
|
246
|
+
const greaterThanVersions = requirements.filter(([operator]) => operator === ">=").map(([_, version]) => version);
|
|
247
|
+
const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 24;
|
|
248
|
+
const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
|
|
249
|
+
if (lowestRequiredVersion > highestPossibleVersion) {
|
|
281
250
|
throw new Oauth2ServerErrorResponseError({
|
|
282
251
|
error: Oauth2ErrorCodes.InvalidRequest,
|
|
283
|
-
error_description:
|
|
252
|
+
error_description: "Could not infer openid4vp version from the openid4vp request payload."
|
|
284
253
|
});
|
|
285
254
|
}
|
|
286
|
-
|
|
255
|
+
return highestPossibleVersion;
|
|
256
|
+
}
|
|
287
257
|
|
|
288
|
-
// src/
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
throw new Oauth2ServerErrorResponseError2({
|
|
294
|
-
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
295
|
-
error_description: `The 'expected_origins' parameter MUST be present when using the dc_api response mode in combinaction with jar.`
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
if ([params.presentation_definition, params.dcql_query].filter(Boolean).length !== 1) {
|
|
299
|
-
throw new Oauth2ServerErrorResponseError2({
|
|
300
|
-
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
301
|
-
error_description: "Exactly one of the following parameters MUST be present in the Authorization Request: dcql_query or presentation_definition"
|
|
302
|
-
});
|
|
258
|
+
// src/client-identifier-scheme/parse-client-identifier-scheme.ts
|
|
259
|
+
function getOpenid4vpClientId(options) {
|
|
260
|
+
const version = parseAuthorizationRequestVersion(options.authorizationRequestPayload);
|
|
261
|
+
if (version < 22) {
|
|
262
|
+
return getLegacyClientId(options);
|
|
303
263
|
}
|
|
304
|
-
if (
|
|
305
|
-
if (!origin) {
|
|
264
|
+
if (isOpenid4vpAuthorizationRequestDcApi(options.authorizationRequestPayload)) {
|
|
265
|
+
if (!options.origin) {
|
|
306
266
|
throw new Oauth2ServerErrorResponseError2({
|
|
307
267
|
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
308
|
-
error_description:
|
|
268
|
+
error_description: "Failed to parse client identifier. 'origin' is required for requests with response_mode 'dc_api' and 'dc_api.jwt'"
|
|
309
269
|
});
|
|
310
270
|
}
|
|
311
|
-
if (
|
|
271
|
+
if (!options.jar || !options.authorizationRequestPayload.client_id) return `web-origin:${options.origin}`;
|
|
272
|
+
return options.authorizationRequestPayload.client_id;
|
|
273
|
+
}
|
|
274
|
+
return options.authorizationRequestPayload.client_id;
|
|
275
|
+
}
|
|
276
|
+
function getLegacyClientId(options) {
|
|
277
|
+
const legacyClientIdScheme = options.authorizationRequestPayload.client_id_scheme ?? "pre-registered";
|
|
278
|
+
const clientIdScheme = legacyClientIdScheme === "entity_id" ? "https" : legacyClientIdScheme;
|
|
279
|
+
if (isOpenid4vpAuthorizationRequestDcApi(options.authorizationRequestPayload)) {
|
|
280
|
+
if (!options.origin) {
|
|
312
281
|
throw new Oauth2ServerErrorResponseError2({
|
|
313
282
|
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
314
|
-
error_description:
|
|
283
|
+
error_description: "Failed to parse client identifier. 'origin' is required for requests with response_mode 'dc_api' and 'dc_api.jwt'"
|
|
315
284
|
});
|
|
316
285
|
}
|
|
286
|
+
if (!options.jar || !options.authorizationRequestPayload.client_id) return `web-origin:${options.origin}`;
|
|
287
|
+
return `${clientIdScheme}:${options.authorizationRequestPayload.client_id}`;
|
|
317
288
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
import { zJwkSet } from "@openid4vc/oauth2";
|
|
326
|
-
import { zHttpsUrl as zHttpsUrl2 } from "@openid4vc/utils";
|
|
327
|
-
import { z as z6 } from "zod";
|
|
328
|
-
|
|
329
|
-
// src/models/z-vp-formats-supported.ts
|
|
330
|
-
import { z as z5 } from "zod";
|
|
331
|
-
var zVpFormatsSupported = z5.record(
|
|
332
|
-
z5.string(),
|
|
333
|
-
z5.object({
|
|
334
|
-
alg_values_supported: z5.optional(z5.array(z5.string()))
|
|
335
|
-
}).passthrough()
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
// src/models/z-client-metadata.ts
|
|
339
|
-
var zClientMetadata = z6.object({
|
|
340
|
-
jwks: z6.optional(zJwkSet),
|
|
341
|
-
vp_formats: z6.optional(zVpFormatsSupported),
|
|
342
|
-
...zJarmClientMetadata.shape,
|
|
343
|
-
logo_uri: zHttpsUrl2.optional(),
|
|
344
|
-
client_name: z6.string().optional()
|
|
345
|
-
}).passthrough();
|
|
346
|
-
|
|
347
|
-
// src/authorization-request/z-authorization-request.ts
|
|
348
|
-
var zOpenid4vpAuthorizationRequest = z7.object({
|
|
349
|
-
response_type: z7.literal("vp_token"),
|
|
350
|
-
client_id: z7.string(),
|
|
351
|
-
redirect_uri: zHttpsUrl3.optional(),
|
|
352
|
-
response_uri: zHttpsUrl3.optional(),
|
|
353
|
-
request_uri: zHttpsUrl3.optional(),
|
|
354
|
-
request_uri_method: z7.optional(z7.string()),
|
|
355
|
-
response_mode: z7.enum(["direct_post", "direct_post.jwt"]).optional(),
|
|
356
|
-
nonce: z7.string(),
|
|
357
|
-
wallet_nonce: z7.string().optional(),
|
|
358
|
-
scope: z7.string().optional(),
|
|
359
|
-
presentation_definition: z7.record(z7.any()).optional(),
|
|
360
|
-
presentation_definition_uri: zHttpsUrl3.optional(),
|
|
361
|
-
dcql_query: z7.record(z7.any()).optional(),
|
|
362
|
-
client_metadata: zClientMetadata.optional(),
|
|
363
|
-
client_metadata_uri: zHttpsUrl3.optional(),
|
|
364
|
-
state: z7.string().optional(),
|
|
365
|
-
transaction_data: z7.array(z7.string()).optional(),
|
|
366
|
-
trust_chain: z7.unknown().optional(),
|
|
367
|
-
client_id_scheme: z7.enum([
|
|
368
|
-
"pre-registered",
|
|
369
|
-
"redirect_uri",
|
|
370
|
-
"entity_id",
|
|
371
|
-
"did",
|
|
372
|
-
"verifier_attestation",
|
|
373
|
-
"x509_san_dns",
|
|
374
|
-
"x509_san_uri"
|
|
375
|
-
]).optional()
|
|
376
|
-
}).passthrough();
|
|
377
|
-
|
|
378
|
-
// src/authorization-request/z-authorization-request-dc-api.ts
|
|
379
|
-
import { z as z8 } from "zod";
|
|
380
|
-
var zOpenid4vpAuthorizationRequestDcApi = zOpenid4vpAuthorizationRequest.pick({
|
|
381
|
-
client_id: true,
|
|
382
|
-
response_type: true,
|
|
383
|
-
response_mode: true,
|
|
384
|
-
nonce: true,
|
|
385
|
-
presentation_definition: true,
|
|
386
|
-
client_metadata: true,
|
|
387
|
-
transaction_data: true,
|
|
388
|
-
dcql_query: true,
|
|
389
|
-
trust_chain: true
|
|
390
|
-
}).extend({
|
|
391
|
-
client_id: z8.optional(z8.string()),
|
|
392
|
-
expected_origins: z8.array(z8.string()).optional(),
|
|
393
|
-
response_mode: z8.enum(["dc_api", "dc_api.jwt", "w3c_dc_api.jwt", "w3c_dc_api"]),
|
|
394
|
-
client_id_scheme: z8.enum([
|
|
395
|
-
"pre-registered",
|
|
396
|
-
"redirect_uri",
|
|
397
|
-
"entity_id",
|
|
398
|
-
"did",
|
|
399
|
-
"verifier_attestation",
|
|
400
|
-
"x509_san_dns",
|
|
401
|
-
"x509_san_uri"
|
|
402
|
-
]).optional()
|
|
403
|
-
}).strip();
|
|
404
|
-
function isOpenid4vpAuthorizationRequestDcApi(request) {
|
|
405
|
-
return request.response_mode === "dc_api" || request.response_mode === "dc_api.jwt" || request.response_mode === "w3c_dc_api.jwt" || request.response_mode === "w3c_dc_api";
|
|
289
|
+
if (clientIdScheme === "https" || clientIdScheme === "did") {
|
|
290
|
+
return options.authorizationRequestPayload.client_id;
|
|
291
|
+
}
|
|
292
|
+
if (clientIdScheme === "pre-registered") {
|
|
293
|
+
return options.authorizationRequestPayload.client_id;
|
|
294
|
+
}
|
|
295
|
+
return `${clientIdScheme}:${options.authorizationRequestPayload.client_id}`;
|
|
406
296
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
});
|
|
429
|
-
} else {
|
|
430
|
-
authorizationRequestPayload = parseWithErrorHandling2(
|
|
431
|
-
zOpenid4vpAuthorizationRequest,
|
|
432
|
-
options.authorizationRequestPayload,
|
|
433
|
-
"Invalid authorization request. Could not parse openid4vp authorization request."
|
|
434
|
-
);
|
|
435
|
-
validateOpenid4vpAuthorizationRequestPayload({
|
|
436
|
-
params: authorizationRequestPayload,
|
|
437
|
-
walletVerificationOptions: wallet
|
|
297
|
+
function parseClientIdentifier(options, parserConfig) {
|
|
298
|
+
const { authorizationRequestPayload, jar } = options;
|
|
299
|
+
const parserConfigWithDefaults = {
|
|
300
|
+
supportedSchemes: parserConfig?.supportedSchemes || Object.values(zClientIdScheme.options)
|
|
301
|
+
};
|
|
302
|
+
const clientId = getOpenid4vpClientId(options);
|
|
303
|
+
const colonIndex = clientId.indexOf(":");
|
|
304
|
+
if (colonIndex === -1) {
|
|
305
|
+
return {
|
|
306
|
+
scheme: "pre-registered",
|
|
307
|
+
identifier: clientId,
|
|
308
|
+
originalValue: clientId,
|
|
309
|
+
clientMetadata: authorizationRequestPayload.client_metadata
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const schemePart = clientId.substring(0, colonIndex);
|
|
313
|
+
const identifierPart = clientId.substring(colonIndex + 1);
|
|
314
|
+
if (!parserConfigWithDefaults.supportedSchemes.includes(schemePart)) {
|
|
315
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
316
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
317
|
+
error_description: `Unsupported client identifier scheme. ${schemePart} is not supported.`
|
|
438
318
|
});
|
|
439
319
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
320
|
+
const scheme = schemePart;
|
|
321
|
+
if (scheme === "https") {
|
|
322
|
+
if (!clientId.startsWith("https://") && !(getGlobalConfig().allowInsecureUrls && clientId.startsWith("http://"))) {
|
|
323
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
324
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
325
|
+
error_description: "Invalid client identifier. Client identifier must start with https:// or http:// if allowInsecureUrls is true."
|
|
326
|
+
});
|
|
443
327
|
}
|
|
444
|
-
const jarResult = await createJarAuthorizationRequest({
|
|
445
|
-
...jar,
|
|
446
|
-
authorizationRequestPayload,
|
|
447
|
-
additionalJwtPayload,
|
|
448
|
-
callbacks
|
|
449
|
-
});
|
|
450
|
-
const url2 = new URL(scheme);
|
|
451
|
-
url2.search = `?${new URLSearchParams([
|
|
452
|
-
...url2.searchParams.entries(),
|
|
453
|
-
...objectToQueryParams(jarResult.jarAuthorizationRequest).entries()
|
|
454
|
-
]).toString()}`;
|
|
455
328
|
return {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
329
|
+
scheme,
|
|
330
|
+
identifier: clientId,
|
|
331
|
+
originalValue: clientId,
|
|
332
|
+
trustChain: authorizationRequestPayload.trust_chain
|
|
460
333
|
};
|
|
461
334
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
// src/jar/z-jar-authorization-request.ts
|
|
482
|
-
import { Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError3 } from "@openid4vc/oauth2";
|
|
483
|
-
import { zHttpsUrl as zHttpsUrl4 } from "@openid4vc/utils";
|
|
484
|
-
import { z as z9 } from "zod";
|
|
485
|
-
var zJarAuthorizationRequest = z9.object({
|
|
486
|
-
request: z9.optional(z9.string()),
|
|
487
|
-
request_uri: z9.optional(zHttpsUrl4),
|
|
488
|
-
request_uri_method: z9.optional(z9.string()),
|
|
489
|
-
client_id: z9.optional(z9.string())
|
|
490
|
-
}).passthrough();
|
|
491
|
-
function validateJarRequestParams(options) {
|
|
492
|
-
const { jarRequestParams } = options;
|
|
493
|
-
if (jarRequestParams.request && jarRequestParams.request_uri) {
|
|
494
|
-
throw new Oauth2ServerErrorResponseError3({
|
|
495
|
-
error: "invalid_request_object",
|
|
496
|
-
error_description: "request and request_uri cannot both be present in a JAR request"
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
if (!jarRequestParams.request && !jarRequestParams.request_uri) {
|
|
500
|
-
throw new Oauth2ServerErrorResponseError3({
|
|
501
|
-
error: "invalid_request_object",
|
|
502
|
-
error_description: "request or request_uri must be present"
|
|
503
|
-
});
|
|
335
|
+
if (scheme === "redirect_uri") {
|
|
336
|
+
if (jar) {
|
|
337
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
338
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
339
|
+
error_description: 'Using client identifier scheme "redirect_uri" the request MUST NOT be signed.'
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
343
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
344
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
345
|
+
error_description: `The client identifier scheme 'redirect_uri' is not supported when using the dc_api response mode.`
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
scheme,
|
|
350
|
+
identifier: identifierPart,
|
|
351
|
+
originalValue: clientId,
|
|
352
|
+
redirectUri: authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri
|
|
353
|
+
};
|
|
504
354
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
// src/authorization-request/parse-authorization-request-params.ts
|
|
512
|
-
function parseOpenid4vpAuthorizationRequestPayload(options) {
|
|
513
|
-
const { authorizationRequest } = options;
|
|
514
|
-
let provided = "params";
|
|
515
|
-
let params;
|
|
516
|
-
if (typeof authorizationRequest === "string") {
|
|
517
|
-
if (authorizationRequest.includes("://")) {
|
|
518
|
-
const url = new URL2(authorizationRequest);
|
|
519
|
-
params = Object.fromEntries(url.searchParams);
|
|
520
|
-
provided = "uri";
|
|
521
|
-
} else {
|
|
522
|
-
const decoded = decodeJwt2({ jwt: authorizationRequest });
|
|
523
|
-
params = decoded.payload;
|
|
524
|
-
provided = "jwt";
|
|
355
|
+
if (scheme === "did") {
|
|
356
|
+
if (!jar) {
|
|
357
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
358
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
359
|
+
error_description: 'Using client identifier scheme "did" requires a signed JAR request.'
|
|
360
|
+
});
|
|
525
361
|
}
|
|
526
|
-
|
|
527
|
-
|
|
362
|
+
if (!clientId.startsWith("did:")) {
|
|
363
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
364
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
365
|
+
error_description: "Invalid client identifier. Client identifier must start with 'did:'"
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
if (!jar.signer.publicJwk.kid) {
|
|
369
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
370
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
371
|
+
error_description: `Missing required 'kid' for client identifier scheme: did`
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
if (!jar.signer.publicJwk.kid?.startsWith(clientId)) {
|
|
375
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
376
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
377
|
+
error_description: 'With client identifier scheme "did" the JAR request must be signed by the same DID as the client identifier.'
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
scheme,
|
|
382
|
+
identifier: clientId,
|
|
383
|
+
originalValue: clientId,
|
|
384
|
+
didUrl: jar.signer.publicJwk.kid
|
|
385
|
+
};
|
|
528
386
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
387
|
+
if (scheme === "x509_san_dns" || scheme === "x509_san_uri") {
|
|
388
|
+
if (!jar) {
|
|
389
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
390
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
391
|
+
error_description: 'Using client identifier scheme "x509_san_dns" or "x509_san_uri" requires a signed JAR request.'
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
if (jar.signer.method !== "x5c") {
|
|
395
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
396
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
397
|
+
error_description: "Something went wrong. The JWT signer method is not x5c but the client identifier scheme is x509_san_dns."
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
if (scheme === "x509_san_dns") {
|
|
401
|
+
if (!options.callbacks.getX509CertificateMetadata) {
|
|
402
|
+
throw new Oauth2ServerErrorResponseError2(
|
|
403
|
+
{
|
|
404
|
+
error: Oauth2ErrorCodes2.ServerError
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
internalMessage: "Missing required 'getX509CertificateMetadata' callback for verification of 'x509_san_dns' client id scheme"
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
const { sanDnsNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
412
|
+
if (!sanDnsNames.includes(identifierPart)) {
|
|
413
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
414
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
415
|
+
error_description: `Invalid client identifier. One of the leaf certificates san dns names [${sanDnsNames.join(", ")}] must match the client identifier '${identifierPart}'. `
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
419
|
+
const uri = authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri;
|
|
420
|
+
if (!uri || new URL2(uri).hostname !== identifierPart) {
|
|
421
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
422
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
423
|
+
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."
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} else if (scheme === "x509_san_uri") {
|
|
428
|
+
if (!options.callbacks.getX509CertificateMetadata) {
|
|
429
|
+
throw new Oauth2ServerErrorResponseError2(
|
|
430
|
+
{
|
|
431
|
+
error: Oauth2ErrorCodes2.ServerError
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
internalMessage: "Missing required 'getX509CertificateMetadata' callback for verification of 'x509_san_uri' client id scheme"
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
const { sanUriNames } = options.callbacks.getX509CertificateMetadata(jar.signer.x5c[0]);
|
|
439
|
+
if (!sanUriNames.includes(identifierPart)) {
|
|
440
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
441
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
442
|
+
error_description: `Invalid client identifier. One of the leaf certificates san uri names [${sanUriNames.join(", ")}] must match the client identifier '${identifierPart}'.`
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
if (!isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
446
|
+
const uri = authorizationRequestPayload.redirect_uri || authorizationRequestPayload.response_uri;
|
|
447
|
+
if (!uri || uri !== identifierPart) {
|
|
448
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
449
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
450
|
+
error_description: "The redirect_uri value MUST match the Client Identifier without the prefix x509_san_uri"
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
534
455
|
return {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
456
|
+
scheme,
|
|
457
|
+
identifier: identifierPart,
|
|
458
|
+
originalValue: clientId,
|
|
459
|
+
x5c: jar.signer.x5c
|
|
538
460
|
};
|
|
539
461
|
}
|
|
540
|
-
if (
|
|
462
|
+
if (scheme === "web-origin") {
|
|
541
463
|
return {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
464
|
+
scheme,
|
|
465
|
+
identifier: identifierPart,
|
|
466
|
+
originalValue: clientId,
|
|
467
|
+
clientMetadata: authorizationRequestPayload.client_metadata
|
|
545
468
|
};
|
|
546
469
|
}
|
|
470
|
+
if (scheme === "verifier_attestation") {
|
|
471
|
+
if (!jar) {
|
|
472
|
+
throw new Oauth2ServerErrorResponseError2({
|
|
473
|
+
error: Oauth2ErrorCodes2.InvalidRequest,
|
|
474
|
+
error_description: 'Using client identifier scheme "verifier_attestation" requires a signed JAR request.'
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
547
478
|
return {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
479
|
+
scheme,
|
|
480
|
+
identifier: identifierPart,
|
|
481
|
+
originalValue: clientId
|
|
551
482
|
};
|
|
552
483
|
}
|
|
553
484
|
|
|
554
|
-
// src/authorization-
|
|
555
|
-
import {
|
|
556
|
-
|
|
557
|
-
|
|
485
|
+
// src/jarm/jarm-authorization-response/verify-jarm-authorization-response.ts
|
|
486
|
+
import {
|
|
487
|
+
Oauth2Error as Oauth2Error3,
|
|
488
|
+
decodeJwt,
|
|
489
|
+
jwtSignerFromJwt,
|
|
490
|
+
zCompactJwe,
|
|
491
|
+
zCompactJwt,
|
|
492
|
+
zJwtHeader as zJwtHeader2
|
|
493
|
+
} from "@openid4vc/oauth2";
|
|
494
|
+
import z8 from "zod";
|
|
558
495
|
|
|
559
|
-
// src/
|
|
560
|
-
|
|
561
|
-
|
|
496
|
+
// src/jarm/jarm-extract-jwks.ts
|
|
497
|
+
function extractJwksFromClientMetadata(clientMetadata) {
|
|
498
|
+
const parsed = zJarmClientMetadataParsed.parse(clientMetadata);
|
|
499
|
+
const encryptionAlg = parsed.client_metadata.authorization_encrypted_response_enc;
|
|
500
|
+
const signingAlg = parsed.client_metadata.authorization_signed_response_alg;
|
|
501
|
+
const encJwk = clientMetadata.jwks.keys.find((key) => key.use === "enc" && key.alg === encryptionAlg) ?? clientMetadata.jwks.keys.find((key) => key.use === "enc") ?? // fallback, take first key. HAIP does not specify requirement on enc
|
|
502
|
+
clientMetadata.jwks.keys?.[0];
|
|
503
|
+
const sigJwk = clientMetadata.jwks.keys.find((key) => key.use === "sig" && key.alg === signingAlg) ?? clientMetadata.jwks.keys.find((key) => key.use === "sig") ?? // falback, take first key
|
|
504
|
+
clientMetadata.jwks.keys?.[0];
|
|
505
|
+
return { encJwk, sigJwk };
|
|
506
|
+
}
|
|
562
507
|
|
|
563
|
-
// src/
|
|
564
|
-
import {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
508
|
+
// src/jarm/jarm-authorization-response/jarm-validate-authorization-response.ts
|
|
509
|
+
import { Oauth2Error as Oauth2Error2 } from "@openid4vc/oauth2";
|
|
510
|
+
import { dateToSeconds } from "@openid4vc/utils";
|
|
511
|
+
|
|
512
|
+
// src/jarm/jarm-authorization-response/z-jarm-authorization-response.ts
|
|
513
|
+
import { zJwtHeader, zJwtPayload } from "@openid4vc/oauth2";
|
|
514
|
+
import { z as z7 } from "zod";
|
|
515
|
+
var zJarmHeader = z7.object({ ...zJwtHeader.shape, apu: z7.string().optional(), apv: z7.string().optional() });
|
|
516
|
+
var zJarmAuthorizationResponse = z7.object({
|
|
517
|
+
/**
|
|
518
|
+
* iss: The issuer URL of the authorization server that created the response
|
|
519
|
+
* aud: The client_id of the client the response is intended for
|
|
520
|
+
* exp: The expiration time of the JWT. A maximum JWT lifetime of 10 minutes is RECOMMENDED.
|
|
521
|
+
*/
|
|
522
|
+
...zJwtPayload.shape,
|
|
523
|
+
...zJwtPayload.pick({ iss: true, aud: true, exp: true }).required().shape,
|
|
524
|
+
state: z7.optional(z7.string())
|
|
525
|
+
}).passthrough();
|
|
526
|
+
var zJarmAuthorizationResponseEncryptedOnly = z7.object({
|
|
527
|
+
...zJwtPayload.shape,
|
|
528
|
+
state: z7.optional(z7.string())
|
|
529
|
+
}).passthrough();
|
|
530
|
+
|
|
531
|
+
// src/jarm/jarm-authorization-response/jarm-validate-authorization-response.ts
|
|
532
|
+
var jarmAuthorizationResponseValidate = (options) => {
|
|
533
|
+
const { expectedClientId, authorizationResponse } = options;
|
|
534
|
+
if (!zJarmAuthorizationResponse.safeParse(authorizationResponse).success) {
|
|
535
|
+
return;
|
|
573
536
|
}
|
|
574
|
-
if (
|
|
575
|
-
|
|
537
|
+
if (expectedClientId !== authorizationResponse.aud) {
|
|
538
|
+
throw new Oauth2Error2(
|
|
539
|
+
`Invalid 'aud' claim in JARM authorization response. Expected '${expectedClientId}' received '${JSON.stringify(authorizationResponse.aud)}'.`
|
|
540
|
+
);
|
|
576
541
|
}
|
|
577
|
-
if (
|
|
578
|
-
|
|
542
|
+
if (authorizationResponse.exp !== void 0 && authorizationResponse.exp < dateToSeconds()) {
|
|
543
|
+
throw new Oauth2Error2("Jarm auth response is expired.");
|
|
579
544
|
}
|
|
580
|
-
|
|
581
|
-
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// src/jarm/jarm-authorization-response/verify-jarm-authorization-response.ts
|
|
548
|
+
var decryptJarmAuthorizationResponseJwt = async (options) => {
|
|
549
|
+
const { jarmAuthorizationResponseJwt, callbacks, authorizationRequestPayload } = options;
|
|
550
|
+
const encryptionJwk = authorizationRequestPayload.client_metadata?.jwks ? extractJwksFromClientMetadata({
|
|
551
|
+
...authorizationRequestPayload.client_metadata,
|
|
552
|
+
jwks: authorizationRequestPayload.client_metadata.jwks
|
|
553
|
+
}).encJwk : void 0;
|
|
554
|
+
const result = await callbacks.decryptJwe(jarmAuthorizationResponseJwt, { jwk: encryptionJwk });
|
|
555
|
+
if (!result.decrypted) {
|
|
556
|
+
throw new Oauth2Error3("Failed to decrypt jarm auth response.");
|
|
582
557
|
}
|
|
583
|
-
|
|
584
|
-
|
|
558
|
+
return result.payload;
|
|
559
|
+
};
|
|
560
|
+
async function verifyJarmAuthorizationResponse(options) {
|
|
561
|
+
const { jarmAuthorizationResponseJwt, callbacks, expectedClientId, authorizationRequestPayload } = options;
|
|
562
|
+
const requestDataIsEncrypted = zCompactJwe.safeParse(jarmAuthorizationResponseJwt).success;
|
|
563
|
+
const decryptedRequestData = requestDataIsEncrypted ? await decryptJarmAuthorizationResponseJwt({
|
|
564
|
+
jarmAuthorizationResponseJwt,
|
|
565
|
+
callbacks,
|
|
566
|
+
authorizationRequestPayload
|
|
567
|
+
}) : jarmAuthorizationResponseJwt;
|
|
568
|
+
const responseIsSigned = zCompactJwt.safeParse(decryptedRequestData).success;
|
|
569
|
+
if (!requestDataIsEncrypted && !responseIsSigned) {
|
|
570
|
+
throw new Oauth2Error3("Jarm Auth Response must be either encrypted, signed, or signed and encrypted.");
|
|
585
571
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
572
|
+
let jarmAuthorizationResponse;
|
|
573
|
+
if (responseIsSigned) {
|
|
574
|
+
const { header: jwsProtectedHeader, payload: jwsPayload } = decodeJwt({
|
|
575
|
+
jwt: decryptedRequestData,
|
|
576
|
+
headerSchema: z8.object({ ...zJwtHeader2.shape, kid: z8.string() })
|
|
577
|
+
});
|
|
578
|
+
const response = zJarmAuthorizationResponse.parse(jwsPayload);
|
|
579
|
+
const jwtSigner = jwtSignerFromJwt({ header: jwsProtectedHeader, payload: jwsPayload });
|
|
580
|
+
const verificationResult = await options.callbacks.verifyJwt(jwtSigner, {
|
|
581
|
+
compact: decryptedRequestData,
|
|
582
|
+
header: jwsProtectedHeader,
|
|
583
|
+
payload: jwsPayload
|
|
584
|
+
});
|
|
585
|
+
if (!verificationResult.verified) {
|
|
586
|
+
throw new Oauth2Error3("Jarm Auth Response is not valid.");
|
|
592
587
|
}
|
|
588
|
+
jarmAuthorizationResponse = response;
|
|
589
|
+
} else {
|
|
590
|
+
const jsonRequestData = JSON.parse(decryptedRequestData);
|
|
591
|
+
jarmAuthorizationResponse = zJarmAuthorizationResponseEncryptedOnly.parse(jsonRequestData);
|
|
593
592
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
593
|
+
jarmAuthorizationResponseValidate({
|
|
594
|
+
expectedClientId,
|
|
595
|
+
authorizationResponse: jarmAuthorizationResponse
|
|
596
|
+
});
|
|
597
|
+
const type = requestDataIsEncrypted && responseIsSigned ? "SignedEncrypted" /* SignedEncrypted */ : requestDataIsEncrypted ? "Encrypted" /* Encrypted */ : "Signed" /* Signed */;
|
|
598
|
+
const issuer = jarmAuthorizationResponse.iss;
|
|
599
|
+
return { jarmAuthorizationResponse, type, issuer };
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/authorization-request/create-authorization-request.ts
|
|
603
|
+
import { Oauth2Error as Oauth2Error4 } from "@openid4vc/oauth2";
|
|
604
|
+
import { URL as URL3, URLSearchParams, objectToQueryParams, parseWithErrorHandling as parseWithErrorHandling2 } from "@openid4vc/utils";
|
|
605
|
+
|
|
606
|
+
// src/jar/create-jar-authorization-request.ts
|
|
607
|
+
import {
|
|
608
|
+
jwtHeaderFromJwtSigner
|
|
609
|
+
} from "@openid4vc/oauth2";
|
|
610
|
+
async function createJarAuthorizationRequest(options) {
|
|
611
|
+
const { jwtSigner, jweEncryptor, authorizationRequestPayload, requestUri, callbacks } = options;
|
|
612
|
+
let authorizationRequestJwt;
|
|
613
|
+
let encryptionJwk;
|
|
614
|
+
const { jwt, signerJwk } = await callbacks.signJwt(jwtSigner, {
|
|
615
|
+
header: { ...jwtHeaderFromJwtSigner(jwtSigner), typ: "oauth-authz-req+jwt" },
|
|
616
|
+
payload: { ...options.additionalJwtPayload, ...authorizationRequestPayload }
|
|
617
|
+
});
|
|
618
|
+
authorizationRequestJwt = jwt;
|
|
619
|
+
if (jweEncryptor) {
|
|
620
|
+
const encryptionResult = await callbacks.encryptJwe(jweEncryptor, authorizationRequestJwt);
|
|
621
|
+
authorizationRequestJwt = encryptionResult.jwe;
|
|
622
|
+
encryptionJwk = encryptionResult.encryptionJwk;
|
|
608
623
|
}
|
|
609
|
-
|
|
610
|
-
|
|
624
|
+
const client_id = authorizationRequestPayload.client_id;
|
|
625
|
+
const jarAuthorizationRequest = requestUri ? { client_id, request_uri: requestUri } : { client_id, request: authorizationRequestJwt };
|
|
626
|
+
return { jarAuthorizationRequest, signerJwk, encryptionJwk, authorizationRequestJwt };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/authorization-request/validate-authorization-request.ts
|
|
630
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes3, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError3 } from "@openid4vc/oauth2";
|
|
631
|
+
import { zHttpsUrl as zHttpsUrl3 } from "@openid4vc/utils";
|
|
632
|
+
var validateOpenid4vpAuthorizationRequestPayload = (options) => {
|
|
633
|
+
const { params, walletVerificationOptions } = options;
|
|
634
|
+
if (!params.redirect_uri && !params.response_uri) {
|
|
635
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
636
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
637
|
+
error_description: `Missing required 'redirect_uri' or 'response_uri' in openid4vp authorization request.`
|
|
638
|
+
});
|
|
611
639
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const highestPossibleVersion = lessThanVersions.length > 0 ? Math.max(Math.min(...lessThanVersions) - 1, 18) : 24;
|
|
615
|
-
const lowestRequiredVersion = greaterThanVersions.length > 0 ? Math.max(...greaterThanVersions) : 18;
|
|
616
|
-
if (lowestRequiredVersion > highestPossibleVersion) {
|
|
617
|
-
throw new Oauth2ServerErrorResponseError4({
|
|
640
|
+
if (params.response_uri && !["direct_post", "direct_post.jwt"].find((mode) => mode === params.response_mode)) {
|
|
641
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
618
642
|
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
619
|
-
error_description:
|
|
643
|
+
error_description: `The 'response_mode' parameter MUST be 'direct_post' or 'direct_post.jwt' when 'response_uri' is provided. Current: ${params.response_mode}`
|
|
620
644
|
});
|
|
621
645
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
return getLegacyClientId(options);
|
|
646
|
+
if ([params.presentation_definition_uri, params.presentation_definition, params.dcql_query, params.scope].filter(
|
|
647
|
+
Boolean
|
|
648
|
+
).length > 1) {
|
|
649
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
650
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
651
|
+
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."
|
|
652
|
+
});
|
|
630
653
|
}
|
|
631
|
-
if (
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
if (!options.jar || !options.authorizationRequestPayload.client_id) return `web-origin:${options.origin}`;
|
|
639
|
-
return options.authorizationRequestPayload.client_id;
|
|
654
|
+
if (params.request_uri_method && !params.request_uri) {
|
|
655
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
656
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
657
|
+
error_description: 'The "request_uri_method" parameter MUST NOT be present in the authorization request if the "request_uri" parameter is not present.'
|
|
658
|
+
});
|
|
640
659
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (isOpenid4vpAuthorizationRequestDcApi(options.authorizationRequestPayload)) {
|
|
647
|
-
if (!options.origin) {
|
|
648
|
-
throw new Oauth2ServerErrorResponseError5({
|
|
649
|
-
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
650
|
-
error_description: "Failed to parse client identifier. 'origin' is required for requests with response_mode 'dc_api' and 'dc_api.jwt'"
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
if (!options.jar || !options.authorizationRequestPayload.client_id) return `web-origin:${options.origin}`;
|
|
654
|
-
return `${clientIdScheme}:${options.authorizationRequestPayload.client_id}`;
|
|
660
|
+
if (params.request_uri_method && !["GET", "POST"].includes(params.request_uri_method)) {
|
|
661
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
662
|
+
error: Oauth2ErrorCodes3.InvalidRequestUriMethod,
|
|
663
|
+
error_description: `The 'request_uri_method' parameter MUST be 'GET' or 'POST'. Current: ${params.request_uri_method}`
|
|
664
|
+
});
|
|
655
665
|
}
|
|
656
|
-
if (
|
|
657
|
-
|
|
666
|
+
if (params.trust_chain && !zHttpsUrl3.safeParse(params.client_id).success) {
|
|
667
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
668
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
669
|
+
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://.'
|
|
670
|
+
});
|
|
658
671
|
}
|
|
659
|
-
if (
|
|
660
|
-
|
|
672
|
+
if (walletVerificationOptions?.expectedNonce && !params.wallet_nonce) {
|
|
673
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
674
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
675
|
+
error_description: 'The "wallet_nonce" parameter MUST be present in the authorization request when the "expectedNonce" parameter is provided.'
|
|
676
|
+
});
|
|
661
677
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
supportedSchemes: parserConfig?.supportedSchemes || Object.values(zClientIdScheme.options)
|
|
668
|
-
};
|
|
669
|
-
const clientId = getClientId(options);
|
|
670
|
-
const colonIndex = clientId.indexOf(":");
|
|
671
|
-
if (colonIndex === -1) {
|
|
672
|
-
return {
|
|
673
|
-
scheme: "pre-registered",
|
|
674
|
-
identifier: clientId,
|
|
675
|
-
originalValue: clientId,
|
|
676
|
-
clientMetadata: authorizationRequestPayload.client_metadata
|
|
677
|
-
};
|
|
678
|
+
if (walletVerificationOptions?.expectedNonce !== params.wallet_nonce) {
|
|
679
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
680
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
681
|
+
error_description: 'The "wallet_nonce" parameter MUST match the "expectedNonce" parameter when the "expectedNonce" parameter is provided.'
|
|
682
|
+
});
|
|
678
683
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
684
|
-
error_description: `Unsupported client identifier scheme. ${schemePart} is not supported.`
|
|
684
|
+
if (params.client_id.startsWith("web-origin:")) {
|
|
685
|
+
throw new Oauth2ServerErrorResponseError3({
|
|
686
|
+
error: Oauth2ErrorCodes3.InvalidRequest,
|
|
687
|
+
error_description: `The 'client_id' parameter MUST NOT use client identifier scheme 'web-origin' when not using the dc_api response mode. Current: ${params.client_id}`
|
|
685
688
|
});
|
|
686
689
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
originalValue: clientId,
|
|
699
|
-
trustChain: authorizationRequestPayload.trust_chain
|
|
700
|
-
};
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
// src/authorization-request/validate-authorization-request-dc-api.ts
|
|
693
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes4, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError4 } from "@openid4vc/oauth2";
|
|
694
|
+
var validateOpenid4vpAuthorizationRequestDcApiPayload = (options) => {
|
|
695
|
+
const { params, isJarRequest, disableOriginValidation, origin } = options;
|
|
696
|
+
if (isJarRequest && !params.expected_origins) {
|
|
697
|
+
throw new Oauth2ServerErrorResponseError4({
|
|
698
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
699
|
+
error_description: `The 'expected_origins' parameter MUST be present when using the dc_api response mode in combinaction with jar.`
|
|
700
|
+
});
|
|
701
701
|
}
|
|
702
|
-
if (
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
|
|
710
|
-
throw new Oauth2ServerErrorResponseError5({
|
|
711
|
-
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
712
|
-
error_description: `The client identifier scheme 'redirect_uri' is not supported when using the dc_api response mode.`
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
return {
|
|
716
|
-
scheme,
|
|
717
|
-
identifier: identifierPart,
|
|
718
|
-
originalValue: clientId,
|
|
719
|
-
redirectUri: authorizationRequestPayload.redirect_uri ?? authorizationRequestPayload.response_uri
|
|
720
|
-
};
|
|
702
|
+
if ([params.presentation_definition, params.dcql_query].filter(Boolean).length !== 1) {
|
|
703
|
+
throw new Oauth2ServerErrorResponseError4({
|
|
704
|
+
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
705
|
+
error_description: "Exactly one of the following parameters MUST be present in the Authorization Request: dcql_query or presentation_definition"
|
|
706
|
+
});
|
|
721
707
|
}
|
|
722
|
-
if (
|
|
723
|
-
if (!
|
|
724
|
-
throw new
|
|
708
|
+
if (params.expected_origins && !disableOriginValidation) {
|
|
709
|
+
if (!origin) {
|
|
710
|
+
throw new Oauth2ServerErrorResponseError4({
|
|
725
711
|
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
726
|
-
error_description:
|
|
712
|
+
error_description: `Failed to validate the 'origin' of the authorization request. The 'origin' was not provided.`
|
|
727
713
|
});
|
|
728
714
|
}
|
|
729
|
-
if (!
|
|
730
|
-
throw new
|
|
715
|
+
if (params.expected_origins && !params.expected_origins.includes(origin)) {
|
|
716
|
+
throw new Oauth2ServerErrorResponseError4({
|
|
731
717
|
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
732
|
-
error_description:
|
|
718
|
+
error_description: `The 'expected_origins' parameter MUST include the origin of the authorization request. Current: ${params.expected_origins.join(", ")}`
|
|
733
719
|
});
|
|
734
720
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
// src/authorization-request/create-authorization-request.ts
|
|
725
|
+
async function createOpenid4vpAuthorizationRequest(options) {
|
|
726
|
+
const { jar, scheme = "openid4vp://", wallet, callbacks } = options;
|
|
727
|
+
let additionalJwtPayload;
|
|
728
|
+
let authorizationRequestPayload;
|
|
729
|
+
if (isOpenid4vpAuthorizationRequestDcApi(options.authorizationRequestPayload)) {
|
|
730
|
+
authorizationRequestPayload = parseWithErrorHandling2(
|
|
731
|
+
zOpenid4vpAuthorizationRequestDcApi,
|
|
732
|
+
options.authorizationRequestPayload,
|
|
733
|
+
"Invalid authorization request. Could not parse openid4vp dc_api authorization request."
|
|
734
|
+
);
|
|
735
|
+
if (jar && !authorizationRequestPayload.expected_origins) {
|
|
736
|
+
throw new Oauth2Error4(
|
|
737
|
+
`The 'expected_origins' parameter MUST be present when using the dc_api response mode in combination with jar.`
|
|
738
|
+
);
|
|
740
739
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
740
|
+
validateOpenid4vpAuthorizationRequestDcApiPayload({
|
|
741
|
+
params: authorizationRequestPayload,
|
|
742
|
+
isJarRequest: Boolean(jar),
|
|
743
|
+
disableOriginValidation: true
|
|
744
|
+
});
|
|
745
|
+
} else {
|
|
746
|
+
authorizationRequestPayload = parseWithErrorHandling2(
|
|
747
|
+
zOpenid4vpAuthorizationRequest,
|
|
748
|
+
options.authorizationRequestPayload,
|
|
749
|
+
"Invalid authorization request. Could not parse openid4vp authorization request."
|
|
750
|
+
);
|
|
751
|
+
validateOpenid4vpAuthorizationRequestPayload({
|
|
752
|
+
params: authorizationRequestPayload,
|
|
753
|
+
walletVerificationOptions: wallet
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
if (jar) {
|
|
757
|
+
if (!jar.additionalJwtPayload?.aud) {
|
|
758
|
+
additionalJwtPayload = { ...jar.additionalJwtPayload, aud: jar.requestUri };
|
|
746
759
|
}
|
|
760
|
+
const jarResult = await createJarAuthorizationRequest({
|
|
761
|
+
...jar,
|
|
762
|
+
authorizationRequestPayload,
|
|
763
|
+
additionalJwtPayload,
|
|
764
|
+
callbacks
|
|
765
|
+
});
|
|
766
|
+
const url2 = new URL3(scheme);
|
|
767
|
+
url2.search = `?${new URLSearchParams([
|
|
768
|
+
...url2.searchParams.entries(),
|
|
769
|
+
...objectToQueryParams(jarResult.jarAuthorizationRequest).entries()
|
|
770
|
+
]).toString()}`;
|
|
747
771
|
return {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
772
|
+
authorizationRequestPayload,
|
|
773
|
+
authorizationRequestObject: jarResult.jarAuthorizationRequest,
|
|
774
|
+
authorizationRequest: url2.toString(),
|
|
775
|
+
jar: { ...jar, ...jarResult }
|
|
752
776
|
};
|
|
753
777
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
}
|
|
820
|
-
}
|
|
778
|
+
const url = new URL3(scheme);
|
|
779
|
+
url.search = `?${new URLSearchParams([
|
|
780
|
+
...url.searchParams.entries(),
|
|
781
|
+
...objectToQueryParams(authorizationRequestPayload).entries()
|
|
782
|
+
]).toString()}`;
|
|
783
|
+
return {
|
|
784
|
+
authorizationRequestPayload,
|
|
785
|
+
authorizationRequestObject: authorizationRequestPayload,
|
|
786
|
+
authorizationRequest: url.toString(),
|
|
787
|
+
jar: void 0
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/authorization-request/parse-authorization-request-params.ts
|
|
792
|
+
import { decodeJwt as decodeJwt2 } from "@openid4vc/oauth2";
|
|
793
|
+
import { parseWithErrorHandling as parseWithErrorHandling3 } from "@openid4vc/utils";
|
|
794
|
+
import z10 from "zod";
|
|
795
|
+
|
|
796
|
+
// src/jar/z-jar-authorization-request.ts
|
|
797
|
+
import { Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError5 } from "@openid4vc/oauth2";
|
|
798
|
+
import { zHttpsUrl as zHttpsUrl4 } from "@openid4vc/utils";
|
|
799
|
+
import { z as z9 } from "zod";
|
|
800
|
+
var zJarAuthorizationRequest = z9.object({
|
|
801
|
+
request: z9.optional(z9.string()),
|
|
802
|
+
request_uri: z9.optional(zHttpsUrl4),
|
|
803
|
+
request_uri_method: z9.optional(z9.string()),
|
|
804
|
+
client_id: z9.optional(z9.string())
|
|
805
|
+
}).passthrough();
|
|
806
|
+
function validateJarRequestParams(options) {
|
|
807
|
+
const { jarRequestParams } = options;
|
|
808
|
+
if (jarRequestParams.request && jarRequestParams.request_uri) {
|
|
809
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
810
|
+
error: "invalid_request_object",
|
|
811
|
+
error_description: "request and request_uri cannot both be present in a JAR request"
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
if (!jarRequestParams.request && !jarRequestParams.request_uri) {
|
|
815
|
+
throw new Oauth2ServerErrorResponseError5({
|
|
816
|
+
error: "invalid_request_object",
|
|
817
|
+
error_description: "request or request_uri must be present"
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
return jarRequestParams;
|
|
821
|
+
}
|
|
822
|
+
function isJarAuthorizationRequest(request) {
|
|
823
|
+
return "request" in request || "request_uri" in request;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/authorization-request/parse-authorization-request-params.ts
|
|
827
|
+
function parseOpenid4vpAuthorizationRequest(options) {
|
|
828
|
+
const { authorizationRequest } = options;
|
|
829
|
+
let provided = "params";
|
|
830
|
+
let params;
|
|
831
|
+
if (typeof authorizationRequest === "string") {
|
|
832
|
+
if (authorizationRequest.includes("://")) {
|
|
833
|
+
params = parseWithErrorHandling3(
|
|
834
|
+
zOpenid4vpAuthorizationRequestFromUriParams,
|
|
835
|
+
authorizationRequest,
|
|
836
|
+
"Unable to parse openid4vp authorization request uri to a valid object"
|
|
837
|
+
);
|
|
838
|
+
provided = "uri";
|
|
839
|
+
} else {
|
|
840
|
+
const decoded = decodeJwt2({ jwt: authorizationRequest });
|
|
841
|
+
params = decoded.payload;
|
|
842
|
+
provided = "jwt";
|
|
821
843
|
}
|
|
844
|
+
} else {
|
|
845
|
+
params = authorizationRequest;
|
|
846
|
+
}
|
|
847
|
+
const parsedRequest = parseWithErrorHandling3(
|
|
848
|
+
z10.union([zOpenid4vpAuthorizationRequest, zJarAuthorizationRequest, zOpenid4vpAuthorizationRequestDcApi]),
|
|
849
|
+
params
|
|
850
|
+
);
|
|
851
|
+
if (isJarAuthorizationRequest(parsedRequest)) {
|
|
822
852
|
return {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
x5c: jar.signer.x5c
|
|
853
|
+
type: "jar",
|
|
854
|
+
provided,
|
|
855
|
+
params: parsedRequest
|
|
827
856
|
};
|
|
828
857
|
}
|
|
829
|
-
if (
|
|
858
|
+
if (isOpenid4vpAuthorizationRequestDcApi(parsedRequest)) {
|
|
830
859
|
return {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
clientMetadata: authorizationRequestPayload.client_metadata
|
|
860
|
+
type: "openid4vp_dc_api",
|
|
861
|
+
provided,
|
|
862
|
+
params: parsedRequest
|
|
835
863
|
};
|
|
836
864
|
}
|
|
837
|
-
if (scheme === "verifier_attestation") {
|
|
838
|
-
if (!jar) {
|
|
839
|
-
throw new Oauth2ServerErrorResponseError5({
|
|
840
|
-
error: Oauth2ErrorCodes4.InvalidRequest,
|
|
841
|
-
error_description: 'Using client identifier scheme "verifier_attestation" requires a signed JAR request.'
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
865
|
return {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
866
|
+
type: "openid4vp",
|
|
867
|
+
provided,
|
|
868
|
+
params: parsedRequest
|
|
849
869
|
};
|
|
850
870
|
}
|
|
851
871
|
|
|
872
|
+
// src/authorization-request/resolve-authorization-request.ts
|
|
873
|
+
import { Oauth2ErrorCodes as Oauth2ErrorCodes9, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError10 } from "@openid4vc/oauth2";
|
|
874
|
+
import { parseWithErrorHandling as parseWithErrorHandling4 } from "@openid4vc/utils";
|
|
875
|
+
import z14 from "zod";
|
|
876
|
+
|
|
852
877
|
// src/fetch-client-metadata.ts
|
|
853
878
|
import { Oauth2ErrorCodes as Oauth2ErrorCodes5, Oauth2ServerErrorResponseError as Oauth2ServerErrorResponseError6 } from "@openid4vc/oauth2";
|
|
854
879
|
import { ContentType, createZodFetcher } from "@openid4vc/utils";
|
|
@@ -1547,7 +1572,7 @@ async function parseJarmAuthorizationResponse(options) {
|
|
|
1547
1572
|
// src/authorization-response/parse-authorization-response.ts
|
|
1548
1573
|
async function parseOpenid4vpAuthorizationResponse(options) {
|
|
1549
1574
|
const { authorizationResponse, callbacks, authorizationRequestPayload, origin } = options;
|
|
1550
|
-
const expectedClientId =
|
|
1575
|
+
const expectedClientId = getOpenid4vpClientId({ authorizationRequestPayload, origin });
|
|
1551
1576
|
if (authorizationResponse.response) {
|
|
1552
1577
|
return parseJarmAuthorizationResponse({
|
|
1553
1578
|
jarmResponseJwt: authorizationResponse.response,
|
|
@@ -1585,8 +1610,8 @@ var Openid4vpClient = class {
|
|
|
1585
1610
|
constructor(options) {
|
|
1586
1611
|
this.options = options;
|
|
1587
1612
|
}
|
|
1588
|
-
|
|
1589
|
-
return
|
|
1613
|
+
parseOpenid4vpAuthorizationRequest(options) {
|
|
1614
|
+
return parseOpenid4vpAuthorizationRequest(options);
|
|
1590
1615
|
}
|
|
1591
1616
|
async resolveOpenId4vpAuthorizationRequest(options) {
|
|
1592
1617
|
return resolveOpenid4vpAuthorizationRequest({ ...options, callbacks: this.options.callbacks });
|
|
@@ -1677,7 +1702,7 @@ var Openid4vpVerifier = class {
|
|
|
1677
1702
|
return createOpenid4vpAuthorizationRequest({ ...options, callbacks: this.options.callbacks });
|
|
1678
1703
|
}
|
|
1679
1704
|
parseOpenid4vpAuthorizationRequestPayload(options) {
|
|
1680
|
-
return
|
|
1705
|
+
return parseOpenid4vpAuthorizationRequest(options);
|
|
1681
1706
|
}
|
|
1682
1707
|
parseOpenid4vpAuthorizationResponse(options) {
|
|
1683
1708
|
return parseOpenid4vpAuthorizationResponse(options);
|
|
@@ -1695,7 +1720,10 @@ var Openid4vpVerifier = class {
|
|
|
1695
1720
|
return parseTransactionData(options);
|
|
1696
1721
|
}
|
|
1697
1722
|
verifyTransactionData(options) {
|
|
1698
|
-
return verifyTransactionData(
|
|
1723
|
+
return verifyTransactionData({
|
|
1724
|
+
...options,
|
|
1725
|
+
callbacks: this.options.callbacks
|
|
1726
|
+
});
|
|
1699
1727
|
}
|
|
1700
1728
|
};
|
|
1701
1729
|
|
|
@@ -1722,11 +1750,12 @@ export {
|
|
|
1722
1750
|
Openid4vpVerifier,
|
|
1723
1751
|
createOpenid4vpAuthorizationRequest,
|
|
1724
1752
|
createOpenid4vpAuthorizationResponse,
|
|
1753
|
+
getOpenid4vpClientId,
|
|
1725
1754
|
isJarmResponseMode,
|
|
1726
1755
|
isOpenid4vpAuthorizationRequestDcApi,
|
|
1727
1756
|
parseDcqlVpToken,
|
|
1728
1757
|
parseJarmAuthorizationResponse,
|
|
1729
|
-
|
|
1758
|
+
parseOpenid4vpAuthorizationRequest,
|
|
1730
1759
|
parseOpenid4vpAuthorizationResponse,
|
|
1731
1760
|
parsePexVpToken,
|
|
1732
1761
|
parseTransactionData,
|