From c43748c245dfbbbaaba84a56a3ee8e85c0efd23c Mon Sep 17 00:00:00 2001 From: Neal Date: Mon, 7 Aug 2023 14:20:00 -0700 Subject: [PATCH] Added Credential Manifest Types --- packages/credentials/src/types.ts | 131 ++++++++++++++- .../tests/credentialmanifest.spec.ts | 154 ++++++++++++++++++ ...e.spec.ts => presentationexchange.spec.ts} | 2 +- 3 files changed, 279 insertions(+), 8 deletions(-) create mode 100644 packages/credentials/tests/credentialmanifest.spec.ts rename packages/credentials/tests/{pe.spec.ts => presentationexchange.spec.ts} (99%) diff --git a/packages/credentials/src/types.ts b/packages/credentials/src/types.ts index de697f0cf..4f555a665 100644 --- a/packages/credentials/src/types.ts +++ b/packages/credentials/src/types.ts @@ -1,5 +1,5 @@ -import type { ICredential, IIssuer, ICredentialSubject, ICredentialSchemaType } from '@sphereon/ssi-types'; -import type { PresentationDefinitionV2 } from '@sphereon/pex-models'; +import type { ICredential, IIssuer, ICredentialSubject, ICredentialSchemaType, ICredentialStatus} from '@sphereon/ssi-types'; +import type { PresentationDefinitionV2, InputDescriptorV2 } from '@sphereon/pex-models'; import type { IPresentation, PresentationSubmission as PexPresentationSubmission, Descriptor, JwtDecodedVerifiableCredential as PexJwtDecodedVc, JwtDecodedVerifiablePresentation as PexJwtDecodedPres } from '@sphereon/ssi-types'; import type { PresentationResult as PexPR } from '@sphereon/pex/dist/main/lib/signing'; @@ -7,10 +7,13 @@ import { PEXv2, EvaluationResults as ER } from '@sphereon/pex'; const pex = new PEXv2(); + +/** Presentation Exchange */ + /** * A Verifiable Credential is a set of one or more claims made by the same entity. * - * @see {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model} + * @see {@link https://www.w3.org/TR/vc-data-model/#credentials | VC Data Model} */ export type VerifiableCredential = ICredential; @@ -22,14 +25,14 @@ export type JwtDecodedVerifiableCredential = PexJwtDecodedVc; /** * Credential Schema Types are useful when enforcing a specific structure on a given collection of data. * - * @see {@link https://www.w3.org/TR/vc-data-model/#data-schemas | Data schemas} + * @see {@link https://www.w3.org/TR/vc-data-model/#data-schemas | Data Schemas} */ export type CredentialSchemaType = ICredentialSchemaType; /** - * Issuer: The acting Entity issuing a Verifiable Credential. + * Issuer: The acting Entity issuing a Verifiable Credential. The value of the issuer property must be either a URI or an object containing an `id` property. * - * @see {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model} + * @see {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer Data Model} */ export type Issuer = IIssuer; @@ -40,6 +43,14 @@ export type Issuer = IIssuer; */ export type CredentialSubject = ICredentialSubject; +/** + * Used for the discovery of information about the current status of a verifiable credential, such as whether it is + * suspended or revoked. + * + * @see {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status} + */ +export type CredentialStatus = ICredentialStatus; + /** * Presentation Definition: Outlines the requirements Verifiers have for Proofs. * @@ -103,4 +114,110 @@ export const evaluatePresentation = (presentationDefinition: PresentationDefinit */ export const presentationFrom = (presentationDefinition: PresentationDefinitionV2, verifiableCredentials: string[]): PresentationResult => { return pex.presentationFrom(presentationDefinition, verifiableCredentials); -}; \ No newline at end of file +}; + + +/** Credential Manifest */ + +/** + * Input Descriptors are objects used to describe the information a Verifier requires of a Holder. All Input Descriptors MUST be satisfied, unless otherwise specified by a Feature. + * + * See {@link https://identity.foundation/presentation-exchange/#input-descriptor-object | Input Descriptor} + */ +export type InputDescriptor = InputDescriptorV2 + +/** + * See {@link https://identity.foundation/wallet-rendering/v0.0.1/#entity-styles | Entity Styles} + */ +export type EntityStyle = { + thumbnail?: Image + hero?: Image + background?: Colorable + text?: Colorable +} + +export type Image = { + /** Valid URI string to an image resource */ + uri: string; + /** String that describes the alternate text for the image */ + alt?: string; +} + +export type Colorable = { + /** HEX string color value */ + color?: string +} + +/** + * See {@link https://identity.foundation/credential-manifest/#output-descriptor | Output Descriptor} + */ +export type OutputDescriptor = { + /** String that does not conflict with the `id` of another OutputDescriptor in the same CredentialManifest */ + id: string; + /** String specifying the schema of the credential to be issued */ + schema: string; + /** Human-readable string that describes what the credential represents */ + name?: string; + /** Human-readable string that descripbes what the credential is in greater detail */ + description?: string; + /** Object or URI of the {@link https://identity.foundation/wallet-rendering/v0.0.1/#entity-styles | Entity Style} to render the OutputDescriptor */ + styles?: EntityStyle | string; + /** Object or URI of the {@link https://identity.foundation/wallet-rendering/v0.0.1/#display-mapping-object | Display Mapping} used to pull data from the target Claim */ + display?: DisplayMapping | string; +} + +/** See {@link https://identity.foundation/wallet-rendering/v0.0.1/#display-mapping-object | Display Mapping Object} */ +export type DisplayMapping = { + /** Array of JSONPath string expressions */ + path: string[]; + schema: { + /** Represents the type of data found with the `path` property */ + type: 'string' | 'boolean' | 'number' | 'integer'; + /** If the `type` property is "string", this property is used to format the string in any rendered UI */ + format?: 'date-time' | 'time' | 'date' | 'email' | 'idn-email' | 'hostname' | 'idn-hostname' | + 'ipv4' | 'ipv6' | 'uri' | 'uri-reference' | 'iri' | 'iri-reference'; + } + /** + * String to be rendered into the UI if all the `path` property's item's value is + * undefined OR incorrectly processed + */ + fallback?: string; +} + +/** See {@link https://identity.foundation/presentation-exchange/#presentation-definition | Presentation Definiton}'s `format` property */ +export type Format = { + [key: string]: any; +} + +/** See {@link https://identity.foundation/presentation-exchange/#input-descriptor-object | Input Descriptor}'s `constraints.fields.filter` property */ +export type Filter = { + [key: string]: any; +} + +/** + * Credential Manifests are a resource format that defines preconditional requirements, Issuer style preferences, and other facets User Agents + * utilize to help articulate and select the inputs necessary for processing and issuance of a specified credential. + * + * See {@link https://identity.foundation/credential-manifest/#credential-manifest | Credential Manifest} + */ +export type CredentialManifest = { + /** String providing a unique identifier for the desired context */ + id: string; + /** String that acts as a summarizing title for the CredentialManifest */ + name?: string; + /** + * String explaining what the CredentialManifest is generally offering for meeting + * its requirements + */ + description?: string; + spec_version?: string; + issuer: Issuer; + /** Output Descriptors are used by an Issuer to describe the credentials they are offering to a Holder. See Output Descriptor */ + output_descriptors: OutputDescriptor[]; + format?: Format + /** + * Presentation Exchange is a specification codifying a Presentation Definition data format Verifiers can use to articulate proof + * requirements in a Presentation Request, and a Presentation Submission data format Holders can use to describe proofs submitted in accordance with them. + */ + presentation_definition?: PresentationDefinition; +} \ No newline at end of file diff --git a/packages/credentials/tests/credentialmanifest.spec.ts b/packages/credentials/tests/credentialmanifest.spec.ts new file mode 100644 index 000000000..7a506de90 --- /dev/null +++ b/packages/credentials/tests/credentialmanifest.spec.ts @@ -0,0 +1,154 @@ +import { expect } from 'chai'; +import { Issuer, CredentialManifest, OutputDescriptor, InputDescriptor } from '../src/types.js'; + +describe('Credential Manifest Types', () => { + it('creates an Output Descriptor', () => { + const outputDescriptor: OutputDescriptor = { + id : 'example-id', + schema : 'example-schema', + name : 'Output Descriptor Name', + description : 'Output Descriptor Description', + styles : { + thumbnail: { + uri : 'https://example.com/image.png', + alt : 'image' + }, + background: { + color: '#ffffff' + } + }, + display: { + path : ['$.example'], + schema : { + type : 'string', + format : 'date-time' + }, + fallback: 'N/A' + } + }; + + expect(outputDescriptor).to.have.property('id'); + expect(outputDescriptor).to.have.property('schema'); + expect(outputDescriptor).to.have.property('name'); + expect(outputDescriptor).to.have.property('description'); + expect(outputDescriptor).to.have.property('styles'); + expect(outputDescriptor.styles).to.have.property('thumbnail'); + expect(outputDescriptor.styles).to.have.property('background'); + expect(outputDescriptor).to.have.property('display'); + expect(outputDescriptor.display).to.have.property('path'); + expect(outputDescriptor.display).to.have.property('schema'); + expect(outputDescriptor.display).to.have.property('fallback'); + }); + + it('creates a Credential Manifest', () => { + const issuer: Issuer = { + id: 'did:example:123456' + }; + + const inputDescriptor: InputDescriptor = { + id : 'input-example', + name : 'Input Name', + purpose : 'Input Purpose', + group : ['group1'], + }; + + const credentialManifest: CredentialManifest = { + id : 'manifest-id', + name : 'Credential Manifest Name', + description : 'Credential Manifest Description', + spec_version : '1.0.0', + issuer : issuer, + output_descriptors : [{ + id : 'output-example', + schema : 'schema-example' + }], + format : { key: 'value' }, + presentation_definition : { + id : 'pd-id', + input_descriptors : [ + inputDescriptor + ] + } + }; + + expect(credentialManifest).to.have.property('id'); + expect(credentialManifest).to.have.property('name'); + expect(credentialManifest).to.have.property('description'); + expect(credentialManifest).to.have.property('spec_version'); + expect(credentialManifest).to.have.property('issuer'); + expect(credentialManifest).to.have.property('output_descriptors'); + expect(credentialManifest.output_descriptors[0]).to.have.property('id'); + expect(credentialManifest.output_descriptors[0]).to.have.property('schema'); + expect(credentialManifest).to.have.property('format'); + expect(credentialManifest.format).to.have.property('key'); + expect(credentialManifest).to.have.property('presentation_definition'); + expect(credentialManifest.presentation_definition).to.have.property('id'); + expect(credentialManifest.presentation_definition).to.have.property('input_descriptors'); + expect(credentialManifest.presentation_definition!.input_descriptors[0]).to.have.property('id'); + }); + + it('creates a Credential Manifest with a single Input Descriptor', () => { + const outputDescriptor: OutputDescriptor = { + 'id' : 'driver_license_output', + 'schema' : 'https://schema.org/EducationalOccupationalCredential', + 'name' : 'Washington State Driver License', + 'description' : 'License to operate a vehicle with a gross combined weight rating (GCWR) of 26,001 or more pounds, as long as the GVWR of the vehicle(s) being towed is over 10,000 pounds.', + 'styles' : { + 'thumbnail': { + 'uri' : 'https://dol.wa.com/logo.png', + 'alt' : 'Washington State Seal' + }, + 'hero': { + 'uri' : 'https://dol.wa.com/happy-people-driving.png', + 'alt' : 'Happy people driving' + }, + 'background': { + 'color': '#ff0000' + }, + 'text': { + 'color': '#d4d400' + } + }, + 'display': { + 'path' : ['$.name', '$.vc.name'], + 'schema' : { + 'type': 'string' + }, + 'fallback': 'Washington State Driver License' + } + }; + + const credentialManifest: CredentialManifest = { + 'id' : 'WA-DL-CLASS-A', + 'spec_version' : 'https://identity.foundation/credential-manifest/spec/v1.0.0/', + 'issuer' : { + 'id' : 'did:example:123?linked-domains=3', + 'name' : 'Washington State Government', + 'styles' : { + 'thumbnail': { + 'uri' : 'https://dol.wa.com/logo.png', + 'alt' : 'Washington State Seal' + }, + 'hero': { + 'uri' : 'https://dol.wa.com/people-working.png', + 'alt' : 'People working on serious things' + }, + 'background': { + 'color': '#ff0000' + }, + 'text': { + 'color': '#d4d400' + } + } + }, + 'output_descriptors': [ + outputDescriptor + ] + }; + + expect(credentialManifest).to.have.property('id'); + expect(credentialManifest).to.have.property('spec_version'); + expect(credentialManifest).to.have.property('issuer'); + expect(credentialManifest).to.have.property('output_descriptors'); + }); +}); diff --git a/packages/credentials/tests/pe.spec.ts b/packages/credentials/tests/presentationexchange.spec.ts similarity index 99% rename from packages/credentials/tests/pe.spec.ts rename to packages/credentials/tests/presentationexchange.spec.ts index 366666a56..4ed3c5c2a 100644 --- a/packages/credentials/tests/pe.spec.ts +++ b/packages/credentials/tests/presentationexchange.spec.ts @@ -18,7 +18,7 @@ if (!globalThis.crypto) globalThis.crypto = webcrypto; let testAgent: TestAgent; -describe('PresentationExchange', () => { +describe('Presentation Exchange Types', () => { before(async () => { testAgent = await TestAgent.create(); });