diff --git a/packages/siopv2-oid4vp-rp-rest-api/__tests__/RestAPI.ts b/packages/siopv2-oid4vp-rp-rest-api/__tests__/RestAPI.ts index 4d08dfd08..9b23f919c 100644 --- a/packages/siopv2-oid4vp-rp-rest-api/__tests__/RestAPI.ts +++ b/packages/siopv2-oid4vp-rp-rest-api/__tests__/RestAPI.ts @@ -4,6 +4,7 @@ import agent from './agent' const opts: ISIOPv2RPRestAPIOpts = { endpointOpts: { + basePath: '/oid4vp', globalAuth: { authentication: { enabled: true, @@ -11,8 +12,8 @@ const opts: ISIOPv2RPRestAPIOpts = { }, }, webappCreateAuthRequest: { - webappBaseURI: 'http://192.168.2.18:5000', - siopBaseURI: 'http://192.168.2.18:5000', + webappBaseURI: 'http://localhost:5000/oid4vp', + siopBaseURI: 'http://localhost:5000', }, }, } diff --git a/packages/siopv2-oid4vp-rp-rest-api/__tests__/agent.ts b/packages/siopv2-oid4vp-rp-rest-api/__tests__/agent.ts index 2ed502135..c21bc158d 100644 --- a/packages/siopv2-oid4vp-rp-rest-api/__tests__/agent.ts +++ b/packages/siopv2-oid4vp-rp-rest-api/__tests__/agent.ts @@ -6,7 +6,6 @@ import { ICredentialHandlerLDLocal, LdDefaultContexts, MethodNames, - SphereonBbsBlsSignature2020, SphereonEd25519Signature2018, SphereonEd25519Signature2020, SphereonJsonWebSignature2020, @@ -24,11 +23,11 @@ import { Resolver } from 'did-resolver' import { DB_CONNECTION_NAME, DB_ENCRYPTION_KEY, getDbConnection } from './database' import { ISIOPv2RP, SIOPv2RP } from '@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth' import { IPresentationExchange, PresentationExchange } from '@sphereon/ssi-sdk.presentation-exchange' -import { CheckLinkedDomain } from '@sphereon/did-auth-siop' import { entraAndSphereonCompatibleDef, entraVerifiedIdPresentation } from './presentationDefinitions' import Debug from 'debug' import { createHash } from 'crypto' import { SchemaValidation } from '@sphereon/ssi-sdk.credential-validation' +import {CheckLinkedDomain} from "@sphereon/did-auth-siop-adapter"; const debug = Debug('ssi-sdk-siopv2-oid4vp-rp-rest-api') @@ -157,7 +156,6 @@ const agent = createAgent< suites: [ new SphereonEd25519Signature2018(), new SphereonEd25519Signature2020(), - new SphereonBbsBlsSignature2020(), new SphereonJsonWebSignature2020(), ], bindingOverrides: new Map([ diff --git a/packages/siopv2-oid4vp-rp-rest-api/package.json b/packages/siopv2-oid4vp-rp-rest-api/package.json index a00a4fdcc..91ccdd235 100644 --- a/packages/siopv2-oid4vp-rp-rest-api/package.json +++ b/packages/siopv2-oid4vp-rp-rest-api/package.json @@ -30,6 +30,7 @@ "dotenv-flow": "^3.3.0", "express": "^4.19.2", "short-uuid": "^4.2.2", + "swagger-ui-express": "^5.0.1", "uuid": "^9.0.1" }, "devDependencies": { @@ -37,6 +38,7 @@ "@sphereon/did-uni-client": "^0.6.3", "@sphereon/pex": "5.0.0-unstable.28", "@sphereon/pex-models": "^2.3.2", + "@sphereon/did-auth-siop-adapter": "0.16.1-feature.MWALL.715.391", "@sphereon/ssi-sdk-ext.did-provider-jwk": "0.27.1-next.14", "@sphereon/ssi-sdk.data-store": "workspace:*", "@sphereon/ssi-sdk.vc-handler-ld-local": "workspace:*", @@ -51,6 +53,7 @@ "@types/node": "^20.17.1", "@types/passport": "^1.0.16", "@types/passport-http-bearer": "^1.0.41", + "@types/swagger-ui-express": "^4.1.7", "@types/uuid": "^9.0.8", "@veramo/data-store": "4.2.0", "@veramo/did-manager": "4.2.0", @@ -64,6 +67,7 @@ "@veramo/utils": "4.2.0", "did-resolver": "^4.1.0", "morgan": "^1.10.0", + "debug": "^4.4.0", "nock": "^13.5.4", "passport": "^0.6.0", "passport-http-bearer": "^1.0.1", diff --git a/packages/siopv2-oid4vp-rp-rest-api/src/siopv2-rp-api-server.ts b/packages/siopv2-oid4vp-rp-rest-api/src/siopv2-rp-api-server.ts index 9dcb990fb..e449c964c 100644 --- a/packages/siopv2-oid4vp-rp-rest-api/src/siopv2-rp-api-server.ts +++ b/packages/siopv2-oid4vp-rp-rest-api/src/siopv2-rp-api-server.ts @@ -3,7 +3,7 @@ import { copyGlobalAuthToEndpoints, ExpressSupport } from '@sphereon/ssi-express import { IPresentationExchange } from '@sphereon/ssi-sdk.presentation-exchange' import { ISIOPv2RP } from '@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth' import { TAgent } from '@veramo/core' -import express, { Express, Router } from 'express' +import express, {Express, Request, Response, Router} from 'express' import { getAuthRequestSIOPv2Endpoint, verifyAuthResponseSIOPv2Endpoint } from './siop-api-functions' import { IRequiredPlugins, ISIOPv2RPRestAPIOpts } from './types' import { @@ -12,13 +12,16 @@ import { getDefinitionsEndpoint, removeAuthRequestStateWebappEndpoint, } from './webapp-api-functions' +import swaggerUi from 'swagger-ui-express' export class SIOPv2RPApiServer { private readonly _express: Express private readonly _router: Router private readonly _agent: TAgent private readonly _opts?: ISIOPv2RPRestAPIOpts + private readonly _basePath: string + private readonly OID4VP_SWAGGER_URL = 'https://api.swaggerhub.com/apis/SphereonInt/OID4VP/0.1.0' constructor(args: { agent: TAgent; expressSupport: ExpressSupport; opts?: ISIOPv2RPRestAPIOpts }) { const { agent, opts } = args this._agent = agent @@ -48,9 +51,39 @@ export class SIOPv2RPApiServer { getAuthRequestSIOPv2Endpoint(this._router, context, opts?.endpointOpts?.siopGetAuthRequest) verifyAuthResponseSIOPv2Endpoint(this._router, context, opts?.endpointOpts?.siopVerifyAuthResponse) } - this._express.use(opts?.endpointOpts?.basePath ?? '', this.router) + this._basePath = opts?.endpointOpts?.basePath ?? '' + this._express.use(this._basePath, this.router) + this.setupSwaggerUi() } + private setupSwaggerUi() { + + fetch(this.OID4VP_SWAGGER_URL) + .then((res) => res.json()) + .then((swagger) => { + const apiDocs = `${this._basePath}/api-docs` + console.log(`[OID4P] API docs available at ${apiDocs}`) + + this._router.use( + '/api-docs', + (req: Request, res: Response, next: any) => { + const regex = `${apiDocs.replace(/\//, '\/')}`.replace('/oid4vp', '').replace(/\/api-docs.*/, '') + swagger.servers = [{url: `${req.protocol}://${req.get('host')}${regex}`, description: 'This server'}] + // @ts-ignore + req.swaggerDoc = swagger + next() + }, + swaggerUi.serveFiles(swagger, options), + swaggerUi.setup(), + ) + }) + .catch((err) => { + console.log(`[OID4VP] Unable to fetch swagger document: ${err}. Will not host api-docs on this instance`) + }) + const options = { + // customCss: '.swagger-ui .topbar { display: none }', + } + } get express(): Express { return this._express } diff --git a/packages/ssi-express-support/src/auth-utils.ts b/packages/ssi-express-support/src/auth-utils.ts index 09ba52f72..af1894e5d 100644 --- a/packages/ssi-express-support/src/auth-utils.ts +++ b/packages/ssi-express-support/src/auth-utils.ts @@ -139,6 +139,10 @@ export function copyGlobalAuthToEndpoint(args?: { opts?: HasEndpointOpts; key: s if (!opts || !key || !hasEndpointOpts(opts)) { return } + if (key === 'basePath') { + // make sure to not copy base path over, as we use these at the global router, and this would repeat the path + return; + } if (opts.endpointOpts?.globalAuth) { if (opts.endpointOpts[key]?.disableGlobalAuth === true) { return diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee0da9d02..a52e2f9f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2588,6 +2588,9 @@ importers: short-uuid: specifier: ^4.2.2 version: 4.2.2 + swagger-ui-express: + specifier: ^5.0.1 + version: 5.0.1(express@4.21.2) uuid: specifier: ^9.0.1 version: 9.0.1 @@ -2595,6 +2598,9 @@ importers: '@decentralized-identity/ion-sdk': specifier: ^0.6.0 version: 0.6.0 + '@sphereon/did-auth-siop-adapter': + specifier: 0.16.1-feature.MWALL.715.391 + version: 0.16.1-feature.MWALL.715.391(encoding@0.1.13)(typescript@5.6.3) '@sphereon/did-uni-client': specifier: ^0.6.3 version: 0.6.3(encoding@0.1.13) @@ -2646,6 +2652,9 @@ importers: '@types/passport-http-bearer': specifier: ^1.0.41 version: 1.0.41 + '@types/swagger-ui-express': + specifier: ^4.1.7 + version: 4.1.7 '@types/uuid': specifier: ^9.0.8 version: 9.0.8 @@ -2679,6 +2688,9 @@ importers: '@veramo/utils': specifier: 4.2.0 version: 4.2.0(encoding@0.1.13) + debug: + specifier: ^4.3.5 + version: 4.4.0 did-resolver: specifier: ^4.1.0 version: 4.1.0