From 172c9088fae4b758b79436d9ae11c8c1f1785341 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Thu, 17 Dec 2020 20:23:24 +0200 Subject: [PATCH] feat: Simpler create VC/VP (#309) --- __tests__/shared/verifiableData.ts | 68 ++++++++++++++++++++++++-- packages/daf-w3c/api/daf-w3c.api.md | 39 +++++++++++++-- packages/daf-w3c/src/action-handler.ts | 58 +++++++++++++++++++--- packages/daf-w3c/src/index.ts | 1 + 4 files changed, 151 insertions(+), 15 deletions(-) diff --git a/__tests__/shared/verifiableData.ts b/__tests__/shared/verifiableData.ts index a18d16f34..0626752c6 100644 --- a/__tests__/shared/verifiableData.ts +++ b/__tests__/shared/verifiableData.ts @@ -28,8 +28,8 @@ export default (testContext: { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiableCredential'], + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://example.com/1/2/3'], + type: ['VerifiableCredential', 'Custom'], issuanceDate: new Date().toISOString(), credentialSubject: { id: 'did:web:example.com', @@ -40,6 +40,11 @@ export default (testContext: { }) expect(verifiableCredential).toHaveProperty('proof.jwt') + expect(verifiableCredential['@context']).toEqual([ + 'https://www.w3.org/2018/credentials/v1', + 'https://example.com/1/2/3', + ]) + expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Custom']) const hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) expect(typeof hash).toEqual('string') @@ -48,6 +53,24 @@ export default (testContext: { expect(verifiableCredential).toEqual(verifiableCredential2) }) + it('should create verifiable credential (simple)', async () => { + const verifiableCredential = await agent.createVerifiableCredential({ + credential: { + issuer: { id: identifier.did }, + credentialSubject: { + id: 'did:web:example.com', + you: 'Rock', + }, + }, + proofFormat: 'jwt', + }) + + expect(verifiableCredential).toHaveProperty('proof.jwt') + expect(verifiableCredential).toHaveProperty('issuanceDate') + expect(verifiableCredential['@context']).toEqual(['https://www.w3.org/2018/credentials/v1']) + expect(verifiableCredential['type']).toEqual(['VerifiableCredential']) + }) + it('should create verifiable presentation', async () => { const verifiableCredential = await agent.createVerifiableCredential({ credential: { @@ -67,8 +90,8 @@ export default (testContext: { presentation: { holder: identifier.did, verifier: [], - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiablePresentation'], + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://example.com/1/2/3'], + type: ['VerifiablePresentation', 'Custom'], issuanceDate: new Date().toISOString(), verifiableCredential: [verifiableCredential], }, @@ -76,6 +99,43 @@ export default (testContext: { }) expect(verifiablePresentation).toHaveProperty('proof.jwt') + expect(verifiablePresentation['@context']).toEqual([ + 'https://www.w3.org/2018/credentials/v1', + 'https://example.com/1/2/3', + ]) + expect(verifiablePresentation['type']).toEqual(['VerifiablePresentation', 'Custom']) + + const hash = await agent.dataStoreSaveVerifiablePresentation({ verifiablePresentation }) + expect(typeof hash).toEqual('string') + + const verifiablePresentation2 = await agent.dataStoreGetVerifiablePresentation({ hash }) + expect(verifiablePresentation).toEqual(verifiablePresentation2) + }) + + it('should create verifiable presentation (simple)', async () => { + const verifiableCredential = await agent.createVerifiableCredential({ + credential: { + issuer: { id: identifier.did }, + credentialSubject: { + id: 'did:web:example.com', + you: 'Rock', + }, + }, + proofFormat: 'jwt', + }) + + const verifiablePresentation = await agent.createVerifiablePresentation({ + presentation: { + holder: identifier.did, + verifier: [], + verifiableCredential: [verifiableCredential], + }, + proofFormat: 'jwt', + }) + + expect(verifiablePresentation).toHaveProperty('proof.jwt') + expect(verifiablePresentation['@context']).toEqual(['https://www.w3.org/2018/credentials/v1']) + expect(verifiablePresentation['type']).toEqual(['VerifiablePresentation']) const hash = await agent.dataStoreSaveVerifiablePresentation({ verifiablePresentation }) expect(typeof hash).toEqual('string') diff --git a/packages/daf-w3c/api/daf-w3c.api.md b/packages/daf-w3c/api/daf-w3c.api.md index 4164b9d55..01158e282 100644 --- a/packages/daf-w3c/api/daf-w3c.api.md +++ b/packages/daf-w3c/api/daf-w3c.api.md @@ -15,8 +15,6 @@ import { IResolver } from 'daf-core'; import { Message } from 'daf-message-handler'; import { VerifiableCredential } from 'daf-core'; import { VerifiablePresentation } from 'daf-core'; -import { W3CCredential } from 'daf-core'; -import { W3CPresentation } from 'daf-core'; // @public export class CredentialIssuer implements IAgentPlugin { @@ -30,17 +28,48 @@ export class CredentialIssuer implements IAgentPlugin { readonly schema: any; } +// @public +export type EncodingFormat = 'jwt'; + // @public export interface ICreateVerifiableCredentialArgs { - credential: W3CCredential; - // Warning: (ae-forgotten-export) The symbol "EncodingFormat" needs to be exported by the entry point index.d.ts + credential: { + '@context'?: string[]; + id?: string; + type?: string[]; + issuer: { + id: string; + [x: string]: any; + }; + issuanceDate?: string; + expirationDate?: string; + credentialSubject: { + id?: string; + [x: string]: any; + }; + credentialStatus?: { + id: string; + type: string; + }; + [x: string]: any; + }; proofFormat: EncodingFormat; save?: boolean; } // @public export interface ICreateVerifiablePresentationArgs { - presentation: W3CPresentation; + presentation: { + id?: string; + holder: string; + issuanceDate?: string; + expirationDate?: string; + '@context'?: string[]; + type?: string[]; + verifier: string[]; + verifiableCredential: VerifiableCredential[]; + [x: string]: any; + }; proofFormat: EncodingFormat; save?: boolean; } diff --git a/packages/daf-w3c/src/action-handler.ts b/packages/daf-w3c/src/action-handler.ts index 4c395ea68..39f663015 100644 --- a/packages/daf-w3c/src/action-handler.ts +++ b/packages/daf-w3c/src/action-handler.ts @@ -46,8 +46,20 @@ export interface ICreateVerifiablePresentationArgs { * * The signer of the Presentation is chosen based on the `holder` property * of the `presentation` + * + * '@context', 'type' and 'issuanceDate' will be added automatically if omitted */ - presentation: W3CPresentation + presentation: { + id?: string + holder: string + issuanceDate?: string + expirationDate?: string + '@context'?: string[] + type?: string[] + verifier: string[] + verifiableCredential: VerifiableCredential[] + [x: string]: any + } /** * If this parameter is true, the resulting VerifiablePresentation is sent to the @@ -75,8 +87,26 @@ export interface ICreateVerifiableCredentialArgs { * * The signer of the Credential is chosen based on the `issuer.id` property * of the `credential` + * + * '@context', 'type' and 'issuanceDate' will be added automatically if omitted */ - credential: W3CCredential + credential: { + '@context'?: string[] + id?: string + type?: string[] + issuer: { id: string; [x: string]: any } + issuanceDate?: string + expirationDate?: string + credentialSubject: { + id?: string + [x: string]: any + } + credentialStatus?: { + id: string + type: string + } + [x: string]: any + } /** * If this parameter is true, the resulting VerifiablePresentation is sent to the @@ -169,15 +199,23 @@ export class CredentialIssuer implements IAgentPlugin { context: IContext, ): Promise { try { + const presentation: W3CPresentation = { + ...args.presentation, + '@context': args.presentation['@context'] || ['https://www.w3.org/2018/credentials/v1'], + //FIXME: make sure 'VerifiablePresentation' is the first element in this array: + type: args.presentation.type || ['VerifiablePresentation'], + issuanceDate: args.presentation.issuanceDate || new Date().toISOString(), + } + //FIXME: if the identifier is not found, the error message should reflect that. - const identifier = await context.agent.didManagerGet({ did: args.presentation.holder }) + const identifier = await context.agent.didManagerGet({ did: presentation.holder }) //FIXME: `args` should allow picking a key or key type const key = identifier.keys.find((k) => k.type === 'Secp256k1') if (!key) throw Error('No signing key for ' + identifier.did) //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` const signer = (data: string) => context.agent.keyManagerSignJWT({ kid: key.kid, data }) debug('Signing VP with', identifier.did) - const jwt = await createVerifiablePresentationJwt(args.presentation, { did: identifier.did, signer }) + const jwt = await createVerifiablePresentationJwt(presentation, { did: identifier.did, signer }) //FIXME: flagging this as a potential privacy leak. debug(jwt) const verifiablePresentation = normalizePresentation(jwt) @@ -197,8 +235,16 @@ export class CredentialIssuer implements IAgentPlugin { context: IContext, ): Promise { try { + const credential: W3CCredential = { + ...args.credential, + '@context': args.credential['@context'] || ['https://www.w3.org/2018/credentials/v1'], + //FIXME: make sure 'VerifiableCredential' is the first element in this array: + type: args.credential.type || ['VerifiableCredential'], + issuanceDate: args.credential.issuanceDate || new Date().toISOString(), + } + //FIXME: if the identifier is not found, the error message should reflect that. - const identifier = await context.agent.didManagerGet({ did: args.credential.issuer.id }) + const identifier = await context.agent.didManagerGet({ did: credential.issuer.id }) //FIXME: `args` should allow picking a key or key type const key = identifier.keys.find((k) => k.type === 'Secp256k1') if (!key) throw Error('No signing key for ' + identifier.did) @@ -206,7 +252,7 @@ export class CredentialIssuer implements IAgentPlugin { const signer = (data: string) => context.agent.keyManagerSignJWT({ kid: key.kid, data }) debug('Signing VC with', identifier.did) - const jwt = await createVerifiableCredentialJwt(args.credential, { did: identifier.did, signer }) + const jwt = await createVerifiableCredentialJwt(credential, { did: identifier.did, signer }) //FIXME: flagging this as a potential privacy leak. debug(jwt) const verifiableCredential = normalizeCredential(jwt) diff --git a/packages/daf-w3c/src/index.ts b/packages/daf-w3c/src/index.ts index 100e60570..75214df84 100644 --- a/packages/daf-w3c/src/index.ts +++ b/packages/daf-w3c/src/index.ts @@ -9,6 +9,7 @@ export { ICredentialIssuer, ICreateVerifiableCredentialArgs, ICreateVerifiablePresentationArgs, + EncodingFormat, } from './action-handler' const schema = require('../plugin.schema.json') export { schema }