@sphereon/ssi-sdk.siopv2-oid4vp-rp-rest-api 0.34.1-feature.SSISDK.47.42 → 0.34.1-feature.SSISDK.50.100
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.cjs +120 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -87
- package/dist/index.d.ts +7 -87
- package/dist/index.js +117 -195
- package/dist/index.js.map +1 -1
- package/package.json +19 -22
- package/src/index.ts +1 -1
- package/src/siop-api-functions.ts +10 -20
- package/src/siopv2-rp-api-server.ts +7 -7
- package/src/types/types.ts +1 -42
- package/src/webapp-api-functions.ts +183 -0
- package/src/middleware/validationMiddleware.ts +0 -20
- package/src/schemas/index.ts +0 -41
- package/src/universal-oid4vp-api-functions.ts +0 -174
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sphereon/ssi-sdk.siopv2-oid4vp-rp-rest-api",
|
|
3
|
-
"version": "0.34.1-feature.SSISDK.
|
|
3
|
+
"version": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
4
4
|
"source": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -23,42 +23,39 @@
|
|
|
23
23
|
"start:dev": "ts-node __tests__/RestAPI.ts"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@sphereon/did-auth-siop": "0.19.1-feature.SSISDK.
|
|
27
|
-
"@sphereon/ssi-express-support": "0.34.1-feature.SSISDK.
|
|
28
|
-
"@sphereon/ssi-sdk.core": "0.34.1-feature.SSISDK.
|
|
29
|
-
"@sphereon/ssi-sdk.credential-validation": "0.34.1-feature.SSISDK.
|
|
30
|
-
"@sphereon/ssi-sdk.kv-store-temp": "0.34.1-feature.SSISDK.
|
|
31
|
-
"@sphereon/ssi-sdk.pd-manager": "0.34.1-feature.SSISDK.
|
|
32
|
-
"@sphereon/ssi-sdk.presentation-exchange": "0.34.1-feature.SSISDK.
|
|
33
|
-
"@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.34.1-feature.SSISDK.
|
|
34
|
-
"@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth": "0.34.1-feature.SSISDK.
|
|
35
|
-
"@sphereon/ssi-types": "0.34.1-feature.SSISDK.
|
|
26
|
+
"@sphereon/did-auth-siop": "0.19.1-feature.SSISDK.45.92",
|
|
27
|
+
"@sphereon/ssi-express-support": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
28
|
+
"@sphereon/ssi-sdk.core": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
29
|
+
"@sphereon/ssi-sdk.credential-validation": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
30
|
+
"@sphereon/ssi-sdk.kv-store-temp": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
31
|
+
"@sphereon/ssi-sdk.pd-manager": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
32
|
+
"@sphereon/ssi-sdk.presentation-exchange": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
33
|
+
"@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
34
|
+
"@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
35
|
+
"@sphereon/ssi-types": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
36
36
|
"@veramo/core": "4.2.0",
|
|
37
37
|
"@veramo/credential-w3c": "4.2.0",
|
|
38
38
|
"awesome-qr": "^2.1.5-rc.0",
|
|
39
39
|
"body-parser": "^1.20.2",
|
|
40
40
|
"cookie-parser": "^1.4.6",
|
|
41
41
|
"cors": "^2.8.5",
|
|
42
|
-
"cross-fetch": "^
|
|
42
|
+
"cross-fetch": "^4.1.0",
|
|
43
43
|
"dotenv-flow": "^3.3.0",
|
|
44
44
|
"express": "^4.19.2",
|
|
45
45
|
"short-uuid": "^4.2.2",
|
|
46
46
|
"swagger-ui-express": "^5.0.1",
|
|
47
|
-
"uuid": "^9.0.1"
|
|
48
|
-
"zod": "^4.1.5"
|
|
47
|
+
"uuid": "^9.0.1"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
50
|
"@decentralized-identity/ion-sdk": "^0.6.0",
|
|
52
|
-
"@sphereon/did-auth-siop-adapter": "0.19.1-feature.SSISDK.
|
|
51
|
+
"@sphereon/did-auth-siop-adapter": "0.19.1-feature.SSISDK.45.92",
|
|
53
52
|
"@sphereon/did-uni-client": "^0.6.3",
|
|
54
53
|
"@sphereon/pex": "5.0.0-unstable.28",
|
|
55
54
|
"@sphereon/pex-models": "^2.3.2",
|
|
56
|
-
"@sphereon/ssi-sdk-ext.did-provider-jwk": "0.34.1-feature.SSISDK.
|
|
57
|
-
"@sphereon/ssi-sdk
|
|
58
|
-
"@sphereon/ssi-sdk
|
|
59
|
-
"@sphereon/ssi-sdk.
|
|
60
|
-
"@sphereon/ssi-sdk.credential-vcdm-jsonld-provider": "0.34.1-feature.SSISDK.47.42+117ad37d",
|
|
61
|
-
"@sphereon/ssi-sdk.data-store": "0.34.1-feature.SSISDK.47.42+117ad37d",
|
|
55
|
+
"@sphereon/ssi-sdk-ext.did-provider-jwk": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
56
|
+
"@sphereon/ssi-sdk.credential-vcdm": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
57
|
+
"@sphereon/ssi-sdk.credential-vcdm-jsonld-provider": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
58
|
+
"@sphereon/ssi-sdk.data-store": "0.34.1-feature.SSISDK.50.100+f7758c66",
|
|
62
59
|
"@types/body-parser": "^1.19.5",
|
|
63
60
|
"@types/cookie-parser": "^1.4.7",
|
|
64
61
|
"@types/cors": "^2.8.17",
|
|
@@ -117,5 +114,5 @@
|
|
|
117
114
|
"OpenID Connect",
|
|
118
115
|
"Authenticator"
|
|
119
116
|
],
|
|
120
|
-
"gitHead": "
|
|
117
|
+
"gitHead": "f7758c6686c8af51167365b8ecf2aa383d4b69a6"
|
|
121
118
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthorizationResponsePayload
|
|
1
|
+
import { AuthorizationResponsePayload } from '@sphereon/did-auth-siop'
|
|
2
2
|
import { checkAuth, ISingleEndpointOpts, sendErrorResponse } from '@sphereon/ssi-express-support'
|
|
3
3
|
import { CredentialMapper } from '@sphereon/ssi-types'
|
|
4
4
|
import { AuthorizationChallengeValidationResponse } from '@sphereon/ssi-sdk.siopv2-oid4vp-common'
|
|
@@ -41,11 +41,7 @@ const parseAuthorizationResponse = (request: Request): AuthorizationResponsePayl
|
|
|
41
41
|
)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export function verifyAuthResponseSIOPv2Endpoint(
|
|
45
|
-
router: Router,
|
|
46
|
-
context: IRequiredContext,
|
|
47
|
-
opts?: ISingleEndpointOpts & { presentationDefinitionLocation?: PresentationDefinitionLocation },
|
|
48
|
-
) {
|
|
44
|
+
export function verifyAuthResponseSIOPv2Endpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) {
|
|
49
45
|
if (opts?.enabled === false) {
|
|
50
46
|
console.log(`verifyAuthResponse SIOP endpoint is disabled`)
|
|
51
47
|
return
|
|
@@ -53,7 +49,7 @@ export function verifyAuthResponseSIOPv2Endpoint(
|
|
|
53
49
|
const path = opts?.path ?? '/siop/definitions/:definitionId/auth-responses/:correlationId'
|
|
54
50
|
router.post(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => {
|
|
55
51
|
try {
|
|
56
|
-
const { correlationId, definitionId, tenantId, version } = request.params
|
|
52
|
+
const { correlationId, definitionId, tenantId, version, credentialQueryId } = request.params // TODO Can credentialQueryId be a request param
|
|
57
53
|
if (!correlationId || !definitionId) {
|
|
58
54
|
console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`)
|
|
59
55
|
return sendErrorResponse(response, 404, 'No authorization request could be found')
|
|
@@ -75,17 +71,11 @@ export function verifyAuthResponseSIOPv2Endpoint(
|
|
|
75
71
|
const verifiedResponse = await context.agent.siopVerifyAuthResponse({
|
|
76
72
|
authorizationResponse,
|
|
77
73
|
correlationId,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
{
|
|
81
|
-
location: opts?.presentationDefinitionLocation ?? PresentationDefinitionLocation.TOPLEVEL_PRESENTATION_DEF,
|
|
82
|
-
definition: definitionItem.definitionPayload,
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
dcqlQuery: definitionItem.dcqlPayload,
|
|
74
|
+
definitionId,
|
|
75
|
+
dcqlQueryPayload: definitionItem.dcqlPayload,
|
|
86
76
|
})
|
|
87
77
|
|
|
88
|
-
const wrappedPresentation = verifiedResponse?.oid4vpSubmission?.
|
|
78
|
+
const wrappedPresentation = verifiedResponse?.oid4vpSubmission?.presentation[credentialQueryId]
|
|
89
79
|
if (wrappedPresentation) {
|
|
90
80
|
// const credentialSubject = wrappedPresentation.presentation.verifiableCredential[0]?.credential?.credentialSubject
|
|
91
81
|
// console.log(JSON.stringify(credentialSubject, null, 2))
|
|
@@ -100,7 +90,7 @@ export function verifyAuthResponseSIOPv2Endpoint(
|
|
|
100
90
|
return response.send(JSON.stringify(authorizationChallengeValidationResponse))
|
|
101
91
|
}
|
|
102
92
|
|
|
103
|
-
const responseRedirectURI = await context.agent.siopGetRedirectURI({ correlationId,
|
|
93
|
+
const responseRedirectURI = await context.agent.siopGetRedirectURI({ correlationId, definitionId, state: verifiedResponse.state })
|
|
104
94
|
if (responseRedirectURI) {
|
|
105
95
|
response.setHeader('Content-Type', 'application/json')
|
|
106
96
|
return response.send(JSON.stringify({ redirect_uri: responseRedirectURI }))
|
|
@@ -135,7 +125,7 @@ export function getAuthRequestSIOPv2Endpoint(router: Router, context: IRequiredC
|
|
|
135
125
|
}
|
|
136
126
|
const requestState = await context.agent.siopGetAuthRequestState({
|
|
137
127
|
correlationId,
|
|
138
|
-
|
|
128
|
+
definitionId,
|
|
139
129
|
errorOnNotFound: false,
|
|
140
130
|
})
|
|
141
131
|
if (!requestState) {
|
|
@@ -159,8 +149,8 @@ export function getAuthRequestSIOPv2Endpoint(router: Router, context: IRequiredC
|
|
|
159
149
|
} finally {
|
|
160
150
|
await context.agent.siopUpdateAuthRequestState({
|
|
161
151
|
correlationId,
|
|
162
|
-
|
|
163
|
-
state: '
|
|
152
|
+
definitionId,
|
|
153
|
+
state: 'sent',
|
|
164
154
|
error,
|
|
165
155
|
})
|
|
166
156
|
}
|
|
@@ -7,11 +7,11 @@ import express, { Express, Request, Response, Router } from 'express'
|
|
|
7
7
|
import { getAuthRequestSIOPv2Endpoint, verifyAuthResponseSIOPv2Endpoint } from './siop-api-functions'
|
|
8
8
|
import { IRequiredPlugins, ISIOPv2RPRestAPIOpts } from './types'
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
authStatusWebappEndpoint,
|
|
11
|
+
createAuthRequestWebappEndpoint,
|
|
12
12
|
getDefinitionsEndpoint,
|
|
13
|
-
|
|
14
|
-
} from './
|
|
13
|
+
removeAuthRequestStateWebappEndpoint,
|
|
14
|
+
} from './webapp-api-functions'
|
|
15
15
|
import swaggerUi from 'swagger-ui-express'
|
|
16
16
|
|
|
17
17
|
export class SIOPv2RPApiServer {
|
|
@@ -40,9 +40,9 @@ export class SIOPv2RPApiServer {
|
|
|
40
40
|
|
|
41
41
|
// Webapp endpoints
|
|
42
42
|
if (features.includes('rp-status')) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
createAuthRequestWebappEndpoint(this._router, context, opts?.endpointOpts?.webappCreateAuthRequest)
|
|
44
|
+
authStatusWebappEndpoint(this._router, context, opts?.endpointOpts?.webappAuthStatus)
|
|
45
|
+
removeAuthRequestStateWebappEndpoint(this._router, context, opts?.endpointOpts?.webappDeleteAuthRequest)
|
|
46
46
|
getDefinitionsEndpoint(this._router, context, opts?.endpointOpts?.webappGetDefinitions)
|
|
47
47
|
}
|
|
48
48
|
|
package/src/types/types.ts
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import { IAgentContext, ICredentialVerifier } from '@veramo/core'
|
|
2
1
|
import { GenericAuthArgs, ISingleEndpointOpts } from '@sphereon/ssi-express-support'
|
|
3
2
|
import { IPresentationExchange } from '@sphereon/ssi-sdk.presentation-exchange'
|
|
4
3
|
import { ISIOPv2RP } from '@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth'
|
|
4
|
+
import { IAgentContext, ICredentialVerifier } from '@veramo/core'
|
|
5
5
|
import { IPDManager } from '@sphereon/ssi-sdk.pd-manager'
|
|
6
|
-
import { AdditionalClaims } from '@sphereon/ssi-types'
|
|
7
|
-
import { AuthorizationRequestStateStatus, AuthorizationResponseStateStatus } from '@sphereon/ssi-sdk.siopv2-oid4vp-common'
|
|
8
|
-
import { Request, Response } from 'express'
|
|
9
|
-
import { z } from 'zod'
|
|
10
|
-
import { CreateAuthorizationRequestBodySchema, CreateAuthorizationResponseSchema } from '../schemas'
|
|
11
6
|
import { QRCodeOpts } from './QRCode.types'
|
|
12
7
|
|
|
13
8
|
export type SiopFeatures = 'rp-status' | 'siop'
|
|
14
|
-
|
|
15
9
|
export interface ISIOPv2RPRestAPIOpts {
|
|
16
10
|
enableFeatures?: SiopFeatures[]
|
|
17
11
|
endpointOpts?: {
|
|
@@ -36,38 +30,3 @@ export interface ICreateAuthRequestWebappEndpointOpts extends ISingleEndpointOpt
|
|
|
36
30
|
|
|
37
31
|
export type IRequiredPlugins = ICredentialVerifier & ISIOPv2RP & IPresentationExchange & IPDManager
|
|
38
32
|
export type IRequiredContext = IAgentContext<IRequiredPlugins>
|
|
39
|
-
|
|
40
|
-
export type CreateAuthorizationRequest = Request<Record<string, never>, any, CreateAuthorizationRequestBody, Record<string, never>>
|
|
41
|
-
|
|
42
|
-
export type CreateAuthorizationRequestBody = z.infer<typeof CreateAuthorizationRequestBodySchema>;
|
|
43
|
-
|
|
44
|
-
export type CreateAuthorizationResponse = Response<CreateAuthorizationRequestResponse>
|
|
45
|
-
|
|
46
|
-
export type CreateAuthorizationRequestResponse = z.infer<typeof CreateAuthorizationResponseSchema>;
|
|
47
|
-
|
|
48
|
-
export type DeleteAuthorizationRequest = Request<DeleteAuthorizationRequestPathParameters, any, Record<string, any>, Record<string, any>>
|
|
49
|
-
|
|
50
|
-
export type DeleteAuthorizationRequestPathParameters = {
|
|
51
|
-
correlationId: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type GetAuthorizationRequestStatus = Request<GetAuthorizationRequestStatusPathParameters, any, Record<string, any>, Record<string, any>>
|
|
55
|
-
|
|
56
|
-
export type GetAuthorizationRequestStatusPathParameters = {
|
|
57
|
-
correlationId: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export type RequestError = {
|
|
61
|
-
status: number
|
|
62
|
-
message: string
|
|
63
|
-
error_details?: string
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface AuthStatusResponse {
|
|
67
|
-
status: AuthorizationRequestStateStatus | AuthorizationResponseStateStatus
|
|
68
|
-
correlation_id: string
|
|
69
|
-
query_id: string
|
|
70
|
-
last_updated: number
|
|
71
|
-
verified_data?: AdditionalClaims
|
|
72
|
-
error?: RequestError
|
|
73
|
-
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { AuthorizationRequestState, AuthorizationResponseStateStatus } from '@sphereon/did-auth-siop'
|
|
2
|
+
import { checkAuth, ISingleEndpointOpts, sendErrorResponse } from '@sphereon/ssi-express-support'
|
|
3
|
+
import { AuthStatusResponse, GenerateAuthRequestURIResponse, uriWithBase } from '@sphereon/ssi-sdk.siopv2-oid4vp-common'
|
|
4
|
+
import { AuthorizationResponseStateWithVerifiedData, VerifiedDataMode } from '@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth'
|
|
5
|
+
import { Request, Response, Router } from 'express'
|
|
6
|
+
import uuid from 'short-uuid'
|
|
7
|
+
import { ICreateAuthRequestWebappEndpointOpts, IRequiredContext } from './types'
|
|
8
|
+
import { shaHasher as defaultHasher } from '@sphereon/ssi-sdk.core'
|
|
9
|
+
|
|
10
|
+
export function createAuthRequestWebappEndpoint(router: Router, context: IRequiredContext, opts?: ICreateAuthRequestWebappEndpointOpts) {
|
|
11
|
+
if (opts?.enabled === false) {
|
|
12
|
+
console.log(`createAuthRequest Webapp endpoint is disabled`)
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
const path = opts?.path ?? '/webapp/definitions/:definitionId/auth-requests'
|
|
16
|
+
router.post(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => {
|
|
17
|
+
try {
|
|
18
|
+
// if (!request.agent) throw Error('No agent configured')
|
|
19
|
+
const definitionId = request.params.definitionId
|
|
20
|
+
if (!definitionId) {
|
|
21
|
+
return sendErrorResponse(response, 400, 'No definitionId query parameter provided')
|
|
22
|
+
}
|
|
23
|
+
const state: string = request.body.state ?? uuid.uuid()
|
|
24
|
+
const correlationId = request.body.correlationId ?? state
|
|
25
|
+
const qrCodeOpts = request.body.qrCodeOpts ?? opts?.qrCodeOpts
|
|
26
|
+
|
|
27
|
+
const requestByReferenceURI = uriWithBase(`/siop/definitions/${definitionId}/auth-requests/${state}`, {
|
|
28
|
+
baseURI: opts?.siopBaseURI,
|
|
29
|
+
})
|
|
30
|
+
const responseURI = uriWithBase(`/siop/definitions/${definitionId}/auth-responses/${state}`, { baseURI: opts?.siopBaseURI })
|
|
31
|
+
// first version is for backwards compat
|
|
32
|
+
const responseRedirectURI =
|
|
33
|
+
('response_redirect_uri' in request.body && (request.body.response_redirect_uri as string | undefined)) ??
|
|
34
|
+
('responseRedirectURI' in request.body && (request.body.responseRedirectURI as string | undefined))
|
|
35
|
+
|
|
36
|
+
const authRequestURI = await context.agent.siopCreateAuthRequestURI({
|
|
37
|
+
definitionId,
|
|
38
|
+
correlationId,
|
|
39
|
+
state,
|
|
40
|
+
nonce: uuid.uuid(),
|
|
41
|
+
requestByReferenceURI,
|
|
42
|
+
responseURIType: 'response_uri',
|
|
43
|
+
responseURI,
|
|
44
|
+
...(responseRedirectURI && { responseRedirectURI }),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
let qrCodeDataUri: string | undefined
|
|
48
|
+
if (qrCodeOpts) {
|
|
49
|
+
const { AwesomeQR } = await import('awesome-qr')
|
|
50
|
+
const qrCode = new AwesomeQR({ ...qrCodeOpts, text: authRequestURI })
|
|
51
|
+
qrCodeDataUri = `data:image/png;base64,${(await qrCode.draw())!.toString('base64')}`
|
|
52
|
+
}
|
|
53
|
+
const authRequestBody: GenerateAuthRequestURIResponse = {
|
|
54
|
+
correlationId,
|
|
55
|
+
state,
|
|
56
|
+
definitionId,
|
|
57
|
+
authRequestURI,
|
|
58
|
+
authStatusURI: `${uriWithBase(opts?.webappAuthStatusPath ?? '/webapp/auth-status', { baseURI: opts?.webappBaseURI })}`,
|
|
59
|
+
...(qrCodeDataUri && { qrCodeDataUri }),
|
|
60
|
+
}
|
|
61
|
+
console.log(`Auth Request URI data to send back: ${JSON.stringify(authRequestBody)}`)
|
|
62
|
+
return response.json(authRequestBody)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return sendErrorResponse(response, 500, 'Could not create an authorization request URI', error)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function authStatusWebappEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) {
|
|
70
|
+
if (opts?.enabled === false) {
|
|
71
|
+
console.log(`authStatus Webapp endpoint is disabled`)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
const path = opts?.path ?? '/webapp/auth-status'
|
|
75
|
+
router.post(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => {
|
|
76
|
+
try {
|
|
77
|
+
console.log('Received auth-status request...')
|
|
78
|
+
const correlationId: string = request.body.correlationId as string
|
|
79
|
+
const definitionId: string = request.body.definitionId as string
|
|
80
|
+
|
|
81
|
+
const requestState =
|
|
82
|
+
correlationId && definitionId
|
|
83
|
+
? await context.agent.siopGetAuthRequestState({
|
|
84
|
+
correlationId,
|
|
85
|
+
definitionId,
|
|
86
|
+
errorOnNotFound: false,
|
|
87
|
+
})
|
|
88
|
+
: undefined
|
|
89
|
+
if (!requestState || !definitionId || !correlationId) {
|
|
90
|
+
console.log(
|
|
91
|
+
`No authentication request mapping could be found for the given URL. correlation: ${correlationId}, definitionId: ${definitionId}`,
|
|
92
|
+
)
|
|
93
|
+
response.statusCode = 404
|
|
94
|
+
const statusBody: AuthStatusResponse = {
|
|
95
|
+
status: requestState ? requestState.status : 'error',
|
|
96
|
+
error: 'No authentication request mapping could be found for the given URL.',
|
|
97
|
+
correlationId,
|
|
98
|
+
definitionId,
|
|
99
|
+
lastUpdated: requestState ? requestState.lastUpdated : Date.now(),
|
|
100
|
+
}
|
|
101
|
+
return response.json(statusBody)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let includeVerifiedData: VerifiedDataMode = VerifiedDataMode.NONE
|
|
105
|
+
if ('includeVerifiedData' in request.body) {
|
|
106
|
+
includeVerifiedData = request.body.includeVerifiedData as VerifiedDataMode
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let responseState
|
|
110
|
+
if (requestState.status === 'sent') {
|
|
111
|
+
responseState = (await context.agent.siopGetAuthResponseState({
|
|
112
|
+
correlationId,
|
|
113
|
+
definitionId,
|
|
114
|
+
includeVerifiedData: includeVerifiedData,
|
|
115
|
+
errorOnNotFound: false,
|
|
116
|
+
})) as AuthorizationResponseStateWithVerifiedData
|
|
117
|
+
}
|
|
118
|
+
const overallState: AuthorizationRequestState | AuthorizationResponseStateWithVerifiedData = responseState ?? requestState
|
|
119
|
+
|
|
120
|
+
const statusBody: AuthStatusResponse = {
|
|
121
|
+
status: overallState.status,
|
|
122
|
+
...(overallState.error ? { error: overallState.error?.message } : {}),
|
|
123
|
+
correlationId,
|
|
124
|
+
definitionId,
|
|
125
|
+
lastUpdated: overallState.lastUpdated,
|
|
126
|
+
...(responseState && responseState.status === AuthorizationResponseStateStatus.VERIFIED
|
|
127
|
+
? {
|
|
128
|
+
payload: await responseState.response.mergedPayloads({ hasher: defaultHasher }),
|
|
129
|
+
verifiedData: responseState.verifiedData,
|
|
130
|
+
}
|
|
131
|
+
: {}),
|
|
132
|
+
}
|
|
133
|
+
console.debug(`Will send auth status: ${JSON.stringify(statusBody)}`)
|
|
134
|
+
if (overallState.status === 'error') {
|
|
135
|
+
response.statusCode = 500
|
|
136
|
+
return response.json(statusBody)
|
|
137
|
+
}
|
|
138
|
+
response.statusCode = 200
|
|
139
|
+
return response.json(statusBody)
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return sendErrorResponse(response, 500, error.message, error)
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function removeAuthRequestStateWebappEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) {
|
|
147
|
+
if (opts?.enabled === false) {
|
|
148
|
+
console.log(`removeAuthStatus Webapp endpoint is disabled`)
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
const path = opts?.path ?? '/webapp/definitions/:definitionId/auth-requests/:correlationId'
|
|
152
|
+
router.delete(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => {
|
|
153
|
+
try {
|
|
154
|
+
const correlationId: string = request.params.correlationId
|
|
155
|
+
const definitionId: string = request.params.definitionId
|
|
156
|
+
if (!correlationId || !definitionId) {
|
|
157
|
+
console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`)
|
|
158
|
+
return sendErrorResponse(response, 404, 'No authorization request could be found')
|
|
159
|
+
}
|
|
160
|
+
response.statusCode = 200
|
|
161
|
+
return response.json(await context.agent.siopDeleteAuthState({ definitionId, correlationId }))
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return sendErrorResponse(response, 500, error.message, error)
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function getDefinitionsEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) {
|
|
169
|
+
if (opts?.enabled === false) {
|
|
170
|
+
console.log(`getDefinitions Webapp endpoint is disabled`)
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
const path = opts?.path ?? '/webapp/definitions'
|
|
174
|
+
router.get(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => {
|
|
175
|
+
try {
|
|
176
|
+
const definitions = await context.agent.pdmGetDefinitions()
|
|
177
|
+
response.statusCode = 200
|
|
178
|
+
return response.json(definitions)
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return sendErrorResponse(response, 500, error.message, error)
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { z, ZodError } from 'zod';
|
|
3
|
-
|
|
4
|
-
export const validateData = (schema: z.ZodObject<any, any>) => {
|
|
5
|
-
return (req: Request, res: Response, next: NextFunction) => {
|
|
6
|
-
try {
|
|
7
|
-
schema.parse(req.body);
|
|
8
|
-
next();
|
|
9
|
-
} catch (error) {
|
|
10
|
-
if (error instanceof ZodError) {
|
|
11
|
-
const errorMessages = error.issues.map((issue: any) => ({
|
|
12
|
-
message: `${issue.path.join('.')} is ${issue.message}`,
|
|
13
|
-
}))
|
|
14
|
-
res.status(400).json({ status: 400, message: 'Invalid data', error_details: errorMessages[0].message });
|
|
15
|
-
} else {
|
|
16
|
-
res.status(500).json({ status: 500, message: 'Internal Server Error' });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
}
|
package/src/schemas/index.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import {
|
|
3
|
-
ResponseMode,
|
|
4
|
-
ResponseType,
|
|
5
|
-
RequestUriMethod,
|
|
6
|
-
CallbackOptsSchema
|
|
7
|
-
} from '@sphereon/did-auth-siop'
|
|
8
|
-
|
|
9
|
-
export const ResponseTypeSchema = z.enum([ResponseType.VP_TOKEN]);
|
|
10
|
-
|
|
11
|
-
export const ResponseModeSchema = z.enum([ResponseMode.DIRECT_POST, ResponseMode.DIRECT_POST_JWT]);
|
|
12
|
-
|
|
13
|
-
export const RequestUriMethodSchema = z.enum(Object.values(RequestUriMethod));
|
|
14
|
-
|
|
15
|
-
export const QRCodeOptsSchema = z.object({
|
|
16
|
-
size: z.number().optional(),
|
|
17
|
-
color_dark: z.string().optional(),
|
|
18
|
-
color_light: z.string().optional(),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export const CreateAuthorizationRequestBodySchema = z.object({
|
|
22
|
-
query_id: z.string(),
|
|
23
|
-
client_id: z.string().optional(),
|
|
24
|
-
request_uri_base: z.string().optional(),
|
|
25
|
-
correlation_id: z.string().optional(),
|
|
26
|
-
request_uri_method: RequestUriMethodSchema.optional(),
|
|
27
|
-
response_type: ResponseTypeSchema.optional(),
|
|
28
|
-
response_mode: ResponseModeSchema.optional(),
|
|
29
|
-
transaction_data: z.array(z.string()).optional(),
|
|
30
|
-
qr_code: QRCodeOptsSchema.optional(),
|
|
31
|
-
direct_post_response_redirect_uri: z.string().optional(),
|
|
32
|
-
callback: CallbackOptsSchema.optional(),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export const CreateAuthorizationResponseSchema = z.object({
|
|
36
|
-
correlation_id: z.string(),
|
|
37
|
-
query_id: z.string(),
|
|
38
|
-
request_uri: z.string(),
|
|
39
|
-
status_uri: z.string(),
|
|
40
|
-
qr_uri: z.string().optional(),
|
|
41
|
-
});
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { AuthorizationResponseStateStatus } from '@sphereon/did-auth-siop'
|
|
2
|
-
import { checkAuth, ISingleEndpointOpts, sendErrorResponse } from '@sphereon/ssi-express-support'
|
|
3
|
-
import { uriWithBase } from '@sphereon/ssi-sdk.siopv2-oid4vp-common'
|
|
4
|
-
import { Request, Response, Router } from 'express'
|
|
5
|
-
import uuid from 'short-uuid'
|
|
6
|
-
import { validateData } from './middleware/validationMiddleware'
|
|
7
|
-
import { CreateAuthorizationRequestBodySchema } from './schemas'
|
|
8
|
-
import {
|
|
9
|
-
CreateAuthorizationRequest,
|
|
10
|
-
CreateAuthorizationRequestResponse,
|
|
11
|
-
CreateAuthorizationResponse,
|
|
12
|
-
DeleteAuthorizationRequest,
|
|
13
|
-
GetAuthorizationRequestStatus,
|
|
14
|
-
AuthStatusResponse,
|
|
15
|
-
ICreateAuthRequestWebappEndpointOpts,
|
|
16
|
-
IRequiredContext
|
|
17
|
-
} from './types'
|
|
18
|
-
|
|
19
|
-
export function createAuthRequestUniversalOID4VPEndpoint(router: Router, context: IRequiredContext, opts?: ICreateAuthRequestWebappEndpointOpts) {
|
|
20
|
-
if (opts?.enabled === false) {
|
|
21
|
-
console.log(`createAuthRequest universal OID4VP endpoint is disabled`)
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const path = opts?.path ?? '/backend/auth/requests'
|
|
26
|
-
router.post(path, checkAuth(opts?.endpoint), validateData(CreateAuthorizationRequestBodySchema), async (request: CreateAuthorizationRequest, response: CreateAuthorizationResponse) => {
|
|
27
|
-
try {
|
|
28
|
-
const correlationId = request.body.correlation_id ?? uuid.uuid()
|
|
29
|
-
const qrCodeOpts = request.body.qr_code ?? opts?.qrCodeOpts
|
|
30
|
-
const queryId = request.body.query_id
|
|
31
|
-
const directPostResponseRedirectUri = request.body.direct_post_response_redirect_uri // TODO Uri not URI
|
|
32
|
-
const requestUriBase = request.body.request_uri_base
|
|
33
|
-
const callback = request.body.callback
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
await context.agent.pdmGetDefinition({ itemId: queryId })
|
|
37
|
-
} catch(e) {
|
|
38
|
-
console.log(`No query could be found for the given id. Query id: ${queryId}`)
|
|
39
|
-
return sendErrorResponse(response, 404, { status: 404, message: 'No query could be found' })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const requestByReferenceURI = uriWithBase(`/siop/definitions/${queryId}/auth-requests/${correlationId}`, {
|
|
43
|
-
baseURI: requestUriBase ?? opts?.siopBaseURI,
|
|
44
|
-
})
|
|
45
|
-
const responseURI = uriWithBase(`/siop/definitions/${queryId}/auth-responses/${correlationId}`, { baseURI: opts?.siopBaseURI })
|
|
46
|
-
|
|
47
|
-
const authRequestURI = await context.agent.siopCreateAuthRequestURI({
|
|
48
|
-
queryId,
|
|
49
|
-
correlationId,
|
|
50
|
-
nonce: uuid.uuid(),
|
|
51
|
-
requestByReferenceURI,
|
|
52
|
-
responseURIType: 'response_uri',
|
|
53
|
-
responseURI,
|
|
54
|
-
...(directPostResponseRedirectUri && { responseRedirectURI: directPostResponseRedirectUri }),
|
|
55
|
-
callback
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
let qrCodeDataUri: string | undefined
|
|
59
|
-
if (qrCodeOpts) {
|
|
60
|
-
const { AwesomeQR } = await import('awesome-qr')
|
|
61
|
-
const qrCode = new AwesomeQR({ ...qrCodeOpts, text: authRequestURI })
|
|
62
|
-
qrCodeDataUri = `data:image/png;base64,${(await qrCode.draw())!.toString('base64')}`
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const authRequestBody = {
|
|
66
|
-
query_id: queryId,
|
|
67
|
-
correlation_id: correlationId,
|
|
68
|
-
request_uri: authRequestURI,
|
|
69
|
-
status_uri: `${uriWithBase(opts?.webappAuthStatusPath ?? `/backend/auth/status/${correlationId}`, { baseURI: opts?.webappBaseURI })}`,
|
|
70
|
-
...(qrCodeDataUri && { qr_uri: qrCodeDataUri }),
|
|
71
|
-
} satisfies CreateAuthorizationRequestResponse
|
|
72
|
-
console.log(`Auth Request URI data to send back: ${JSON.stringify(authRequestBody)}`)
|
|
73
|
-
|
|
74
|
-
return response.status(201).json(authRequestBody)
|
|
75
|
-
} catch (error) {
|
|
76
|
-
return sendErrorResponse(response, 500, { status: 500, message: 'Could not create an authorization request URI' }, error)
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function removeAuthRequestStateUniversalOID4VPEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) {
|
|
82
|
-
if (opts?.enabled === false) {
|
|
83
|
-
console.log(`removeAuthStatus universal OID4VP endpoint is disabled`)
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const path = opts?.path ?? '/backend/auth/requests/:correlationId'
|
|
88
|
-
router.delete(path, checkAuth(opts?.endpoint), async (request: DeleteAuthorizationRequest, response: Response) => {
|
|
89
|
-
try {
|
|
90
|
-
const correlationId: string = request.params.correlationId
|
|
91
|
-
|
|
92
|
-
const authRequestState = await context.agent.siopGetAuthRequestState({
|
|
93
|
-
correlationId,
|
|
94
|
-
errorOnNotFound: false
|
|
95
|
-
})
|
|
96
|
-
if (!authRequestState) {
|
|
97
|
-
console.log(`No authorization request could be found for the given correlationId. correlationId: ${correlationId}`)
|
|
98
|
-
return sendErrorResponse(response, 404, { status: 404, message: 'No authorization request could be found' })
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
await context.agent.siopDeleteAuthState({ correlationId })
|
|
102
|
-
|
|
103
|
-
return response.status(204).json()
|
|
104
|
-
} catch (error) {
|
|
105
|
-
return sendErrorResponse(response, 500, { status: 500, message: error.message }, error)
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function authStatusUniversalOID4VPEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) {
|
|
111
|
-
if (opts?.enabled === false) {
|
|
112
|
-
console.log(`authStatus universal OID4VP endpoint is disabled`)
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const path = opts?.path ?? '/backend/auth/status/:correlationId'
|
|
117
|
-
router.get(path, checkAuth(opts?.endpoint), async (request: GetAuthorizationRequestStatus, response: Response) => {
|
|
118
|
-
try {
|
|
119
|
-
console.log('Received auth-status request...')
|
|
120
|
-
const correlationId: string = request.params.correlationId
|
|
121
|
-
|
|
122
|
-
const requestState = await context.agent.siopGetAuthRequestState({
|
|
123
|
-
correlationId,
|
|
124
|
-
errorOnNotFound: false
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
if (!requestState) {
|
|
128
|
-
console.log(`No authorization request could be found for the given correlationId. correlationId: ${correlationId}`)
|
|
129
|
-
return sendErrorResponse(response, 404, { status: 404, message: 'No authorization request could be found' })
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
let responseState
|
|
133
|
-
if (requestState.status === 'authorization_request_created') {
|
|
134
|
-
responseState = (await context.agent.siopGetAuthResponseState({ correlationId, errorOnNotFound: false }))
|
|
135
|
-
}
|
|
136
|
-
const overallState = responseState ?? requestState
|
|
137
|
-
|
|
138
|
-
const statusBody = {
|
|
139
|
-
status: overallState.status,
|
|
140
|
-
correlation_id: overallState.correlationId,
|
|
141
|
-
query_id: overallState.queryId,
|
|
142
|
-
last_updated: overallState.lastUpdated,
|
|
143
|
-
...((responseState?.status === AuthorizationResponseStateStatus.VERIFIED && responseState.verifiedData !== undefined) && { verifiedData: responseState.verifiedData }),
|
|
144
|
-
...(overallState.error && { message: overallState.error.message })
|
|
145
|
-
} satisfies AuthStatusResponse
|
|
146
|
-
console.debug(`Will send auth status: ${JSON.stringify(statusBody)}`)
|
|
147
|
-
|
|
148
|
-
if (overallState.status === 'error') {
|
|
149
|
-
return response.status(500).json(statusBody)
|
|
150
|
-
}
|
|
151
|
-
return response.status(200).json(statusBody)
|
|
152
|
-
} catch (error) {
|
|
153
|
-
return sendErrorResponse(response, 500, { status: 500, message: error.message }, error)
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export function getDefinitionsEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) {
|
|
159
|
-
if (opts?.enabled === false) {
|
|
160
|
-
console.log(`getDefinitions universal OID4VP endpoint is disabled`)
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const path = opts?.path ?? '/backend/definitions'
|
|
165
|
-
router.get(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => {
|
|
166
|
-
try {
|
|
167
|
-
const definitions = await context.agent.pdmGetDefinitions()
|
|
168
|
-
response.statusCode = 200
|
|
169
|
-
return response.json(definitions)
|
|
170
|
-
} catch (error) {
|
|
171
|
-
return sendErrorResponse(response, 500, { status: 500, message: error.message }, error)
|
|
172
|
-
}
|
|
173
|
-
})
|
|
174
|
-
}
|