@bedrock/vc-delivery 5.0.1 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/http.js +10 -73
- package/lib/oid4/http.js +327 -0
- package/lib/oid4/oid4vci.js +566 -0
- package/lib/oid4/oid4vp.js +330 -0
- package/lib/vcapi.js +77 -1
- package/package.json +1 -1
- package/lib/openId.js +0 -1057
package/lib/http.js
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Copyright (c) 2018-2024 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
|
-
import * as _openId from './openId.js';
|
|
5
4
|
import * as bedrock from '@bedrock/core';
|
|
6
5
|
import * as exchanges from './exchanges.js';
|
|
6
|
+
import * as oid4 from './oid4/http.js';
|
|
7
|
+
import {createExchange, processExchange} from './vcapi.js';
|
|
7
8
|
import {
|
|
8
9
|
createExchangeBody, useExchangeBody
|
|
9
10
|
} from '../schemas/bedrock-vc-workflow.js';
|
|
10
|
-
import {exportJWK, generateKeyPair, importJWK} from 'jose';
|
|
11
|
-
import {generateRandom, getWorkflowId} from './helpers.js';
|
|
12
11
|
import {metering, middleware} from '@bedrock/service-core';
|
|
13
12
|
import {asyncHandler} from '@bedrock/express';
|
|
14
13
|
import bodyParser from 'body-parser';
|
|
15
14
|
import cors from 'cors';
|
|
15
|
+
import {getWorkflowId} from './helpers.js';
|
|
16
16
|
import {logger} from './logger.js';
|
|
17
|
-
import {processExchange} from './vcapi.js';
|
|
18
17
|
import {createValidateMiddleware as validate} from '@bedrock/validation';
|
|
19
18
|
|
|
20
|
-
const {util: {BedrockError}} = bedrock;
|
|
21
|
-
|
|
22
19
|
// FIXME: remove and apply at top-level application
|
|
23
20
|
bedrock.events.on('bedrock-express.configure.bodyParser', app => {
|
|
24
21
|
app.use(bodyParser.json({
|
|
@@ -73,75 +70,15 @@ export async function addRoutes({app, service} = {}) {
|
|
|
73
70
|
// FIXME: check available storage via meter before allowing operation
|
|
74
71
|
|
|
75
72
|
try {
|
|
76
|
-
const {config} = req.serviceObject;
|
|
73
|
+
const {config: workflow} = req.serviceObject;
|
|
77
74
|
const {
|
|
78
75
|
ttl, openId, variables = {},
|
|
79
76
|
// allow steps to be skipped by creator as needed
|
|
80
|
-
step =
|
|
77
|
+
step = workflow.initialStep
|
|
81
78
|
} = req.body;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
throw new BedrockError(`Undefined step "${step}".`, {
|
|
86
|
-
name: 'DataError',
|
|
87
|
-
details: {httpStatusCode: 400, public: true}
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if(openId) {
|
|
92
|
-
// either issuer instances or a single issuer zcap be given if
|
|
93
|
-
// any expected credential requests are given
|
|
94
|
-
const {expectedCredentialRequests} = openId;
|
|
95
|
-
if(expectedCredentialRequests &&
|
|
96
|
-
!(config.issuerInstances || config.zcaps.issue)) {
|
|
97
|
-
throw new BedrockError(
|
|
98
|
-
'Credential requests are not supported by this workflow.', {
|
|
99
|
-
name: 'DataError',
|
|
100
|
-
details: {httpStatusCode: 400, public: true}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// perform key generation if requested
|
|
105
|
-
if(openId.oauth2?.generateKeyPair) {
|
|
106
|
-
const {oauth2} = openId;
|
|
107
|
-
const {algorithm} = oauth2.generateKeyPair;
|
|
108
|
-
const kp = await generateKeyPair(algorithm, {extractable: true});
|
|
109
|
-
const [privateKeyJwk, publicKeyJwk] = await Promise.all([
|
|
110
|
-
exportJWK(kp.privateKey),
|
|
111
|
-
exportJWK(kp.publicKey),
|
|
112
|
-
]);
|
|
113
|
-
oauth2.keyPair = {privateKeyJwk, publicKeyJwk};
|
|
114
|
-
delete oauth2.generateKeyPair;
|
|
115
|
-
} else {
|
|
116
|
-
// ensure key pair can be imported
|
|
117
|
-
try {
|
|
118
|
-
const {oauth2: {keyPair}} = openId;
|
|
119
|
-
await Promise.all([
|
|
120
|
-
importJWK(keyPair.privateKeyJwk),
|
|
121
|
-
importJWK(keyPair.publicKeyJwk)
|
|
122
|
-
]);
|
|
123
|
-
} catch(e) {
|
|
124
|
-
throw new BedrockError(
|
|
125
|
-
'Could not import OpenID OAuth2 key pair.', {
|
|
126
|
-
name: 'DataError',
|
|
127
|
-
details: {httpStatusCode: 400, public: true},
|
|
128
|
-
cause: e
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// insert exchange
|
|
135
|
-
const {id: workflowId} = config;
|
|
136
|
-
const exchange = {
|
|
137
|
-
id: await generateRandom(),
|
|
138
|
-
ttl,
|
|
139
|
-
variables,
|
|
140
|
-
openId,
|
|
141
|
-
step
|
|
142
|
-
};
|
|
143
|
-
await exchanges.insert({workflowId, exchange});
|
|
144
|
-
const location = `${workflowId}/exchanges/${exchange.id}`;
|
|
79
|
+
const exchange = {ttl, openId, variables, step};
|
|
80
|
+
const {id} = await createExchange({workflow, exchange});
|
|
81
|
+
const location = `${workflow.id}/exchanges/${id}`;
|
|
145
82
|
res.status(204).location(location).send();
|
|
146
83
|
} catch(error) {
|
|
147
84
|
logger.error(error.message, {error});
|
|
@@ -180,7 +117,7 @@ export async function addRoutes({app, service} = {}) {
|
|
|
180
117
|
await processExchange({req, res, workflow, exchange});
|
|
181
118
|
}));
|
|
182
119
|
|
|
183
|
-
// create
|
|
184
|
-
await
|
|
120
|
+
// create OID4* routes to be used with each individual exchange
|
|
121
|
+
await oid4.createRoutes(
|
|
185
122
|
{app, exchangeRoute: routes.exchange, getConfigMiddleware, getExchange});
|
|
186
123
|
}
|
package/lib/oid4/http.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2022-2024 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import * as oid4vci from './oid4vci.js';
|
|
5
|
+
import * as oid4vp from './oid4vp.js';
|
|
6
|
+
import {
|
|
7
|
+
openIdAuthorizationResponseBody,
|
|
8
|
+
openIdBatchCredentialBody,
|
|
9
|
+
openIdCredentialBody,
|
|
10
|
+
openIdTokenBody
|
|
11
|
+
} from '../../schemas/bedrock-vc-workflow.js';
|
|
12
|
+
import {asyncHandler} from '@bedrock/express';
|
|
13
|
+
import bodyParser from 'body-parser';
|
|
14
|
+
import cors from 'cors';
|
|
15
|
+
import {UnsecuredJWT} from 'jose';
|
|
16
|
+
import {createValidateMiddleware as validate} from '@bedrock/validation';
|
|
17
|
+
|
|
18
|
+
/* NOTE: Parts of the OID4VCI design imply tight integration between the
|
|
19
|
+
authorization server and the credential issuance / delivery server. This
|
|
20
|
+
file provides the routes for both and treats them as integrated; supporting
|
|
21
|
+
the OID4VCI pre-authz code flow only as a result. However, we also try to
|
|
22
|
+
avoid tight-coupling where possible to enable the non-pre-authz code flow
|
|
23
|
+
that would use, somehow, a separate authorization server.
|
|
24
|
+
|
|
25
|
+
One tight coupling we try to avoid involves the option where the authorization
|
|
26
|
+
server generates the challenge nonce to be signed in a DID proof, but the
|
|
27
|
+
credential delivery server is the system responsible for checking and tracking
|
|
28
|
+
this challenge. The Credential Delivery server cannot know the challenge is
|
|
29
|
+
authentic without breaking some abstraction around how the Authorization
|
|
30
|
+
Server is implemented behind its API. Here we do not implement this option,
|
|
31
|
+
instead, if a challenge is required, the credential delivery server will send
|
|
32
|
+
an error with the challenge nonce if one was not provided in the payload to the
|
|
33
|
+
credential endpoint. This error follows the OID4VCI spec and avoids this
|
|
34
|
+
particular tight coupling.
|
|
35
|
+
|
|
36
|
+
Other tight couplings cannot be avoided at this time -- such as the fact that
|
|
37
|
+
the credential endpoint is specified in the authorization server's metadata;
|
|
38
|
+
this creates challenges for SaaS based solutions and for issuers that want to
|
|
39
|
+
use multiple different Issuance / Delivery server backends. We solve these
|
|
40
|
+
challenges by using the "pre-authorized code" flows and effectively
|
|
41
|
+
instantiating a new authorization server instance per VC exchange. */
|
|
42
|
+
|
|
43
|
+
// creates OID4VCI Authorization Server + Credential Delivery Server
|
|
44
|
+
// endpoints for each individual exchange
|
|
45
|
+
export async function createRoutes({
|
|
46
|
+
app, exchangeRoute, getConfigMiddleware, getExchange
|
|
47
|
+
} = {}) {
|
|
48
|
+
const openIdRoute = `${exchangeRoute}/openid`;
|
|
49
|
+
const routes = {
|
|
50
|
+
// OID4VCI routes
|
|
51
|
+
asMetadata1: `/.well-known/oauth-authorization-server${exchangeRoute}`,
|
|
52
|
+
asMetadata2: `${exchangeRoute}/.well-known/oauth-authorization-server`,
|
|
53
|
+
ciMetadata1: `/.well-known/openid-credential-issuer${exchangeRoute}`,
|
|
54
|
+
ciMetadata2: `${exchangeRoute}/.well-known/openid-credential-issuer`,
|
|
55
|
+
batchCredential: `${openIdRoute}/batch_credential`,
|
|
56
|
+
credential: `${openIdRoute}/credential`,
|
|
57
|
+
credentialOffer: `${openIdRoute}/credential-offer`,
|
|
58
|
+
token: `${openIdRoute}/token`,
|
|
59
|
+
jwks: `${openIdRoute}/jwks`,
|
|
60
|
+
// OID4VP routes
|
|
61
|
+
authorizationRequest: `${openIdRoute}/client/authorization/request`,
|
|
62
|
+
authorizationResponse: `${openIdRoute}/client/authorization/response`
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// urlencoded body parser (extended=true for rich JSON-like representation)
|
|
66
|
+
const urlencoded = bodyParser.urlencoded({extended: true});
|
|
67
|
+
|
|
68
|
+
/* Note: The well-known metadata paths for the OID4VCI spec have been
|
|
69
|
+
specified in at least two different ways over time, including
|
|
70
|
+
`<path>/.well-known/...` and `/.well-known/.../<path>`, so they are provided
|
|
71
|
+
here using both approaches to maximize interoperability with clients. It
|
|
72
|
+
is also notable that some versions of the spec have indicated that the
|
|
73
|
+
credential issuer metadata should be expressed in the authorization server
|
|
74
|
+
metadata and others have indicated that they can be separate; since our
|
|
75
|
+
approach virtualizes both the AS and CI anyway, it is all served together. */
|
|
76
|
+
|
|
77
|
+
// an authorization server meta data endpoint
|
|
78
|
+
// serves `.well-known` oauth2 AS config for each exchange; each config is
|
|
79
|
+
// based on the workflow used to create the exchange
|
|
80
|
+
app.get(
|
|
81
|
+
routes.asMetadata1,
|
|
82
|
+
cors(),
|
|
83
|
+
getConfigMiddleware,
|
|
84
|
+
getExchange,
|
|
85
|
+
asyncHandler(async (req, res) => {
|
|
86
|
+
res.json(await oid4vci.getAuthorizationServerConfig({req}));
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
// an authorization server meta data endpoint
|
|
90
|
+
// serves `.well-known` oauth2 AS config for each exchange; each config is
|
|
91
|
+
// based on the workflow used to create the exchange
|
|
92
|
+
app.get(
|
|
93
|
+
routes.asMetadata2,
|
|
94
|
+
cors(),
|
|
95
|
+
getConfigMiddleware,
|
|
96
|
+
getExchange,
|
|
97
|
+
asyncHandler(async (req, res) => {
|
|
98
|
+
res.json(await oid4vci.getAuthorizationServerConfig({req}));
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
// a credential issuer meta data endpoint
|
|
102
|
+
// serves `.well-known` oauth2 AS / CI config for each exchange; each config
|
|
103
|
+
// is based on the workflow used to create the exchange
|
|
104
|
+
app.get(
|
|
105
|
+
routes.ciMetadata1,
|
|
106
|
+
cors(),
|
|
107
|
+
getConfigMiddleware,
|
|
108
|
+
getExchange,
|
|
109
|
+
asyncHandler(async (req, res) => {
|
|
110
|
+
res.json(await oid4vci.getCredentialIssuerConfig({req}));
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
// a credential issuer meta data endpoint
|
|
114
|
+
// serves `.well-known` oauth2 AS / CI config for each exchange; each config
|
|
115
|
+
// is based on the workflow used to create the exchange
|
|
116
|
+
app.get(
|
|
117
|
+
routes.ciMetadata2,
|
|
118
|
+
cors(),
|
|
119
|
+
getConfigMiddleware,
|
|
120
|
+
getExchange,
|
|
121
|
+
asyncHandler(async (req, res) => {
|
|
122
|
+
res.json(await oid4vci.getCredentialIssuerConfig({req}));
|
|
123
|
+
}));
|
|
124
|
+
|
|
125
|
+
// an authorization server endpoint
|
|
126
|
+
// serves JWKs associated with each exchange; JWKs are stored with the
|
|
127
|
+
// workflow used to create the exchange
|
|
128
|
+
app.get(
|
|
129
|
+
routes.jwks,
|
|
130
|
+
cors(),
|
|
131
|
+
getExchange,
|
|
132
|
+
asyncHandler(async (req, res) => {
|
|
133
|
+
// serve exchange's public key(s)
|
|
134
|
+
const keys = await oid4vci.getJwks({req});
|
|
135
|
+
res.json({keys});
|
|
136
|
+
}));
|
|
137
|
+
|
|
138
|
+
// an authorization server endpoint
|
|
139
|
+
// handles pre-authorization code exchange for access token; only supports
|
|
140
|
+
// pre-authorization code grant type
|
|
141
|
+
app.options(routes.token, cors());
|
|
142
|
+
app.post(
|
|
143
|
+
routes.token,
|
|
144
|
+
cors(),
|
|
145
|
+
urlencoded,
|
|
146
|
+
validate({bodySchema: openIdTokenBody}),
|
|
147
|
+
getConfigMiddleware,
|
|
148
|
+
getExchange,
|
|
149
|
+
asyncHandler(async (req, res) => {
|
|
150
|
+
const response = await oid4vci.processAccessTokenRequest({req, res});
|
|
151
|
+
res.json(response);
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
// a credential delivery server endpoint
|
|
155
|
+
// receives a credential request and returns VCs
|
|
156
|
+
app.options(routes.credential, cors());
|
|
157
|
+
app.post(
|
|
158
|
+
routes.credential,
|
|
159
|
+
cors(),
|
|
160
|
+
validate({bodySchema: openIdCredentialBody}),
|
|
161
|
+
getConfigMiddleware,
|
|
162
|
+
getExchange,
|
|
163
|
+
asyncHandler(async (req, res) => {
|
|
164
|
+
/* Clients must POST, e.g.:
|
|
165
|
+
POST /credential HTTP/1.1
|
|
166
|
+
Host: server.example.com
|
|
167
|
+
Content-Type: application/json
|
|
168
|
+
Authorization: BEARER czZCaGRSa3F0MzpnWDFmQmF0M2JW
|
|
169
|
+
|
|
170
|
+
{
|
|
171
|
+
"format": "ldp_vc",
|
|
172
|
+
"credential_definition": {
|
|
173
|
+
"@context": [
|
|
174
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
175
|
+
"https://www.w3.org/2018/credentials/examples/v1"
|
|
176
|
+
],
|
|
177
|
+
"type": [
|
|
178
|
+
"VerifiableCredential",
|
|
179
|
+
"UniversityDegreeCredential"
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
"did": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
|
183
|
+
"proof": {
|
|
184
|
+
"proof_type": "jwt",
|
|
185
|
+
"jwt": "eyJra...nOzM"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
*/
|
|
189
|
+
const result = await oid4vci.processCredentialRequests({
|
|
190
|
+
req, res, isBatchRequest: false
|
|
191
|
+
});
|
|
192
|
+
if(!result) {
|
|
193
|
+
// DID proof request response sent
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Note: The `/credential` route only supports sending a single VC;
|
|
198
|
+
assume here that this workflow is configured for a single VC and an
|
|
199
|
+
error code would have been sent to the client to use the batch
|
|
200
|
+
endpoint if there was more than one VC to deliver. */
|
|
201
|
+
const {response, format} = result;
|
|
202
|
+
const {verifiablePresentation: {verifiableCredential: [vc]}} = response;
|
|
203
|
+
|
|
204
|
+
// parse any enveloped VC
|
|
205
|
+
let credential;
|
|
206
|
+
if(vc.type === 'EnvelopedVerifiableCredential' &&
|
|
207
|
+
vc.id?.startsWith('data:application/jwt,')) {
|
|
208
|
+
credential = vc.id.slice('data:application/jwt,'.length);
|
|
209
|
+
} else {
|
|
210
|
+
credential = vc;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// send OID4VCI response
|
|
214
|
+
res.json({
|
|
215
|
+
// FIXME: this doesn't seem to be in the spec anymore (draft 14+)...
|
|
216
|
+
format,
|
|
217
|
+
credential
|
|
218
|
+
});
|
|
219
|
+
}));
|
|
220
|
+
|
|
221
|
+
// a credential delivery server endpoint
|
|
222
|
+
// serves the credential offer for all possible credentials in the exchange
|
|
223
|
+
app.get(
|
|
224
|
+
routes.credentialOffer,
|
|
225
|
+
cors(),
|
|
226
|
+
getConfigMiddleware,
|
|
227
|
+
getExchange,
|
|
228
|
+
asyncHandler(async (req, res) => {
|
|
229
|
+
const offer = await oid4vci.getCredentialOffer({req});
|
|
230
|
+
res.json(offer);
|
|
231
|
+
}));
|
|
232
|
+
|
|
233
|
+
// a batch credential delivery server endpoint
|
|
234
|
+
// receives N credential requests and returns N VCs
|
|
235
|
+
app.options(routes.batchCredential, cors());
|
|
236
|
+
app.post(
|
|
237
|
+
routes.batchCredential,
|
|
238
|
+
cors(),
|
|
239
|
+
validate({bodySchema: openIdBatchCredentialBody}),
|
|
240
|
+
getConfigMiddleware,
|
|
241
|
+
getExchange,
|
|
242
|
+
asyncHandler(async (req, res) => {
|
|
243
|
+
/* Clients must POST, e.g.:
|
|
244
|
+
POST /batch_credential HTTP/1.1
|
|
245
|
+
Host: server.example.com
|
|
246
|
+
Content-Type: application/json
|
|
247
|
+
Authorization: BEARER czZCaGRSa3F0MzpnWDFmQmF0M2JW
|
|
248
|
+
|
|
249
|
+
{
|
|
250
|
+
credential_requests: [{
|
|
251
|
+
"format": "ldp_vc",
|
|
252
|
+
"credential_definition": {
|
|
253
|
+
"@context": [
|
|
254
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
255
|
+
"https://www.w3.org/2018/credentials/examples/v1"
|
|
256
|
+
],
|
|
257
|
+
"type": [
|
|
258
|
+
"VerifiableCredential",
|
|
259
|
+
"UniversityDegreeCredential"
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
"did": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
|
263
|
+
"proof": {
|
|
264
|
+
"proof_type": "jwt",
|
|
265
|
+
"jwt": "eyJra...nOzM"
|
|
266
|
+
}
|
|
267
|
+
}]
|
|
268
|
+
}
|
|
269
|
+
*/
|
|
270
|
+
const result = await oid4vci.processCredentialRequests({
|
|
271
|
+
req, res, isBatchRequest: true
|
|
272
|
+
});
|
|
273
|
+
if(!result) {
|
|
274
|
+
// DID proof request response sent
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// send VCs
|
|
279
|
+
const {response: {verifiablePresentation}, format} = result;
|
|
280
|
+
// FIXME: "format" doesn't seem to be in the spec anymore (draft 14+)...
|
|
281
|
+
const responses = verifiablePresentation.verifiableCredential.map(vc => {
|
|
282
|
+
// parse any enveloped VC
|
|
283
|
+
let credential;
|
|
284
|
+
if(vc.type === 'EnvelopedVerifiableCredential' &&
|
|
285
|
+
vc.id?.startsWith('data:application/jwt,')) {
|
|
286
|
+
credential = vc.id.slice('data:application/jwt,'.length);
|
|
287
|
+
} else {
|
|
288
|
+
credential = vc;
|
|
289
|
+
}
|
|
290
|
+
return {format, credential};
|
|
291
|
+
});
|
|
292
|
+
res.json({credential_responses: responses});
|
|
293
|
+
}));
|
|
294
|
+
|
|
295
|
+
// an OID4VP verifier endpoint
|
|
296
|
+
// serves the authorization request, including presentation definition
|
|
297
|
+
// associated with the current step in the exchange
|
|
298
|
+
app.get(
|
|
299
|
+
routes.authorizationRequest,
|
|
300
|
+
cors(),
|
|
301
|
+
getConfigMiddleware,
|
|
302
|
+
getExchange,
|
|
303
|
+
asyncHandler(async (req, res) => {
|
|
304
|
+
const {
|
|
305
|
+
authorizationRequest
|
|
306
|
+
} = await oid4vp.getAuthorizationRequest({req});
|
|
307
|
+
// construct and send authz request as unsecured JWT
|
|
308
|
+
const jwt = new UnsecuredJWT(authorizationRequest).encode();
|
|
309
|
+
res.set('content-type', 'application/oauth-authz-req+jwt');
|
|
310
|
+
res.send(jwt);
|
|
311
|
+
}));
|
|
312
|
+
|
|
313
|
+
// an OID4VP verifier endpoint
|
|
314
|
+
// receives an authorization response with vp_token
|
|
315
|
+
app.options(routes.authorizationResponse, cors());
|
|
316
|
+
app.post(
|
|
317
|
+
routes.authorizationResponse,
|
|
318
|
+
cors(),
|
|
319
|
+
urlencoded,
|
|
320
|
+
validate({bodySchema: openIdAuthorizationResponseBody()}),
|
|
321
|
+
getConfigMiddleware,
|
|
322
|
+
getExchange,
|
|
323
|
+
asyncHandler(async (req, res) => {
|
|
324
|
+
const result = await oid4vp.processAuthorizationResponse({req});
|
|
325
|
+
res.json(result);
|
|
326
|
+
}));
|
|
327
|
+
}
|