From 003929800f5310cf88af8618f3a19a41c05e859e Mon Sep 17 00:00:00 2001 From: Josh B <30708316+joshbax189@users.noreply.github.com> Date: Tue, 23 Nov 2021 01:41:59 -0800 Subject: [PATCH] fix: normalizeCredential overwrite of "evidence" when "vc" is missing (#86) --- src/__tests__/converters.test.ts | 23 ++++++++++++++++++++- src/converters.ts | 34 ++++++++++++++++---------------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/__tests__/converters.test.ts b/src/__tests__/converters.test.ts index 3beb49c..20737a8 100644 --- a/src/__tests__/converters.test.ts +++ b/src/__tests__/converters.test.ts @@ -436,6 +436,27 @@ describe('credential', () => { expect(result).toMatchObject({ evidence: 'foo', vc: { evidence: 'foo' } }) }) + it('does not insert evidence when using credentialSubject instead of vc object', () => { + const result = normalizeCredential({ + credentialSubject: { + id: 'did:example:ebfeb1f712ebc6f1c276e12ec21', + }, + }) + expect(result).not.toHaveProperty('evidence') + }) + + it('does not overwrite evidence when passed at top-level', () => { + const result = normalizeCredential({ + credentialSubject: { + id: 'did:example:ebfeb1f712ebc6f1c276e12ec21', + }, + evidence: { + foo: 'bar', + }, + }) + expect(result.evidence).toMatchObject({ foo: 'bar' }) + }) + it('uses credentialStatus from vc', () => { const result = normalizeCredential({ vc: { credentialStatus: 'foo' } }) expect(result).toMatchObject({ credentialStatus: 'foo' }) @@ -957,7 +978,7 @@ describe('credential', () => { }) }) - describe('other fields W3C fields', () => { + describe('other W3C fields', () => { it('maps evidence to vc', () => { const result = transformCredentialInput({ evidence: 'foo' }) expect(result).toMatchObject({ vc: { evidence: 'foo' } }) diff --git a/src/converters.ts b/src/converters.ts index 28c64ab..f888b3f 100644 --- a/src/converters.ts +++ b/src/converters.ts @@ -15,6 +15,14 @@ import { } from './types' import { decodeJWT } from 'did-jwt' +/* + * Additional W3C VC fields: + * These are defined as optional top-level properties in the W3C spec but are not mapped to top-level JWT names, + * so they should be moved inside the "vc" object when transforming to a JWT. + * Conversely, they should be moved out of the "vc" object when transforming from a JWT to W3C JSON. + */ +const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus'] + // eslint-disable-next-line @typescript-eslint/no-explicit-any export function asArray(arg: any | any[]): any[] { return Array.isArray(arg) ? arg : [arg] @@ -111,19 +119,15 @@ function normalizeJwtCredentialPayload( delete result.vc?.type } - result.evidence = input.vc?.evidence - if (removeOriginalFields) { - delete result.vc?.evidence - } - - result.credentialStatus = input.vc?.credentialStatus - if (removeOriginalFields) { - delete result.vc?.credentialStatus - } - - result.termsOfUse = input.vc?.termsOfUse - if (removeOriginalFields) { - delete result.vc?.termsOfUse + for (const prop of additionalPropNames) { + if (input.vc && input.vc[prop]) { + if (!result[prop]) { + result[prop] = input.vc[prop] + } + if (removeOriginalFields) { + delete result.vc[prop] + } + } } const contextArray: string[] = [ @@ -313,10 +317,6 @@ export function transformCredentialInput( delete result.credentialSubject } - // additional W3C VC fields to map: - // these may exist at the top-level of a W3C credential, but should be moved inside vc when transforming to JWT - const additionalPropNames = ['evidence', 'termsOfUse', 'refreshService', 'credentialSchema', 'credentialStatus'] - for (const prop of additionalPropNames) { if (input[prop]) { if (!result.vc[prop]) {