diff --git a/packages/data-store/src/contact/ContactStore.ts b/packages/data-store/src/contact/ContactStore.ts index 56a2e7ad2..853c3efdd 100644 --- a/packages/data-store/src/contact/ContactStore.ts +++ b/packages/data-store/src/contact/ContactStore.ts @@ -124,7 +124,6 @@ export class ContactStore extends AbstractContactStore { removeContact = async ({ contactId }: IRemoveContactArgs): Promise => { debug('Removing contact', contactId) - ;(await this.dbConnection) .getRepository(ContactEntity) .findOneById(contactId) diff --git a/packages/presentation-exchange/package.json b/packages/presentation-exchange/package.json index d2a5bf7f2..001d5ff31 100644 --- a/packages/presentation-exchange/package.json +++ b/packages/presentation-exchange/package.json @@ -14,7 +14,7 @@ "build:clean": "tsc --build --clean && tsc --build" }, "dependencies": { - "@sphereon/pex": "2.0.0-unstable.13", + "@sphereon/pex": "2.0.0-unstable.14", "@sphereon/pex-models": "^1.2.2", "@sphereon/ssi-types": "^0.9.0", "@sphereon/ssi-sdk-ext.did-utils": "^0.10.2-next.20", diff --git a/packages/presentation-exchange/src/functions.ts b/packages/presentation-exchange/src/functions.ts index d48dd3472..f6d85df91 100644 --- a/packages/presentation-exchange/src/functions.ts +++ b/packages/presentation-exchange/src/functions.ts @@ -1,23 +1,8 @@ import { IPEXOptions, IPEXPresentationSignCallback, IRequiredContext } from './types/IPresentationExchange' import { IPresentationDefinition } from '@sphereon/pex' import { PresentationPayload } from '@veramo/core' -import { W3CVerifiablePresentation } from '@sphereon/ssi-types' - -/* -export async function getPresentationDefinitionStore(pexOptions?: IPEXOptions): Promise | undefined> { - if (pexOptions && pexOptions.definitionId) { - if (!pexOptions.definitionStore) { - // yes the assignment is ugly, but we want an in-memory fallback and it cannot be re-instantiated every time - pexOptions.definitionStore = new KeyValueStore({ - namespace: 'definitions', - store: new Map(), - }) - } - return pexOptions.definitionStore - } - return undefined -} -*/ +import { CredentialMapper, W3CVerifiablePresentation } from '@sphereon/ssi-types' +import { Format } from '@sphereon/pex-models' export async function getPresentationDefinition(pexOptions?: IPEXOptions): Promise { return pexOptions?.definition @@ -29,28 +14,33 @@ export async function createPEXPresentationSignCallback( { kid, fetchRemoteContexts, + format, domain, challenge, }: { kid: string fetchRemoteContexts?: boolean + format?: Format domain?: string challenge?: string }, - context: IRequiredContext + context: IRequiredContext, ): Promise { return async ({ - presentation, - domain, - presentationDefinition, - challenge, - }: { + presentation, + domain, + presentationDefinition, + format, + challenge, + }: { presentation: PresentationPayload presentationDefinition: IPresentationDefinition + format?: Format domain?: string challenge?: string }): Promise => { - const format = presentationDefinition.format + const formatOptions = format ?? presentationDefinition.format + const proofFormat = formatOptions && (!!formatOptions.ldp || !!formatOptions.ldp_vp) ? 'lds' : 'jwt' const vp = await context.agent.createVerifiablePresentation({ presentation, @@ -58,8 +48,9 @@ export async function createPEXPresentationSignCallback( domain, challenge, fetchRemoteContexts: fetchRemoteContexts !== undefined ? fetchRemoteContexts : true, - proofFormat: format && (format.ldp || format.ldp_vp) ? 'lds' : 'jwt', + proofFormat, }) - return vp as W3CVerifiablePresentation + // makes sure we extract an actual JWT from the internal representation in case it is a JWT + return CredentialMapper.storedPresentationToOriginalFormat(vp as W3CVerifiablePresentation) } } diff --git a/packages/siopv2-oid4vp-op-auth/package.json b/packages/siopv2-oid4vp-op-auth/package.json index 96600c35e..71a8c00d5 100644 --- a/packages/siopv2-oid4vp-op-auth/package.json +++ b/packages/siopv2-oid4vp-op-auth/package.json @@ -14,7 +14,7 @@ "build:clean": "tsc --build --clean && tsc --build" }, "dependencies": { - "@sphereon/did-auth-siop": "^0.3.0-unstable.35", + "@sphereon/did-auth-siop": "^0.3.0-unstable.36", "@sphereon/pex": "2.0.0-unstable.14", "@sphereon/ssi-sdk-presentation-exchange": "^0.9.0", "@sphereon/ssi-sdk-core": "^0.9.0", diff --git a/packages/siopv2-oid4vp-op-auth/src/session/functions.ts b/packages/siopv2-oid4vp-op-auth/src/session/functions.ts index bf3af42cc..5b18e7f3a 100644 --- a/packages/siopv2-oid4vp-op-auth/src/session/functions.ts +++ b/packages/siopv2-oid4vp-op-auth/src/session/functions.ts @@ -15,12 +15,14 @@ import { import { TKeyType } from '@veramo/core' import { IVerifyCallbackArgs, IVerifyCredentialResult } from '@sphereon/wellknown-dids-client' import { createPEXPresentationSignCallback } from '@sphereon/ssi-sdk-presentation-exchange' +import { Format } from '@sphereon/pex-models' export async function createOID4VPPresentationSignCallback({ presentationSignCallback, kid, domain, fetchRemoteContexts, + format, challenge, context, }: { @@ -29,13 +31,14 @@ export async function createOID4VPPresentationSignCallback({ domain?: string challenge?: string fetchRemoteContexts?: boolean + format?: Format context: IRequiredContext }): Promise { // fixme: Remove once IPresentation in proper form is available in PEX // @ts-ignore return presentationSignCallback ? presentationSignCallback - : createPEXPresentationSignCallback({ kid, fetchRemoteContexts, domain, challenge }, context) + : createPEXPresentationSignCallback({ kid, fetchRemoteContexts, domain, challenge, format }, context) /*async (args: PresentationSignCallBackParams): Promise => { const presentation: PresentationPayload = args.presentation as PresentationPayload diff --git a/packages/siopv2-oid4vp-rp-rest-api/src/SIOPv2RPRestAPI.ts b/packages/siopv2-oid4vp-rp-rest-api/src/SIOPv2RPRestAPI.ts index 071e2b9c1..f767cdd50 100644 --- a/packages/siopv2-oid4vp-rp-rest-api/src/SIOPv2RPRestAPI.ts +++ b/packages/siopv2-oid4vp-rp-rest-api/src/SIOPv2RPRestAPI.ts @@ -95,17 +95,17 @@ export class SIOPv2RPRestAPI { private removeAuthRequestStateWebappEndpoint() { this.express.delete( - this._opts?.webappDeleteAuthRequestPath ?? '/webapp/definitions/:definitionId/auth-requests/:correlationId', - async (request, response) => { - const correlationId: string = request.params.correlationId - const definitionId: string = request.params.definitionId - if (!correlationId || !definitionId) { - console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) - return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') - } - response.statusCode = 200 - return response.send(this.agent.siopDeleteAuthState({ definitionId, correlationId })) + this._opts?.webappDeleteAuthRequestPath ?? '/webapp/definitions/:definitionId/auth-requests/:correlationId', + async (request, response) => { + const correlationId: string = request.params.correlationId + const definitionId: string = request.params.definitionId + if (!correlationId || !definitionId) { + console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') } + response.statusCode = 200 + return response.send(this.agent.siopDeleteAuthState({ definitionId, correlationId })) + } ) } @@ -115,16 +115,16 @@ export class SIOPv2RPRestAPI { const correlationId: string = request.body.correlationId as string const definitionId: string = request.body.definitionId as string const requestState = - correlationId && definitionId - ? await this.agent.siopGetAuthRequestState({ - correlationId, - definitionId, - errorOnNotFound: false, - }) - : undefined + correlationId && definitionId + ? await this.agent.siopGetAuthRequestState({ + correlationId, + definitionId, + errorOnNotFound: false, + }) + : undefined if (!requestState || !definitionId || !correlationId) { console.log( - `No authentication request mapping could be found for the given URL. correlation: ${correlationId}, definitionId: ${definitionId}` + `No authentication request mapping could be found for the given URL. correlation: ${correlationId}, definitionId: ${definitionId}` ) response.statusCode = 404 @@ -155,8 +155,8 @@ export class SIOPv2RPRestAPI { definitionId, lastUpdated: overallState.lastUpdated, ...(responseState && responseState.status === AuthorizationResponseStateStatus.VERIFIED - ? { payload: await responseState.response.mergedPayloads() } - : {}), + ? { payload: await responseState.response.mergedPayloads() } + : {}), } console.log(`Will send auth status: ${JSON.stringify(statusBody)}`) if (overallState.status === AuthorizationRequestStateStatus.ERROR || overallState.status === AuthorizationResponseStateStatus.ERROR) { @@ -170,19 +170,19 @@ export class SIOPv2RPRestAPI { private createAuthRequestWebappEndpoint() { this.express.post( - this._opts?.webappCreateAuthRequestPath || '/webapp/definitions/:definitionId/auth-requests', - (request: RequestWithAgent, response) => { - // if (!request.agent) throw Error('No agent configured') - const definitionId = request.params.definitionId - const state: string = uuid.uuid() - const correlationId = state + this._opts?.webappCreateAuthRequestPath || '/webapp/definitions/:definitionId/auth-requests', + (request: RequestWithAgent, response) => { + // if (!request.agent) throw Error('No agent configured') + const definitionId = request.params.definitionId + const state: string = uuid.uuid() + const correlationId = state - const requestByReferenceURI = uriWithBase(`/siop/definitions/${definitionId}/auth-requests/${correlationId}`, { - baseURI: this._opts?.siopBaseURI, - }) - const redirectURI = uriWithBase(`/siop/definitions/${definitionId}/auth-responses/${correlationId}`, { baseURI: this._opts?.siopBaseURI }) + const requestByReferenceURI = uriWithBase(`/siop/definitions/${definitionId}/auth-requests/${correlationId}`, { + baseURI: this._opts?.siopBaseURI, + }) + const redirectURI = uriWithBase(`/siop/definitions/${definitionId}/auth-responses/${correlationId}`, { baseURI: this._opts?.siopBaseURI }) - this.agent + this.agent .siopCreateAuthRequestURI({ definitionId, correlationId, @@ -204,31 +204,31 @@ export class SIOPv2RPRestAPI { console.error(e, e.stack) return SIOPv2RPRestAPI.sendErrorResponse(response, 500, 'Could not create an authorization request URI: ' + e.message) }) - } + } ) } private verifyAuthResponseSIOPv2Endpoint() { this.express.post( - this._opts?.siopVerifyAuthResponsePath ?? '/siop/definitions/:definitionId/auth-responses/:correlationId', - async (request, response) => { - const correlationId = request.params.correlationId - const definitionId = request.params.definitionId - if (!correlationId || !definitionId) { - console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) - return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') - } - console.log('Authorization Response (siop-sessions') - console.log(JSON.stringify(request.body, null, 2)) - const definition = await this.agent.pexStoreGetDefinition({ definitionId }) - const authorizationResponse = typeof request.body === 'string' ? request.body : (request.body as AuthorizationResponsePayload) - console.log(`URI: ${JSON.stringify(authorizationResponse)}`) - if (!definition) { - response.statusCode = 404 - response.statusMessage = `No definition ${definitionId}` - return response.send() - } - await this.agent + this._opts?.siopVerifyAuthResponsePath ?? '/siop/definitions/:definitionId/auth-responses/:correlationId', + async (request, response) => { + const correlationId = request.params.correlationId + const definitionId = request.params.definitionId + if (!correlationId || !definitionId) { + console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') + } + console.log('Authorization Response (siop-sessions') + console.log(JSON.stringify(request.body, null, 2)) + const definition = await this.agent.pexStoreGetDefinition({ definitionId }) + const authorizationResponse = typeof request.body === 'string' ? request.body : (request.body as AuthorizationResponsePayload) + console.log(`URI: ${JSON.stringify(authorizationResponse)}`) + if (!definition) { + response.statusCode = 404 + response.statusMessage = `No definition ${definitionId}` + return response.send() + } + await this.agent .siopVerifyAuthResponse({ authorizationResponse, correlationId, @@ -263,50 +263,50 @@ export class SIOPv2RPRestAPI { response.statusMessage = reason.message return response.send() }) - } + } ) } private getAuthRequestSIOPv2Endpoint() { this.express.get( - this._opts?.siopGetAuthRequestPath ?? '/siop/definitions/:definitionId/auth-requests/:correlationId', - async (request, response) => { - const correlationId = request.params.correlationId - const definitionId = request.params.definitionId - if (!correlationId || !definitionId) { - console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) - return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') - } - const requestState = await this.agent.siopGetAuthRequestState({ + this._opts?.siopGetAuthRequestPath ?? '/siop/definitions/:definitionId/auth-requests/:correlationId', + async (request, response) => { + const correlationId = request.params.correlationId + const definitionId = request.params.definitionId + if (!correlationId || !definitionId) { + console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, 'No authorization request could be found') + } + const requestState = await this.agent.siopGetAuthRequestState({ + correlationId, + definitionId, + errorOnNotFound: false, + }) + if (!requestState) { + console.log( + `No authorization request could be found for the given url in the state manager. correlationId: ${correlationId}, definitionId: ${definitionId}` + ) + return SIOPv2RPRestAPI.sendErrorResponse(response, 404, `No authorization request could be found`) + } + const requestObject = await requestState.request?.requestObject?.toJwt() + console.log('JWT Request object:') + console.log(requestObject) + + let error: string | undefined + try { + response.statusCode = 200 + return response.send(requestObject) + } catch (e) { + error = typeof e === 'string' ? e : e instanceof Error ? e.message : undefined + } finally { + this.agent.siopUpdateAuthRequestState({ correlationId, definitionId, - errorOnNotFound: false, + state: AuthorizationRequestStateStatus.SENT, + error, }) - if (!requestState) { - console.log( - `No authorization request could be found for the given url in the state manager. correlationId: ${correlationId}, definitionId: ${definitionId}` - ) - return SIOPv2RPRestAPI.sendErrorResponse(response, 404, `No authorization request could be found`) - } - const requestObject = await requestState.request?.requestObject?.toJwt() - console.log('JWT Request object:') - console.log(requestObject) - - let error: string | undefined - try { - response.statusCode = 200 - return response.send(requestObject) - } catch (e) { - error = typeof e === 'string' ? e : e instanceof Error ? e.message : undefined - } finally { - this.agent.siopUpdateAuthRequestState({ - correlationId, - definitionId, - state: AuthorizationRequestStateStatus.SENT, - error, - }) - } } + } ) } } diff --git a/packages/siopv2-oid4vp-rp-rest-client/__tests__/integration.test.ts b/packages/siopv2-oid4vp-rp-rest-client/__tests__/integration.test.ts index def1b3e48..218f3e1ae 100644 --- a/packages/siopv2-oid4vp-rp-rest-client/__tests__/integration.test.ts +++ b/packages/siopv2-oid4vp-rp-rest-client/__tests__/integration.test.ts @@ -6,7 +6,7 @@ const definitionId = '9449e2db-791f-407c-b086-c21cc677d2e0' const baseUrl = 'https://ssi-backend.sphereon.com' const agent = createAgent({ - plugins: [new SIOPv2OID4VPRPRestClient({baseUrl, definitionId})], + plugins: [new SIOPv2OID4VPRPRestClient({ baseUrl, definitionId })], }) describe('@sphereon/siopv2-oid4vp-rp-rest-client', () => { diff --git a/packages/siopv2-oid4vp-rp-rest-client/__tests__/mockedEndpoints.test.ts b/packages/siopv2-oid4vp-rp-rest-client/__tests__/mockedEndpoints.test.ts index 47780c6d6..27440703d 100644 --- a/packages/siopv2-oid4vp-rp-rest-client/__tests__/mockedEndpoints.test.ts +++ b/packages/siopv2-oid4vp-rp-rest-client/__tests__/mockedEndpoints.test.ts @@ -7,7 +7,7 @@ const definitionId = '9449e2db-791f-407c-b086-c21cc677d2e0' const baseUrl = 'https://my-siop-endpoint' const agent = createAgent({ - plugins: [new SIOPv2OID4VPRPRestClient({baseUrl, definitionId})], + plugins: [new SIOPv2OID4VPRPRestClient({ baseUrl, definitionId })], }) afterAll(() => { nock.cleanAll() diff --git a/packages/siopv2-oid4vp-rp-rest-client/src/agent/SIOPv2OID4VPRPRestClient.ts b/packages/siopv2-oid4vp-rp-rest-client/src/agent/SIOPv2OID4VPRPRestClient.ts index 1f94d6f53..2f304ef69 100644 --- a/packages/siopv2-oid4vp-rp-rest-client/src/agent/SIOPv2OID4VPRPRestClient.ts +++ b/packages/siopv2-oid4vp-rp-rest-client/src/agent/SIOPv2OID4VPRPRestClient.ts @@ -22,7 +22,7 @@ export class SIOPv2OID4VPRPRestClient implements IAgentPlugin { private readonly baseUrl?: string private readonly definitionId?: string - constructor(args?: { baseUrl?: string, definitionId?: string }) { + constructor(args?: { baseUrl?: string; definitionId?: string }) { if (args?.baseUrl) { this.baseUrl = args.baseUrl } diff --git a/packages/ssi-types/src/mapper/credential-mapper.ts b/packages/ssi-types/src/mapper/credential-mapper.ts index cb8a4d552..e4bf7ce7e 100644 --- a/packages/ssi-types/src/mapper/credential-mapper.ts +++ b/packages/ssi-types/src/mapper/credential-mapper.ts @@ -452,7 +452,7 @@ export class CredentialMapper { if (typeof document === 'string') { return this.isJsonLdAsString(document) ? DocumentFormat.JSONLD : DocumentFormat.JWT } - const proofs = 'vc' in document ? document.vc.proof : ('vp' in document ? document.vp.proof : (document).proof) + const proofs = 'vc' in document ? document.vc.proof : 'vp' in document ? document.vp.proof : (document).proof const proof: IProof = Array.isArray(proofs) ? proofs[0] : proofs if (proof?.jwt) { diff --git a/yarn.lock b/yarn.lock index 567d27c96..23c612ed1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2584,6 +2584,39 @@ uint8arrays "^3.1.1" uuid "^9.0.0" +"@sphereon/did-auth-siop@^0.3.0-unstable.36": + version "0.3.0-unstable.36" + resolved "https://registry.yarnpkg.com/@sphereon/did-auth-siop/-/did-auth-siop-0.3.0-unstable.36.tgz#28c1afe6b9b3aa3bf6e416088f2c7a5375ebe66a" + integrity sha512-Q70ZhufI8CqVAAfAVYRNj+bttc6s2bFl2peMgcWcVryI3zamroB+8i+O+y/djgY+Ac37V5eTEXXoPHpdRLwygg== + dependencies: + "@sphereon/did-uni-client" "^0.6.0" + "@sphereon/pex" "^2.0.0-unstable.14" + "@sphereon/pex-models" "^1.2.2" + "@sphereon/ssi-types" "^0.9.1-next.109" + "@sphereon/wellknown-dids-client" "^0.1.3" + "@stablelib/ed25519" "^1.0.3" + "@stablelib/random" "^1.0.2" + "@stablelib/sha256" "^1.0.1" + "@stablelib/x25519" "^1.0.3" + "@stablelib/xchacha20poly1305" "^1.0.1" + bech32 "^2.0.0" + bs58 "^5.0.0" + canonicalize "^1.0.8" + cross-fetch "^3.1.5" + did-jwt "^6.11.2" + did-resolver "^4.1.0" + elliptic "^6.5.4" + eth-crypto "^2.5.0" + events "^3.3.0" + jose "^4.12.0" + js-sha3 "^0.8.0" + language-tags "^1.0.8" + multiformats "^11.0.1" + querystring "^0.2.1" + ts-interface-checker "^1.0.2" + uint8arrays "^3.1.1" + uuid "^9.0.0" + "@sphereon/did-uni-client@^0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@sphereon/did-uni-client/-/did-uni-client-0.4.0.tgz#1a4c4302c3ad6714e23aa07adf8154a81c1968dd" @@ -2672,20 +2705,6 @@ resolved "https://registry.yarnpkg.com/@sphereon/pex-models/-/pex-models-1.2.2.tgz#85d2062693c0a9c31fb5c1be0847675141803846" integrity sha512-/pGDQGs4lSK4Fuf6WzaGengb1UqNYmjRdkIC+dJT89kMktIwJLoNB2PvQHcggn3nEbmwZFB/1dgJGumAnWPYsg== -"@sphereon/pex@2.0.0-unstable.13": - version "2.0.0-unstable.13" - resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-2.0.0-unstable.13.tgz#27e6315b4758ff0bf8c198ba45d5c95f2d6495b1" - integrity sha512-ZJPQK4xqL9urfYRUSZMHp/jb0j4VmrR89WG7gbKJb9St6KLL1Ew/jxARP6+vrMeo5lvLH5KkX/Wq4POK15vDyw== - dependencies: - "@sphereon/pex-models" "^1.2.2" - "@sphereon/ssi-types" "^0.9.0" - ajv "^8.12.0" - ajv-formats "^2.1.1" - jsonpath "^1.1.1" - jwt-decode "^3.1.2" - nanoid "^3.3.4" - string.prototype.matchall "^4.0.8" - "@sphereon/pex@2.0.0-unstable.14", "@sphereon/pex@^2.0.0-unstable.14": version "2.0.0-unstable.14" resolved "https://registry.yarnpkg.com/@sphereon/pex/-/pex-2.0.0-unstable.14.tgz#13727200866206a93aa478a2e4ba651bb2024d91" @@ -2789,7 +2808,7 @@ did-resolver "^4.0.1" elliptic "^6.5.4" -"@sphereon/ssi-types@^0.9.1-next.100": +"@sphereon/ssi-types@^0.9.1-next.100", "@sphereon/ssi-types@^0.9.1-next.109": version "0.9.1-unstable.136" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.9.1-unstable.136.tgz#160a1ef075e01879e8fd7cac5523f3afdc0bd8c4" integrity sha512-ugtoM+FVBTR+lNmJvQPPLkrkaC8tPDzS5GA/A56gdCSh8YZoCh7CympVZYHKF9W0gg0lqhCd+kh09UoFUdGLQg==