Skip to content

Commit

Permalink
feat: More support for definition Formats when creating VPs from SIOP
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Apr 26, 2023
1 parent 7c8439e commit 846ef0b
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 135 deletions.
1 change: 0 additions & 1 deletion packages/data-store/src/contact/ContactStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export class ContactStore extends AbstractContactStore {

removeContact = async ({ contactId }: IRemoveContactArgs): Promise<void> => {
debug('Removing contact', contactId)

;(await this.dbConnection)
.getRepository(ContactEntity)
.findOneById(contactId)
Expand Down
2 changes: 1 addition & 1 deletion packages/presentation-exchange/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
43 changes: 17 additions & 26 deletions packages/presentation-exchange/src/functions.ts
Original file line number Diff line number Diff line change
@@ -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<IKeyValueStore<IPresentationDefinition> | 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<string, IPresentationDefinition>(),
})
}
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<IPresentationDefinition | undefined> {
return pexOptions?.definition
Expand All @@ -29,37 +14,43 @@ export async function createPEXPresentationSignCallback(
{
kid,
fetchRemoteContexts,
format,
domain,
challenge,
}: {
kid: string
fetchRemoteContexts?: boolean
format?: Format
domain?: string
challenge?: string
},
context: IRequiredContext
context: IRequiredContext,
): Promise<IPEXPresentationSignCallback> {
return async ({
presentation,
domain,
presentationDefinition,
challenge,
}: {
presentation,
domain,
presentationDefinition,
format,
challenge,
}: {
presentation: PresentationPayload
presentationDefinition: IPresentationDefinition
format?: Format
domain?: string
challenge?: string
}): Promise<W3CVerifiablePresentation> => {
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,
keyRef: kid,
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)
}
}
2 changes: 1 addition & 1 deletion packages/siopv2-oid4vp-op-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 4 additions & 1 deletion packages/siopv2-oid4vp-op-auth/src/session/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}: {
Expand All @@ -29,13 +31,14 @@ export async function createOID4VPPresentationSignCallback({
domain?: string
challenge?: string
fetchRemoteContexts?: boolean
format?: Format
context: IRequiredContext
}): Promise<PresentationSignCallback> {
// 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<W3CVerifiablePresentation> => {
const presentation: PresentationPayload = args.presentation as PresentationPayload
Expand Down
172 changes: 86 additions & 86 deletions packages/siopv2-oid4vp-rp-rest-api/src/SIOPv2RPRestAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }))
}
)
}

Expand All @@ -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

Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
})
}
}
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const definitionId = '9449e2db-791f-407c-b086-c21cc677d2e0'
const baseUrl = 'https://ssi-backend.sphereon.com'

const agent = createAgent<IResolver & ISIOPv2OID4VPRPRestClient>({
plugins: [new SIOPv2OID4VPRPRestClient({baseUrl, definitionId})],
plugins: [new SIOPv2OID4VPRPRestClient({ baseUrl, definitionId })],
})

describe('@sphereon/siopv2-oid4vp-rp-rest-client', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const definitionId = '9449e2db-791f-407c-b086-c21cc677d2e0'
const baseUrl = 'https://my-siop-endpoint'

const agent = createAgent<IResolver & ISIOPv2OID4VPRPRestClient>({
plugins: [new SIOPv2OID4VPRPRestClient({baseUrl, definitionId})],
plugins: [new SIOPv2OID4VPRPRestClient({ baseUrl, definitionId })],
})
afterAll(() => {
nock.cleanAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit 846ef0b

Please sign in to comment.