From da7786d4ae8b2e5ed4294122962190d68658406b Mon Sep 17 00:00:00 2001 From: nitro-neal <5314059+nitro-neal@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:20:36 -0600 Subject: [PATCH] adjusting vc.create (#311) * adjusting vc.create * fix spec --- .web5-spec/credentials.ts | 8 +- .../credentials/src/verifiable-credential.ts | 48 +++++++--- .../tests/presentation-exchange.spec.ts | 36 +++---- .../tests/verifiable-credential.spec.ts | 96 +++++++++---------- 4 files changed, 106 insertions(+), 82 deletions(-) diff --git a/.web5-spec/credentials.ts b/.web5-spec/credentials.ts index 39908fb4a..04449ce01 100644 --- a/.web5-spec/credentials.ts +++ b/.web5-spec/credentials.ts @@ -34,7 +34,13 @@ export async function credentialIssue(req: Request, res: Response) { signer : signer }; - const vc: VerifiableCredential = VerifiableCredential.create(body.credential.type[body.credential.type.length - 1], body.credential.issuer, subjectIssuerDid, body.credential.credentialSubject); + const vc: VerifiableCredential = VerifiableCredential.create({ + type: body.credential.type[body.credential.type.length - 1], + issuer: body.credential.issuer, + subject: subjectIssuerDid, + data: body.credential.credentialSubject + }); + const vcJwt: string = await vc.sign(signOptions); const resp: paths["/credentials/issue"]["post"]["responses"]["200"]["content"]["application/json"] = diff --git a/packages/credentials/src/verifiable-credential.ts b/packages/credentials/src/verifiable-credential.ts index 2da6086bf..0fc754f5d 100644 --- a/packages/credentials/src/verifiable-credential.ts +++ b/packages/credentials/src/verifiable-credential.ts @@ -21,6 +21,24 @@ export const DEFAULT_VC_TYPE = 'VerifiableCredential'; */ export type VcDataModel = ICredential; +/** + * @param type Optional. The type of the credential, can be a string or an array of strings. + * @param issuer The issuer URI of the credential, as a string. + * @param subject The subject URI of the credential, as a string. + * @param data The credential data, as a generic type any. + * @param issuanceDate Optional. The issuance date of the credential, as a string. + * Defaults to the current date if not specified. + * @param expirationDate Optional. The expiration date of the credential, as a string. + */ +export type VerifiableCredentialCreateOptions = { + type?: string | string[]; + issuer: string; + subject: string; + data: any; + issuanceDate?: string; + expirationDate?: string; +}; + export type SignOptions = { kid: string; issuerDid: string; @@ -113,24 +131,22 @@ export class VerifiableCredential { /** * Create a [VerifiableCredential] based on the provided parameters. * - * @param type The type of the credential, as a [String]. - * @param issuer The issuer URI of the credential, as a [String]. - * @param subject The subject URI of the credential, as a [String]. - * @param data The credential data, as a generic type [T]. + * @param vcCreateOptions The options to use when creating the Verifiable Credential. * @return A [VerifiableCredential] instance. * * Example: * ``` - * const vc = VerifiableCredential.create("ExampleCredential", "http://example.com/issuers/1", "http://example.com/subjects/1", myData) + * const vc = VerifiableCredential.create({ + * type: 'StreetCredibility', + * issuer: 'did:ex:issuer', + * subject: 'did:ex:subject', + * data: { 'arbitrary': 'data' } + * }) * ``` */ - public static create( - type: string, - issuer: string, - subject: string, - data: T, - expirationDate?: string - ): VerifiableCredential { + public static create(vcCreateOptions: VerifiableCredentialCreateOptions): VerifiableCredential { + const { type, issuer, subject, data, issuanceDate, expirationDate } = vcCreateOptions; + const jsonData = JSON.parse(JSON.stringify(data)); if (typeof jsonData !== 'object') { @@ -147,11 +163,13 @@ export class VerifiableCredential { }; const vcDataModel: VcDataModel = { - '@context' : [DEFAULT_CONTEXT], - type : [DEFAULT_VC_TYPE, type], + '@context' : [DEFAULT_CONTEXT], + type : Array.isArray(type) + ? [DEFAULT_VC_TYPE, ...type] + : (type ? [DEFAULT_VC_TYPE, type] : [DEFAULT_VC_TYPE]), id : `urn:uuid:${uuidv4()}`, issuer : issuer, - issuanceDate : getCurrentXmlSchema112Timestamp(), + issuanceDate : issuanceDate || getCurrentXmlSchema112Timestamp(), // use default if undefined credentialSubject : credentialSubject, ...(expirationDate && { expirationDate }), // optional property }; diff --git a/packages/credentials/tests/presentation-exchange.spec.ts b/packages/credentials/tests/presentation-exchange.spec.ts index 881614b85..5e923a323 100644 --- a/packages/credentials/tests/presentation-exchange.spec.ts +++ b/packages/credentials/tests/presentation-exchange.spec.ts @@ -36,12 +36,12 @@ describe('PresentationExchange', () => { signer : signer }; - const vc = VerifiableCredential.create( - 'StreetCred', - alice.did, - alice.did, - new BitcoinCredential('btcAddress123'), - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : alice.did, + subject : alice.did, + data : new BitcoinCredential('btcAddress123'), + }); btcCredentialJwt = await vc.sign(signOptions); presentationDefinition = createPresentationDefinition(); @@ -57,12 +57,12 @@ describe('PresentationExchange', () => { }); it('should return the only one verifiable credential', async () => { - const vc = VerifiableCredential.create( - 'StreetCred', - signOptions.issuerDid, - signOptions.subjectDid, - new OtherCredential('otherstuff'), - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : signOptions.issuerDid, + subject : signOptions.subjectDid, + data : new OtherCredential('otherstuff'), + }); const otherCredJwt = await vc.sign(signOptions); @@ -127,12 +127,12 @@ describe('PresentationExchange', () => { }); it('should fail to create a presentation with vc that does not match presentation definition', async() => { - const vc = VerifiableCredential.create( - 'StreetCred', - signOptions.issuerDid, - signOptions.subjectDid, - new OtherCredential('otherstuff'), - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : signOptions.issuerDid, + subject : signOptions.subjectDid, + data : new OtherCredential('otherstuff'), + }); const otherCredJwt = await vc.sign(signOptions); await expectThrowsAsync(() => PresentationExchange.createPresentationFromCredentials([otherCredJwt], presentationDefinition), 'Failed to create Verifiable Presentation JWT due to: Required Credentials Not Present'); diff --git a/packages/credentials/tests/verifiable-credential.spec.ts b/packages/credentials/tests/verifiable-credential.spec.ts index 13a9476dc..d4f9bfbe0 100644 --- a/packages/credentials/tests/verifiable-credential.spec.ts +++ b/packages/credentials/tests/verifiable-credential.spec.ts @@ -36,12 +36,12 @@ describe('Verifiable Credential Tests', () => { const issuerDid = signOptions.issuerDid; const subjectDid = signOptions.subjectDid; - const vc = VerifiableCredential.create( - 'StreetCred', - issuerDid, - subjectDid, - new StreetCredibility('high', true), - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid, + subject : subjectDid, + data : new StreetCredibility('high', true), + }); expect(vc.issuer).to.equal(issuerDid); expect(vc.subject).to.equal(subjectDid); @@ -57,12 +57,12 @@ describe('Verifiable Credential Tests', () => { const invalidData = 'NotAJSONObject'; expect(() => { - VerifiableCredential.create( - 'InvalidDataTest', - issuerDid, - subjectDid, - invalidData - ); + VerifiableCredential.create({ + type : 'InvalidDataTest', + issuer : issuerDid, + subject : subjectDid, + data : invalidData + }); }).to.throw('Expected data to be parseable into a JSON object'); }); @@ -72,21 +72,21 @@ describe('Verifiable Credential Tests', () => { const validData = new StreetCredibility('high', true); expect(() => { - VerifiableCredential.create( - 'IssuerUndefinedTest', - '', - subjectDid, - validData - ); + VerifiableCredential.create({ + type : 'IssuerUndefinedTest', + issuer : '', + subject : subjectDid, + data : validData + }); }).to.throw('Issuer and subject must be defined'); expect(() => { - VerifiableCredential.create( - 'SubjectUndefinedTest', - issuerDid, - '', - validData - ); + VerifiableCredential.create({ + type : 'SubjectUndefinedTest', + issuer : issuerDid, + subject : '', + data : validData + }); }).to.throw('Issuer and subject must be defined'); }); @@ -95,12 +95,12 @@ describe('Verifiable Credential Tests', () => { const issuerDid = signOptions.issuerDid; const subjectDid = signOptions.subjectDid; - const vc = VerifiableCredential.create( - 'StreetCred', - issuerDid, - subjectDid, - new StreetCredibility('high', true), - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : issuerDid, + subject : subjectDid, + data : new StreetCredibility('high', true), + }); const vcJwt = await vc.sign(signOptions); expect(vcJwt).to.not.be.null; @@ -117,12 +117,12 @@ describe('Verifiable Credential Tests', () => { }); it('verify fails with bad issuer did', async () => { - const vc = VerifiableCredential.create( - 'StreetCred', - 'bad:did: invalidDid', - signOptions.subjectDid, - new StreetCredibility('high', true) - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : 'bad:did: invalidDid', + subject : signOptions.subjectDid, + data : new StreetCredibility('high', true) + }); const badSignOptions = { issuerDid : 'bad:did: invalidDid', @@ -141,12 +141,12 @@ describe('Verifiable Credential Tests', () => { }); it('parseJwt returns an instance of VerifiableCredential on success', async () => { - const vc = VerifiableCredential.create( - 'StreetCred', - signOptions.issuerDid, - signOptions.subjectDid, - new StreetCredibility('high', true) - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : signOptions.issuerDid, + subject : signOptions.subjectDid, + data : new StreetCredibility('high', true), + }); const vcJwt = await vc.sign(signOptions); const parsedVc = VerifiableCredential.parseJwt(vcJwt); @@ -170,12 +170,12 @@ describe('Verifiable Credential Tests', () => { }); it('verify does not throw an exception with vaild vc', async () => { - const vc = VerifiableCredential.create( - 'StreetCred', - signOptions.issuerDid, - signOptions.subjectDid, - new StreetCredibility('high', true) - ); + const vc = VerifiableCredential.create({ + type : 'StreetCred', + issuer : signOptions.issuerDid, + subject : signOptions.subjectDid, + data : new StreetCredibility('high', true), + }); const vcJwt = await vc.sign(signOptions);