diff --git a/packages/did-auth-siop-op-authenticator/README.md b/packages/did-auth-siop-op-authenticator/README.md index 54aa90851..537557fee 100644 --- a/packages/did-auth-siop-op-authenticator/README.md +++ b/packages/did-auth-siop-op-authenticator/README.md @@ -176,8 +176,7 @@ For more detailed information see: [Self Issued OpenID Provider v2 (SIOP)](https const sessionId = 'example_session_id' const authorizationRequestDetailsResponse = await agent.getSiopAuthorizationRequestDetails({ sessionId, - verifiedAuthorizationRequest: createAuthorizationResponse, - verifiableCredentials: [credential], + verifiedAuthorizationRequest: createAuthorizationResponse }) ``` diff --git a/packages/did-auth-siop-op-authenticator/__tests__/localAgent.test.ts b/packages/did-auth-siop-op-authenticator/__tests__/localAgent.test.ts index acff8a649..d7fee9606 100644 --- a/packages/did-auth-siop-op-authenticator/__tests__/localAgent.test.ts +++ b/packages/did-auth-siop-op-authenticator/__tests__/localAgent.test.ts @@ -1,13 +1,32 @@ +import * as fs from 'fs' import { getConfig } from '@veramo/cli/build/setup' import { createObjects } from '@veramo/cli/build/lib/objectCreator' +import didAuthSiopOpAuthenticatorAgentLogic from './shared/didAuthSiopOpAuthenticatorAgentLogic' +import { PresentationSignCallback } from '@sphereon/did-auth-siop' jest.setTimeout(30000) -import didAuthSiopOpAuthenticatorAgentLogic from './shared/didAuthSiopOpAuthenticatorAgentLogic' -import { presentationSignCallback } from './shared/mockedData' +function getFile(path: string) { + return fs.readFileSync(path, 'utf-8') +} + +function getFileAsJson(path: string) { + return JSON.parse(getFile(path)) +} let agent: any +const presentationSignCallback: PresentationSignCallback = async (args) => { + const presentationSignProof = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/psc/psc.json' + ) + + return { + ...args.presentation, + ...presentationSignProof + } +} + const setup = async (): Promise => { const config = getConfig('packages/did-auth-siop-op-authenticator/agent.yml') config.agent.$args[0].plugins[1].$args[0] = presentationSignCallback diff --git a/packages/did-auth-siop-op-authenticator/__tests__/restAgent.test.ts b/packages/did-auth-siop-op-authenticator/__tests__/restAgent.test.ts index d78537bef..62e47543d 100644 --- a/packages/did-auth-siop-op-authenticator/__tests__/restAgent.test.ts +++ b/packages/did-auth-siop-op-authenticator/__tests__/restAgent.test.ts @@ -1,6 +1,8 @@ +import * as fs from 'fs' import 'cross-fetch/polyfill' +// @ts-ignore import express from 'express' -import { IAgent, createAgent, IAgentOptions } from '@veramo/core' +import { IAgent, createAgent, IAgentOptions, IDataStore } from '@veramo/core' import { AgentRestClient } from '@veramo/remote-client' import { Server } from 'http' import { AgentRouter, RequestWithAgentRouter } from '@veramo/remote-server' @@ -12,17 +14,36 @@ import { getDidKeyResolver } from '@veramo/did-provider-key' import { DIDResolverPlugin } from '@veramo/did-resolver' import { getUniResolver } from '@sphereon/did-uni-client' import didAuthSiopOpAuthenticatorAgentLogic from './shared/didAuthSiopOpAuthenticatorAgentLogic' -import { presentationSignCallback } from './shared/mockedData' +import { PresentationSignCallback } from '@sphereon/did-auth-siop' jest.setTimeout(30000) +function getFile(path: string) { + return fs.readFileSync(path, 'utf-8') +} + +function getFileAsJson(path: string) { + return JSON.parse(getFile(path)) +} + const port = 3002 const basePath = '/agent' let serverAgent: IAgent let restServer: Server +const presentationSignCallback: PresentationSignCallback = async (args) => { + const presentationSignProof = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/psc/psc.json' + ) + + return { + ...args.presentation, + ...presentationSignProof + } +} + const getAgent = (options?: IAgentOptions) => - createAgent({ + createAgent({ ...options, plugins: [ new DidAuthSiopOpAuthenticator(presentationSignCallback), diff --git a/packages/did-auth-siop-op-authenticator/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts b/packages/did-auth-siop-op-authenticator/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts index 5246519e9..33b5059e3 100644 --- a/packages/did-auth-siop-op-authenticator/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts +++ b/packages/did-auth-siop-op-authenticator/__tests__/shared/didAuthSiopOpAuthenticatorAgentLogic.ts @@ -1,8 +1,10 @@ -import { TAgent } from '@veramo/core' -import { OP, AuthorizationRequest } from '@sphereon/did-auth-siop' -import { IDidAuthSiopOpAuthenticator, IGetSiopAuthorizationRequestDetailsArgs } from '../../src' +import * as fs from 'fs' +import { IDataStore, TAgent, VerifiableCredential } from '@veramo/core' +import { IAuthRequestDetails, IDidAuthSiopOpAuthenticator } from '../../src' +import { OP, AuthorizationRequest, VerifiablePresentationWithLocation } from '@sphereon/did-auth-siop' import { + PresentationDefinitionWithLocation, ResponseContext, ResponseMode, ResponseType, @@ -10,10 +12,17 @@ import { UrlEncodingFormat, VerificationMode, VerifiedAuthorizationRequest, - ParsedAuthorizationRequestURI, + ParsedAuthorizationRequestURI } from '@sphereon/did-auth-siop' import { mapIdentifierKeysToDoc } from '@veramo/utils' -import { pdMultiple, pdSingle, vcs, vpMultiple, vpSingle } from './mockedData' + +function getFile(path: string) { + return fs.readFileSync(path, 'utf-8') +} + +function getFileAsJson(path: string) { + return JSON.parse(getFile(path)) +} const nock = require('nock') jest.mock('@veramo/utils', () => ({ @@ -21,7 +30,7 @@ jest.mock('@veramo/utils', () => ({ mapIdentifierKeysToDoc: jest.fn(), })) -type ConfiguredAgent = TAgent +type ConfiguredAgent = TAgent const didMethod = 'ethr' const did = 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a' @@ -156,6 +165,16 @@ export default (testContext: { await testContext.setup() agent = testContext.getAgent() + const idCardCredential: VerifiableCredential = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_idCardCredential.json' + ) + await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: idCardCredential }) + + const driverLicenseCredential: VerifiableCredential = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_driverLicense.json' + ) + await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: driverLicenseCredential }) + nock(redirectUrl).get(`?stateId=${stateId}`).times(5).reply(200, requestResultMockedText) const mockedMapIdentifierKeysToDocMethod = mapIdentifierKeysToDoc as jest.Mock @@ -298,52 +317,96 @@ export default (testContext: { }) it('should get authentication details with single credential', async () => { - let authorizationRequestArgs: IGetSiopAuthorizationRequestDetailsArgs = { + const pd_single: PresentationDefinitionWithLocation = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_single.json' + ) + const vp_single: VerifiablePresentationWithLocation = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_single.json' + ) + vp_single.presentation.presentation_submission!.id = expect.any(String) + + const result: IAuthRequestDetails = await agent.getSiopAuthorizationRequestDetails({ sessionId, verifiedAuthorizationRequest: { ...createAuthorizationResponseMockedResult, - presentationDefinitions: pdSingle, + presentationDefinitions: [pd_single], authorizationRequest: {} as AuthorizationRequest, versions: [], payload: {}, }, - verifiableCredentials: vcs, signingOptions: { nonce: 'nonce202212272050', domain: 'domain202212272051', }, - } - const result = await agent.getSiopAuthorizationRequestDetails(authorizationRequestArgs) + }) + + expect(result).toEqual({ + id: 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', + vpResponseOpts: [vp_single], + }) + }) + + it('should get authentication details with getting specific credentials', async () => { + const pdSingle: PresentationDefinitionWithLocation = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_single.json' + ) + const vpSingle: VerifiablePresentationWithLocation = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_single.json' + ) + vpSingle.presentation.presentation_submission!.id = expect.any(String) + + const result: IAuthRequestDetails = await agent.getSiopAuthorizationRequestDetails({ + sessionId, + verifiedAuthorizationRequest: { + ...createAuthorizationResponseMockedResult, + presentationDefinitions: [pdSingle], + authorizationRequest: {} as AuthorizationRequest, + versions: [], + payload: {}, + }, + credentialFilter: { + where: [{ + column: 'id', + value: ['https://example.com/credentials/1872'] + }] + } + }) expect(result).toEqual({ id: 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', - vpResponseOpts: vpSingle, + alsoKnownAs: undefined, + vpResponseOpts: [vpSingle], }) }) it('should get authentication details with multiple credentials', async () => { - let authorizationRequestArgs: IGetSiopAuthorizationRequestDetailsArgs = { + const pdMultiple: PresentationDefinitionWithLocation = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_multiple.json' + ) + const vpMultiple: VerifiablePresentationWithLocation = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_multiple.json' + ) + vpMultiple.presentation.presentation_submission!.id = expect.any(String) + + const result: IAuthRequestDetails = await agent.getSiopAuthorizationRequestDetails({ sessionId, verifiedAuthorizationRequest: { ...createAuthorizationResponseMockedResult, - presentationDefinitions: pdMultiple, + presentationDefinitions: [pdMultiple], authorizationRequest: {} as AuthorizationRequest, versions: [], payload: {}, }, - verifiableCredentials: vcs, signingOptions: { nonce: 'nonce202212272050', domain: 'domain202212272051', }, - } - - const result = await agent.getSiopAuthorizationRequestDetails(authorizationRequestArgs) + }) expect(result).toEqual({ alsoKnownAs: undefined, id: 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', - vpResponseOpts: vpMultiple, + vpResponseOpts: [vpMultiple], }) }) @@ -370,11 +433,15 @@ export default (testContext: { }) it('should send authentication response', async () => { + const pdMultiple: PresentationDefinitionWithLocation = getFileAsJson( + './packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_multiple.json' + ) + const result = await agent.sendSiopAuthorizationResponse({ sessionId, verifiedAuthorizationRequest: { ...createAuthorizationResponseMockedResult, - presentationDefinitions: pdMultiple, + presentationDefinitions: [pdMultiple], authorizationRequest: {} as AuthorizationRequest, versions: [], authorizationRequestPayload: {}, diff --git a/packages/did-auth-siop-op-authenticator/__tests__/shared/mockedData.ts b/packages/did-auth-siop-op-authenticator/__tests__/shared/mockedData.ts deleted file mode 100644 index 6b6eaaeda..000000000 --- a/packages/did-auth-siop-op-authenticator/__tests__/shared/mockedData.ts +++ /dev/null @@ -1,271 +0,0 @@ -import { - PresentationDefinitionWithLocation, - PresentationDefinitionLocation, - PresentationSignCallback, - PresentationLocation, -} from '@sphereon/did-auth-siop' - -export const pdSingle: PresentationDefinitionWithLocation[] = [ - { - definition: { - id: 'Credentials', - input_descriptors: [ - { - id: 'ID Card Credential', - schema: [ - { - uri: 'https://www.w3.org/2018/credentials/examples/v1/IDCardCredential', - }, - ], - constraints: { - fields: [ - { - path: ['$.issuer.id'], - filter: { - type: 'string', - pattern: 'did:example:issuer', - }, - }, - ], - }, - }, - ], - }, - location: PresentationDefinitionLocation.TOPLEVEL_PRESENTATION_DEF, - }, -] - -export const pdMultiple: PresentationDefinitionWithLocation[] = [ - { - definition: { - id: 'Credentials', - input_descriptors: [ - { - id: "ID Card Credential and Driver's License", - schema: [ - { - uri: 'https://www.w3.org/2018/credentials/examples/v1/IDCardCredential', - }, - { - uri: 'https://www.w3.org/2018/credentials/examples/v1/DriversLicense', - }, - ], - constraints: { - fields: [ - { - path: ['$.issuer.id'], - filter: { - type: 'string', - pattern: 'did:example:[issuer|ebfeb1f712ebc6f1c276e12ec21]', - }, - }, - ], - }, - }, - ], - }, - location: PresentationDefinitionLocation.TOPLEVEL_PRESENTATION_DEF, - }, -] - -export const vcs = [ - { - id: 'https://example.com/credentials/1872', - type: ['VerifiableCredential', 'IDCardCredential'], - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1/IDCardCredential'], - issuer: { - id: 'did:example:issuer', - }, - issuanceDate: '2010-01-01T19:23:24Z', - credentialSubject: { - given_name: 'Fredrik', - family_name: 'Stremberg', - birthdate: '1949-01-22', - }, - proof: { - type: 'RsaSignature2018', - created: '2018-09-14T21:19:10Z', - proofPurpose: 'authentication', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - }, - }, - { - id: 'https://example.com/credentials/1873', - type: ['VerifiableCredential', 'DriversLicense'], - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1/DriversLicense'], - issuer: { - id: 'did:example:ebfeb1f712ebc6f1c276e12ec21', - }, - issuanceDate: '2010-01-01T19:23:24Z', - credentialSubject: { - given_name: 'John', - family_name: 'Doe', - birthdate: '1975-01-05', - }, - proof: { - type: 'RsaSignature2018', - created: '2018-09-14T21:19:10Z', - proofPurpose: 'authentication', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - }, - }, -] - -export const vpSingle = [ - { - format: 'ldp_vp', - location: PresentationLocation.ID_TOKEN, - presentation: { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://identity.foundation/presentation-exchange/submission/v1'], - presentation_submission: { - definition_id: 'Credentials', - descriptor_map: [ - { - format: 'ldp_vc', - id: 'ID Card Credential', - path: '$.verifiableCredential[0]', - }, - ], - id: expect.any(String), - }, - proof: { - type: 'RsaSignature2018', - created: '2018-09-14T21:19:10Z', - proofPurpose: 'authentication', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - }, - type: ['VerifiablePresentation', 'PresentationSubmission'], - verifiableCredential: [ - { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1/IDCardCredential'], - credentialSubject: { - birthdate: '1949-01-22', - family_name: 'Stremberg', - given_name: 'Fredrik', - }, - id: 'https://example.com/credentials/1872', - issuanceDate: '2010-01-01T19:23:24Z', - issuer: { - id: 'did:example:issuer', - }, - proof: { - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - created: '2018-09-14T21:19:10Z', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - proofPurpose: 'authentication', - type: 'RsaSignature2018', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - }, - type: ['VerifiableCredential', 'IDCardCredential'], - }, - ], - }, - }, -] - -export const vpMultiple = [ - { - format: 'ldp_vp', - location: PresentationLocation.ID_TOKEN, - presentation: { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://identity.foundation/presentation-exchange/submission/v1'], - presentation_submission: { - definition_id: 'Credentials', - descriptor_map: [ - { - format: 'ldp_vc', - id: "ID Card Credential and Driver's License", - path: '$.verifiableCredential[0]', - }, - { - format: 'ldp_vc', - id: "ID Card Credential and Driver's License", - path: '$.verifiableCredential[1]', - }, - ], - id: expect.any(String), - }, - proof: { - type: 'RsaSignature2018', - created: '2018-09-14T21:19:10Z', - proofPurpose: 'authentication', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - }, - type: ['VerifiablePresentation', 'PresentationSubmission'], - verifiableCredential: [ - { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1/IDCardCredential'], - credentialSubject: { - birthdate: '1949-01-22', - family_name: 'Stremberg', - given_name: 'Fredrik', - }, - id: 'https://example.com/credentials/1872', - issuanceDate: '2010-01-01T19:23:24Z', - issuer: { - id: 'did:example:issuer', - }, - proof: { - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - created: '2018-09-14T21:19:10Z', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - proofPurpose: 'authentication', - type: 'RsaSignature2018', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - }, - type: ['VerifiableCredential', 'IDCardCredential'], - }, - { - id: 'https://example.com/credentials/1873', - type: ['VerifiableCredential', 'DriversLicense'], - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1/DriversLicense'], - issuer: { - id: 'did:example:ebfeb1f712ebc6f1c276e12ec21', - }, - issuanceDate: '2010-01-01T19:23:24Z', - credentialSubject: { - given_name: 'John', - family_name: 'Doe', - birthdate: '1975-01-05', - }, - proof: { - type: 'RsaSignature2018', - created: '2018-09-14T21:19:10Z', - proofPurpose: 'authentication', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - }, - }, - ], - }, - }, -] - -export const presentationSignCallback: PresentationSignCallback = async (_args) => ({ - ..._args.presentation, - proof: { - type: 'RsaSignature2018', - created: '2018-09-14T21:19:10Z', - proofPurpose: 'authentication', - verificationMethod: 'did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1', - challenge: '1f44d55f-f161-4938-a659-f8026467f126', - domain: '4jt78h47fh47', - jws: 'eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78', - }, -}) diff --git a/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_multiple.json b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_multiple.json new file mode 100644 index 000000000..8c86517e8 --- /dev/null +++ b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_multiple.json @@ -0,0 +1,30 @@ +{ + "definition": { + "id": "Credentials", + "input_descriptors": [ + { + "id": "ID Card Credential and Driver's License", + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential" + }, + { + "uri": "https://www.w3.org/2018/credentials/examples/v1/DriversLicense" + } + ], + "constraints": { + "fields": [ + { + "path": ["$.issuer.id"], + "filter": { + "type": "string", + "pattern": "did:example:[issuer|ebfeb1f712ebc6f1c276e12ec21]" + } + } + ] + } + } + ] + }, + "location": "presentation_definition" +} diff --git a/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_single.json b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_single.json new file mode 100644 index 000000000..81a68f376 --- /dev/null +++ b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/pd/pd_single.json @@ -0,0 +1,27 @@ +{ + "definition": { + "id": "Credentials", + "input_descriptors": [ + { + "id": "ID Card Credential", + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential" + } + ], + "constraints": { + "fields": [ + { + "path": ["$.issuer.id"], + "filter": { + "type": "string", + "pattern": "did:example:issuer" + } + } + ] + } + } + ] + }, + "location": "presentation_definition" +} diff --git a/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/psc/psc.json b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/psc/psc.json new file mode 100644 index 000000000..9755bd33f --- /dev/null +++ b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/psc/psc.json @@ -0,0 +1,11 @@ +{ + "proof": { + "type": "RsaSignature2018", + "created": "2018-09-14T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" + } +} diff --git a/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_driverLicense.json b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_driverLicense.json new file mode 100644 index 000000000..daa0f0f2b --- /dev/null +++ b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_driverLicense.json @@ -0,0 +1,23 @@ +{ + "id": "https://example.com/credentials/1873", + "type": ["VerifiableCredential", "DriversLicense"], + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/DriversLicense"], + "issuer": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + }, + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "given_name": "John", + "family_name": "Doe", + "birthdate": "1975-01-05" + }, + "proof": { + "type": "RsaSignature2018", + "created": "2018-09-14T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" + } +} diff --git a/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_idCardCredential.json b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_idCardCredential.json new file mode 100644 index 000000000..01abcea0e --- /dev/null +++ b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vc/vc_idCardCredential.json @@ -0,0 +1,23 @@ +{ + "id": "https://example.com/credentials/1872", + "type": ["VerifiableCredential", "IDCardCredential"], + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], + "issuer": { + "id": "did:example:issuer" + }, + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "given_name": "Fredrik", + "family_name": "Stremberg", + "birthdate": "1949-01-22" + }, + "proof": { + "type": "RsaSignature2018", + "created": "2018-09-14T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" + } +} diff --git a/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_multiple.json b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_multiple.json new file mode 100644 index 000000000..4f23bbcca --- /dev/null +++ b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_multiple.json @@ -0,0 +1,81 @@ +{ + "format": "ldp_vp", + "location": "id_token", + "presentation": { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://identity.foundation/presentation-exchange/submission/v1"], + "presentation_submission": { + "definition_id": "Credentials", + "descriptor_map": [ + { + "format": "ldp_vc", + "id": "ID Card Credential and Driver's License", + "path": "$.verifiableCredential[0]" + }, + { + "format": "ldp_vc", + "id": "ID Card Credential and Driver's License", + "path": "$.verifiableCredential[1]" + } + ], + "id": "8oBenRGlNXd0Sp770bCb3" + }, + "proof": { + "type": "RsaSignature2018", + "created": "2018-09-14T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" + }, + "type": ["VerifiablePresentation", "PresentationSubmission"], + "verifiableCredential": [ + { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], + "credentialSubject": { + "birthdate": "1949-01-22", + "family_name": "Stremberg", + "given_name": "Fredrik" + }, + "id": "https://example.com/credentials/1872", + "issuanceDate": "2010-01-01T19:23:24Z", + "issuer": { + "id": "did:example:issuer" + }, + "proof": { + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "created": "2018-09-14T21:19:10Z", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78", + "proofPurpose": "authentication", + "type": "RsaSignature2018", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1" + }, + "type": ["VerifiableCredential", "IDCardCredential"] + }, + { + "id": "https://example.com/credentials/1873", + "type": ["VerifiableCredential", "DriversLicense"], + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/DriversLicense"], + "issuer": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + }, + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "given_name": "John", + "family_name": "Doe", + "birthdate": "1975-01-05" + }, + "proof": { + "type": "RsaSignature2018", + "created": "2018-09-14T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" + } + } + ] + } +} diff --git a/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_single.json b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_single.json new file mode 100644 index 000000000..bb4919614 --- /dev/null +++ b/packages/did-auth-siop-op-authenticator/__tests__/vc_vp_examples/vp/vp_single.json @@ -0,0 +1,53 @@ +{ + "format": "ldp_vp", + "location": "id_token", + "presentation": { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://identity.foundation/presentation-exchange/submission/v1"], + "presentation_submission": { + "definition_id": "Credentials", + "descriptor_map": [ + { + "format": "ldp_vc", + "id": "ID Card Credential", + "path": "$.verifiableCredential[0]" + } + ], + "id": "8oBenRGlNXd0Sp770bCb3" + }, + "proof": { + "type": "RsaSignature2018", + "created": "2018-09-14T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" + }, + "type": ["VerifiablePresentation", "PresentationSubmission"], + "verifiableCredential": [ + { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1/IDCardCredential"], + "credentialSubject": { + "birthdate": "1949-01-22", + "family_name": "Stremberg", + "given_name": "Fredrik" + }, + "id": "https://example.com/credentials/1872", + "issuanceDate": "2010-01-01T19:23:24Z", + "issuer": { + "id": "did:example:issuer" + }, + "proof": { + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "created": "2018-09-14T21:19:10Z", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78", + "proofPurpose": "authentication", + "type": "RsaSignature2018", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1" + }, + "type": ["VerifiableCredential", "IDCardCredential"] + } + ] + } +} diff --git a/packages/did-auth-siop-op-authenticator/agent.yml b/packages/did-auth-siop-op-authenticator/agent.yml index 10303d341..98f5bd4e4 100644 --- a/packages/did-auth-siop-op-authenticator/agent.yml +++ b/packages/did-auth-siop-op-authenticator/agent.yml @@ -12,6 +12,20 @@ constants: - verifySiopAuthorizationRequestURI - sendSiopAuthorizationResponse +# Database +dbConnection: + $require: typeorm#DataSource + $args: + - type: sqlite + database: ':memory:' + synchronize: false + migrationsRun: true + migrations: + $require: '@veramo/data-store?t=object#migrations' + logging: false + entities: + $require: '@veramo/data-store?t=object#Entities' + server: baseUrl: $ref: /constants/baseUrl @@ -104,5 +118,11 @@ agent: plugins: - $ref: /didResolver - $require: ./packages/did-auth-siop-op-authenticator/dist#DidAuthSiopOpAuthenticator - '$args': + $args: - presentationSignCallback: {} + - $require: '@veramo/data-store#DataStore' + $args: + - $ref: /dbConnection + - $require: '@veramo/data-store#DataStoreORM' + $args: + - $ref: /dbConnection diff --git a/packages/did-auth-siop-op-authenticator/plugin.schema.json b/packages/did-auth-siop-op-authenticator/plugin.schema.json index 43158adba..03a1513eb 100644 --- a/packages/did-auth-siop-op-authenticator/plugin.schema.json +++ b/packages/did-auth-siop-op-authenticator/plugin.schema.json @@ -155,18 +155,15 @@ "additionalProperties": true } }, - "verifiableCredentials": { - "type": "array", - "items": { - "type": "object", - "properties": { - "additionalProperties": true - } + "credentialFilter": { + "type": "object", + "properties": { + "additionalProperties": true } }, "additionalProperties": false }, - "required": ["sessionId", "verifiedAuthenticationRequest", "verifiableCredentials"], + "required": ["sessionId", "verifiedAuthenticationRequest"], "description": "Arguments needed for {@link DidAuthSiopOpAuthenticator.getSiopAuthenticationRequestDetails } " }, "IAuthRequestDetails": { diff --git a/packages/did-auth-siop-op-authenticator/src/agent/DidAuthSiopOpAuthenticator.ts b/packages/did-auth-siop-op-authenticator/src/agent/DidAuthSiopOpAuthenticator.ts index 7e4627968..6d0afdf12 100644 --- a/packages/did-auth-siop-op-authenticator/src/agent/DidAuthSiopOpAuthenticator.ts +++ b/packages/did-auth-siop-op-authenticator/src/agent/DidAuthSiopOpAuthenticator.ts @@ -1,5 +1,5 @@ import { schema } from '../index' -import { IAgentPlugin } from '@veramo/core' +import { IAgentPlugin, UniqueVerifiableCredential } from '@veramo/core' import { OpSession } from '../session/OpSession' import { v4 as uuidv4 } from 'uuid' @@ -20,6 +20,7 @@ import { IVerifySiopAuthorizationRequestUriArgs, } from '../types/IDidAuthSiopOpAuthenticator' import { VerifiedAuthorizationRequest, ParsedAuthorizationRequestURI, PresentationSignCallback } from '@sphereon/did-auth-siop' +import { CredentialMapper, IVerifiableCredential } from '@sphereon/ssi-types' export class DidAuthSiopOpAuthenticator implements IAgentPlugin { readonly schema = schema.IDidAuthSiopOpAuthenticator @@ -97,7 +98,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin { } private async authenticateWithSiop(args: IAuthenticateWithSiopArgs, context: IRequiredContext): Promise { - return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session) => + return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session: OpSession) => session.authenticateWithSiop({ ...args, customApprovals: this.customApprovals }).then(async (response: Response) => { await context.agent.emit(events.DID_SIOP_AUTHENTICATED, response) return response @@ -109,30 +110,36 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin { args: IGetSiopAuthorizationRequestFromRpArgs, context: IRequiredContext ): Promise { - return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session) => session.getSiopAuthorizationRequestFromRP(args)) + return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session: OpSession) => + session.getSiopAuthorizationRequestFromRP(args) + ) } private async getSiopAuthorizationRequestDetails( args: IGetSiopAuthorizationRequestDetailsArgs, context: IRequiredContext ): Promise { - return this.getSessionForSiop( - { - sessionId: args.sessionId, - }, - context - ).then((session) => session.getSiopAuthorizationRequestDetails(args, this.presentationSignCallback)) + const uniqueVcs: Array = await context.agent.dataStoreORMGetVerifiableCredentials(args.credentialFilter) + const verifiableCredentials: Array = uniqueVcs.map((uniqueVc: UniqueVerifiableCredential) => + CredentialMapper.toExternalVerifiableCredential(uniqueVc.verifiableCredential) + ) + + return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session: OpSession) => + session.getSiopAuthorizationRequestDetails({ ...args, verifiableCredentials, presentationSignCallback: this.presentationSignCallback }) + ) } private async verifySiopAuthorizationRequestURI( args: IVerifySiopAuthorizationRequestUriArgs, context: IRequiredContext ): Promise { - return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session) => session.verifySiopAuthorizationRequestURI(args)) + return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session: OpSession) => + session.verifySiopAuthorizationRequestURI(args) + ) } private async sendSiopAuthorizationResponse(args: ISendSiopAuthorizationResponseArgs, context: IRequiredContext): Promise { - return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session) => + return this.getSessionForSiop({ sessionId: args.sessionId }, context).then((session: OpSession) => session.sendSiopAuthorizationResponse(args).then(async (response: Response) => { await context.agent.emit(events.DID_SIOP_AUTHENTICATED, response) return response diff --git a/packages/did-auth-siop-op-authenticator/src/session/OpSession.ts b/packages/did-auth-siop-op-authenticator/src/session/OpSession.ts index 95495cd31..284c418f0 100644 --- a/packages/did-auth-siop-op-authenticator/src/session/OpSession.ts +++ b/packages/did-auth-siop-op-authenticator/src/session/OpSession.ts @@ -104,15 +104,11 @@ export class OpSession { .catch((error: unknown) => Promise.reject(error)) } - public async getSiopAuthorizationRequestDetails( - args: IOpsGetSiopAuthorizationRequestDetailsArgs, - presentationSignCallback: PresentationSignCallback - ): Promise { - // TODO fix vc retrievement https://sphereon.atlassian.net/browse/MYC-142 + public async getSiopAuthorizationRequestDetails(args: IOpsGetSiopAuthorizationRequestDetailsArgs): Promise { const presentationDefs = args.verifiedAuthorizationRequest.presentationDefinitions const matchedPresentationWithPresentationDefinition = presentationDefs && presentationDefs.length > 0 - ? await this.matchPresentationDefinitions(presentationDefs, args.verifiableCredentials, presentationSignCallback, args.signingOptions) + ? await this.matchPresentationDefinitions(presentationDefs, args.verifiableCredentials, args.presentationSignCallback, args.signingOptions) : [] const didResolutionResult = args.verifiedAuthorizationRequest.didResolutionResult diff --git a/packages/did-auth-siop-op-authenticator/src/types/IDidAuthSiopOpAuthenticator.ts b/packages/did-auth-siop-op-authenticator/src/types/IDidAuthSiopOpAuthenticator.ts index 906baf4a5..09a6b4c65 100644 --- a/packages/did-auth-siop-op-authenticator/src/types/IDidAuthSiopOpAuthenticator.ts +++ b/packages/did-auth-siop-op-authenticator/src/types/IDidAuthSiopOpAuthenticator.ts @@ -1,4 +1,13 @@ -import { DIDDocumentSection, IAgentContext, IIdentifier, IPluginMethodMap, IResolver, IKeyManager } from '@veramo/core' +import { + DIDDocumentSection, + IAgentContext, + IIdentifier, + IPluginMethodMap, + IResolver, + IKeyManager, + IDataStoreORM, + FindCredentialsArgs, +} from '@veramo/core' import { IPresentation, IVerifiableCredential } from '@sphereon/ssi-types' import { OpSession } from '../session/OpSession' import { @@ -56,7 +65,7 @@ export interface IGetSiopAuthorizationRequestFromRpArgs { export interface IGetSiopAuthorizationRequestDetailsArgs { sessionId: string verifiedAuthorizationRequest: VerifiedAuthorizationRequest - verifiableCredentials: IVerifiableCredential[] + credentialFilter?: FindCredentialsArgs signingOptions?: { nonce?: string domain?: string @@ -133,7 +142,7 @@ export interface IOpsGetSiopAuthorizationRequestDetailsArgs { nonce?: string domain?: string } - presentationSignCallback?: PresentationSignCallback + presentationSignCallback: PresentationSignCallback } export interface IOpsVerifySiopAuthorizationRequestUriArgs { @@ -149,4 +158,4 @@ export enum events { DID_SIOP_AUTHENTICATED = 'didSiopAuthenticated', } -export type IRequiredContext = IAgentContext +export type IRequiredContext = IAgentContext diff --git a/packages/ssi-types/__tests__/encoding.test.ts b/packages/ssi-types/__tests__/encoding.test.ts index 2b69b5cd8..ad70e2298 100644 --- a/packages/ssi-types/__tests__/encoding.test.ts +++ b/packages/ssi-types/__tests__/encoding.test.ts @@ -21,7 +21,7 @@ describe('Encoding - Decoding', () => { const jwtVp: OriginalVerifiablePresentation = getFile('packages/ssi-types/__tests__/vc_vp_examples/vp/vp_universityDegree.jwt') const jwtVc: OriginalVerifiableCredential = getFile('packages/ssi-types/__tests__/vc_vp_examples/vc/vc_universityDegree.jwt') const ldpVp: OriginalVerifiablePresentation = getFileAsJson('packages/ssi-types/__tests__/vc_vp_examples/vp/vp_general.json') - const ldpVc: OriginalVerifiableCredential = getFileAsJson('packages/ssi-types/__tests__/vc_vp_examples/vc/vc-driverLicense.json') + const ldpVc: OriginalVerifiableCredential = getFileAsJson('packages/ssi-types/__tests__/vc_vp_examples/vc/vc_driverLicense.json') const decodedJwtVp = CredentialMapper.decodeVerifiablePresentation(jwtVp) as JwtDecodedVerifiablePresentation const decodedJwtVc = CredentialMapper.decodeVerifiableCredential(jwtVc) as JwtDecodedVerifiableCredential diff --git a/packages/ssi-types/__tests__/internal.test.ts b/packages/ssi-types/__tests__/internal.test.ts new file mode 100644 index 000000000..1bd04300c --- /dev/null +++ b/packages/ssi-types/__tests__/internal.test.ts @@ -0,0 +1,72 @@ +import * as fs from 'fs' +import { CredentialMapper, IVerifiableCredential } from '../src' +import { VerifiableCredential } from '@veramo/core' + +function getFile(path: string) { + return fs.readFileSync(path, 'utf-8') +} + +function getFileAsJson(path: string) { + return JSON.parse(getFile(path)) +} + +describe('Internal', () => { + it('Should set type to VerifiableCredential when none is present', () => { + const internalVerifiableCredential: VerifiableCredential = getFileAsJson('./packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json') + delete internalVerifiableCredential.type + const externalVerifiableCredential: IVerifiableCredential = CredentialMapper.toExternalVerifiableCredential(internalVerifiableCredential) + + expect(externalVerifiableCredential.type).toEqual(['VerifiableCredential']) + }) + + it('Should set correct type when type is present', () => { + const internalVerifiableCredential: VerifiableCredential = getFileAsJson('./packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json') + const externalVerifiableCredential: IVerifiableCredential = CredentialMapper.toExternalVerifiableCredential(internalVerifiableCredential) + + expect(externalVerifiableCredential.type).toEqual(['VerifiableCredential', 'PermanentResidentCard']) + }) + + it('Should set type array when type is a string', () => { + const internalVerifiableCredential: VerifiableCredential = getFileAsJson('./packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json') + internalVerifiableCredential.type = 'VerifiableCredential' + const externalVerifiableCredential: IVerifiableCredential = CredentialMapper.toExternalVerifiableCredential(internalVerifiableCredential) + + expect(externalVerifiableCredential.type).toEqual(['VerifiableCredential']) + }) + + it('Should throw error when proof type is not present', () => { + const internalVerifiableCredential: VerifiableCredential = getFileAsJson('./packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json') + delete internalVerifiableCredential.proof.type + + expect(function () { + CredentialMapper.toExternalVerifiableCredential(internalVerifiableCredential) + }).toThrow('Verifiable credential proof is missing a type') + }) + + it('Should throw error when proof created date is not present', () => { + const internalVerifiableCredential: VerifiableCredential = getFileAsJson('./packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json') + delete internalVerifiableCredential.proof.created + + expect(function () { + CredentialMapper.toExternalVerifiableCredential(internalVerifiableCredential) + }).toThrow('Verifiable credential proof is missing a created date') + }) + + it('Should throw error when proof purpose is not present', () => { + const internalVerifiableCredential: VerifiableCredential = getFileAsJson('./packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json') + delete internalVerifiableCredential.proof.proofPurpose + + expect(function () { + CredentialMapper.toExternalVerifiableCredential(internalVerifiableCredential) + }).toThrow('Verifiable credential proof is missing a proof purpose') + }) + + it('Should throw error when proof verification method is not present', () => { + const internalVerifiableCredential: VerifiableCredential = getFileAsJson('./packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json') + delete internalVerifiableCredential.proof.verificationMethod + + expect(function () { + CredentialMapper.toExternalVerifiableCredential(internalVerifiableCredential) + }).toThrow('Verifiable credential proof is missing a verification method') + }) +}) diff --git a/packages/ssi-types/__tests__/vc_vp_examples/vc/vc-driverLicense.json b/packages/ssi-types/__tests__/vc_vp_examples/vc/vc_driverLicense.json similarity index 100% rename from packages/ssi-types/__tests__/vc_vp_examples/vc/vc-driverLicense.json rename to packages/ssi-types/__tests__/vc_vp_examples/vc/vc_driverLicense.json diff --git a/packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json b/packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json new file mode 100644 index 000000000..9bf1e9138 --- /dev/null +++ b/packages/ssi-types/__tests__/vc_vp_examples/vc/vc_internal.json @@ -0,0 +1,33 @@ +{ + "description": "Government of Example Permanent Resident Card.", + "expirationDate": "2029-12-03T12:19:52Z", + "issuanceDate": "2019-12-03T12:19:52Z", + "id": "https://issuer.oidp.uscis.gov/credentials/83627465", + "name": "Permanent Resident Card", + "identifier": "83627465", + "credentialSubject": { + "birthDate": "1958-07-17", + "lprCategory": "C09", + "lprNumber": "999-999-999", + "image": "data:image/png;base64,iVBORw0KGgokJggg==", + "type": ["PermanentResident", "Person"], + "commuterClassification": "C1", + "familyName": "SMITH", + "id": "did:example:b34ca6cd37bbf23", + "givenName": "JANE", + "gender": "Female", + "residentSince": "2015-01-01", + "birthCountry": "Bahamas" + }, + "type": ["VerifiableCredential", "PermanentResidentCard"], + "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/suites/ed25519-2020/v1", "https://w3id.org/citizenship/v1"], + "issuer": "did:key:z6MkuDyqwjCVhFFQEZdS5utguwYD2KRig2PEb9qbfP9iqwn9", + "proof": { + "type": "BbsBlsSignatureProof2020", + "created": "2020-04-25", + "verificationMethod": "did:example:489398593#test", + "proofPurpose": "assertionMethod", + "proofValue": "kTTbA3pmDa6Qia/JkOnIXDLmoBz3vsi7L5t3DWySI/VLmBqleJ/Tbus5RoyiDERDBEh5rnACXlnOqJ/U8yFQFtcp/mBCc2FtKNPHae9jKIv1dm9K9QK1F3GI1AwyGoUfjLWrkGDObO1ouNAhpEd0+et+qiOf2j8p3MTTtRRx4Hgjcl0jXCq7C7R5/nLpgimHAAAAdAx4ouhMk7v9dXijCIMaG0deicn6fLoq3GcNHuH5X1j22LU/hDu7vvPnk/6JLkZ1xQAAAAIPd1tu598L/K3NSy0zOy6obaojEnaqc1R5Ih/6ZZgfEln2a6tuUp4wePExI1DGHqwj3j2lKg31a/6bSs7SMecHBQdgIYHnBmCYGNQnu/LZ9TFV56tBXY6YOWZgFzgLDrApnrFpixEACM9rwrJ5ORtxAAAAAgE4gUIIC9aHyJNa5TBklMOh6lvQkMVLXa/vEl+3NCLXblxjgpM7UEMqBkE9/QcoD3Tgmy+z0hN+4eky1RnJsEg=", + "nonce": "6i3dTz5yFfWJ8zgsamuyZa4yAHPm75tUOOXddR6krCvCYk77sbCOuEVcdBCDd/l6tIY=" + } +} diff --git a/packages/ssi-types/package.json b/packages/ssi-types/package.json index 10e2698d1..b77d5673f 100644 --- a/packages/ssi-types/package.json +++ b/packages/ssi-types/package.json @@ -11,6 +11,7 @@ "jwt-decode": "^3.1.2" }, "devDependencies": { + "@veramo/core": "4.2.0", "typescript": "4.6.4", "@types/jest": "^27.0.2", "jest": "^27.3.1", diff --git a/packages/ssi-types/src/mapper/credential-mapper.ts b/packages/ssi-types/src/mapper/credential-mapper.ts index b4a158be2..078f70754 100644 --- a/packages/ssi-types/src/mapper/credential-mapper.ts +++ b/packages/ssi-types/src/mapper/credential-mapper.ts @@ -14,6 +14,7 @@ import { } from '../types' import jwt_decode from 'jwt-decode' import { ObjectUtils } from '../utils' +import { VerifiableCredential } from '@veramo/core' export class CredentialMapper { static decodeVerifiablePresentation(presentation: OriginalVerifiablePresentation): JwtDecodedVerifiablePresentation | IVerifiablePresentation { @@ -272,4 +273,38 @@ export class CredentialMapper { } return credential } + + static toExternalVerifiableCredential(verifiableCredential: VerifiableCredential): IVerifiableCredential { + if (!verifiableCredential.proof.type) { + throw new Error('Verifiable credential proof is missing a type') + } + + if (!verifiableCredential.proof.created) { + throw new Error('Verifiable credential proof is missing a created date') + } + + if (!verifiableCredential.proof.proofPurpose) { + throw new Error('Verifiable credential proof is missing a proof purpose') + } + + if (!verifiableCredential.proof.verificationMethod) { + throw new Error('Verifiable credential proof is missing a verification method') + } + + return { + ...verifiableCredential, + type: verifiableCredential.type + ? typeof verifiableCredential.type === 'string' + ? [verifiableCredential.type] + : verifiableCredential.type + : ['VerifiableCredential'], + proof: { + ...verifiableCredential.proof, + type: verifiableCredential.proof.type, + created: verifiableCredential.proof.created, + proofPurpose: verifiableCredential.proof.proofPurpose, + verificationMethod: verifiableCredential.proof.verificationMethod, + }, + } + } }