diff --git a/packages/app-runtime/src/AppStringProcessor.ts b/packages/app-runtime/src/AppStringProcessor.ts index 167d13381..1f494ae75 100644 --- a/packages/app-runtime/src/AppStringProcessor.ts +++ b/packages/app-runtime/src/AppStringProcessor.ts @@ -2,7 +2,7 @@ import { OpenId4VciResolvedCredentialOffer } from "@credo-ts/openid4vc"; import { ILogger, ILoggerFactory } from "@js-soft/logging-abstractions"; import { Serializable } from "@js-soft/ts-serval"; import { EventBus, Result } from "@js-soft/ts-utils"; -import { VerifiablePresentation } from "@nmshd/content"; +import { TokenContentVerifiablePresentation } from "@nmshd/content"; import { ICoreAddress, Reference } from "@nmshd/core-types"; import { AnonymousServices, DeviceMapper, RuntimeServices } from "@nmshd/runtime"; import { BackboneIds, TokenContentDeviceSharedSecret } from "@nmshd/transport"; @@ -292,7 +292,7 @@ export class AppStringProcessor { case "Token": const tokenContent = this.parseTokenContent(result.value.value.content); - if (tokenContent instanceof VerifiablePresentation) { + if (tokenContent instanceof TokenContentVerifiablePresentation) { // TODO: add technical validation await uiBridge.showVerifiablePresentation(account, result.value.value, true); break; diff --git a/packages/app-runtime/test/runtime/AppStringProcessor.test.ts b/packages/app-runtime/test/runtime/AppStringProcessor.test.ts index a9d005554..8396618dd 100644 --- a/packages/app-runtime/test/runtime/AppStringProcessor.test.ts +++ b/packages/app-runtime/test/runtime/AppStringProcessor.test.ts @@ -1,4 +1,4 @@ -import { ArbitraryRelationshipTemplateContentJSON, AuthenticationRequestItem, RelationshipTemplateContent, VerifiablePresentation } from "@nmshd/content"; +import { ArbitraryRelationshipTemplateContentJSON, AuthenticationRequestItem, RelationshipTemplateContent, TokenContentVerifiablePresentation } from "@nmshd/content"; import { CoreDate, PasswordLocationIndicatorOptions } from "@nmshd/core-types"; import { DeviceOnboardingInfoDTO, PeerRelationshipTemplateLoadedEvent } from "@nmshd/runtime"; import assert from "assert"; @@ -379,7 +379,7 @@ describe("AppStringProcessor", function () { test("get a token with verifiable presentation content using a url", async function () { const tokenResult = await runtime1Session.transportServices.tokens.createOwnToken({ - content: VerifiablePresentation.from({ + content: TokenContentVerifiablePresentation.from({ value: { claim: "test" }, type: "dc+sd-jwt" }).toJSON(), diff --git a/packages/consumption/src/modules/openid4vc/OpenId4VcController.ts b/packages/consumption/src/modules/openid4vc/OpenId4VcController.ts index c20ac2a80..e1de7aa7c 100644 --- a/packages/consumption/src/modules/openid4vc/OpenId4VcController.ts +++ b/packages/consumption/src/modules/openid4vc/OpenId4VcController.ts @@ -1,6 +1,6 @@ import { DcqlValidCredential, W3cJsonCredential } from "@credo-ts/core"; import { OpenId4VciResolvedCredentialOffer, OpenId4VpResolvedAuthorizationRequest } from "@credo-ts/openid4vc"; -import { VerifiableCredential, VerifiablePresentation } from "@nmshd/content"; +import { TokenContentVerifiablePresentation, VerifiableCredential } from "@nmshd/content"; import { ConsumptionBaseController } from "../../consumption/ConsumptionBaseController"; import { ConsumptionController } from "../../consumption/ConsumptionController"; import { ConsumptionControllerName } from "../../consumption/ConsumptionControllerName"; @@ -130,7 +130,7 @@ export class OpenId4VcController extends ConsumptionBaseController { return { status: serverResponse.status, message: serverResponse.body }; } - public async createPresentationTokenContent(credential: VerifiableCredential): Promise { + public async createPresentationTokenContent(credential: VerifiableCredential): Promise { return await this.holder.createPresentationTokenContent(credential); } } diff --git a/packages/consumption/src/modules/openid4vc/local/Holder.ts b/packages/consumption/src/modules/openid4vc/local/Holder.ts index 06e7f9ef7..aa0b6ecb3 100644 --- a/packages/consumption/src/modules/openid4vc/local/Holder.ts +++ b/packages/consumption/src/modules/openid4vc/local/Holder.ts @@ -13,7 +13,7 @@ import { X509Module } from "@credo-ts/core"; import { OpenId4VciCredentialResponse, OpenId4VcModule, type OpenId4VciResolvedCredentialOffer, type OpenId4VpResolvedAuthorizationRequest } from "@credo-ts/openid4vc"; -import { VerifiableCredential, VerifiablePresentation } from "@nmshd/content"; +import { TokenContentVerifiablePresentation, VerifiableCredential } from "@nmshd/content"; import { AccountController } from "@nmshd/transport"; import { AttributesController, OwnIdentityAttribute } from "../../attributes"; import { BaseAgent } from "./BaseAgent"; @@ -204,7 +204,7 @@ export class Holder extends BaseAgent> // hacky solution because credo doesn't support credentials without key binding // TODO: use credentials without key binding once supported - public async createPresentationTokenContent(credential: VerifiableCredential): Promise { + public async createPresentationTokenContent(credential: VerifiableCredential): Promise { if (credential.type !== ClaimFormat.SdJwtDc) throw new Error("Only SD-JWT credentials have been tested so far with token presentation"); const sdJwtVcApi = this.agent.dependencyManager.resolve(SdJwtVcApi); @@ -217,7 +217,7 @@ export class Holder extends BaseAgent> } }); - return VerifiablePresentation.from({ + return TokenContentVerifiablePresentation.from({ value: presentation, type: credential.type, displayInformation: credential.displayInformation diff --git a/packages/content/src/attributes/RelationshipAttributeQuery.ts b/packages/content/src/attributes/RelationshipAttributeQuery.ts index 5d82566d0..18af396dc 100644 --- a/packages/content/src/attributes/RelationshipAttributeQuery.ts +++ b/packages/content/src/attributes/RelationshipAttributeQuery.ts @@ -4,7 +4,7 @@ import { AbstractAttributeQuery, AbstractAttributeQueryJSON, IAbstractAttributeQ import { AttributeValues } from "./AttributeValueTypes"; import { IValueHints, ValueHints, ValueHintsJSON } from "./hints"; import { RelationshipAttributeConfidentiality } from "./RelationshipAttributeConfidentiality"; -import { PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH, PROPRIETARY_ATTRIBUTE_MAX_TITLE_LENGTH } from "./types/proprietary/ProprietaryAttributeValue"; +import { PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH, PROPRIETARY_ATTRIBUTE_MAX_TITLE_LENGTH } from "./types/proprietary"; export interface RelationshipAttributeCreationHintsJSON { title: string; diff --git a/packages/content/src/attributes/types/VerifiableCredential.ts b/packages/content/src/attributes/types/VerifiableCredential.ts index dee12cb09..3f9421641 100644 --- a/packages/content/src/attributes/types/VerifiableCredential.ts +++ b/packages/content/src/attributes/types/VerifiableCredential.ts @@ -1,7 +1,7 @@ import { serialize, type, validate } from "@js-soft/ts-serval"; import { AbstractAttributeValue, AbstractAttributeValueJSON, IAbstractAttributeValue } from "../AbstractAttributeValue"; import { RenderHints, RenderHintsEditType, RenderHintsTechnicalType, ValueHints } from "../hints"; -import { PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH } from "./proprietary/ProprietaryAttributeValue"; +import { PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH } from "./proprietary"; export interface VerifiableCredentialJSON extends AbstractAttributeValueJSON { "@type": "VerifiableCredential"; diff --git a/packages/content/src/attributes/types/VerifiablePresentation.ts b/packages/content/src/attributes/types/VerifiablePresentation.ts deleted file mode 100644 index 9f2f2deff..000000000 --- a/packages/content/src/attributes/types/VerifiablePresentation.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { serialize, type, validate } from "@js-soft/ts-serval"; -import { AbstractAttributeValue, AbstractAttributeValueJSON, IAbstractAttributeValue } from "../AbstractAttributeValue"; -import { RenderHints, RenderHintsEditType, RenderHintsTechnicalType, ValueHints } from "../hints"; -import { PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH } from "./proprietary/ProprietaryAttributeValue"; - -export interface VerifiablePresentationJSON extends AbstractAttributeValueJSON { - "@type": "VerifiablePresentation"; - value: string | Record; - type: string; - displayInformation?: Record[]; -} - -export interface IVerifiablePresentation extends IAbstractAttributeValue { - value: string | Record; - type: string; - displayInformation?: Record[]; -} - -@type("VerifiablePresentation") -export class VerifiablePresentation extends AbstractAttributeValue implements IVerifiablePresentation { - @serialize({ any: true }) - @validate({ customValidator: validateValue }) - public value: string | Record; - - @serialize() - @validate({ nullable: true }) - public type: string; - - @serialize() - @validate({ nullable: true, max: PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH }) - public displayInformation?: Record[]; - - public static get valueHints(): ValueHints { - return ValueHints.from({}); - } - - public static get renderHints(): RenderHints { - return RenderHints.from({ - editType: RenderHintsEditType.TextArea, - technicalType: RenderHintsTechnicalType.Unknown - }); - } - - public static from(value: IVerifiablePresentation | Omit): VerifiablePresentation { - return this.fromAny(value); - } - - public override toJSON(verbose?: boolean | undefined, serializeAsString?: boolean | undefined): VerifiablePresentationJSON { - return super.toJSON(verbose, serializeAsString) as VerifiablePresentationJSON; - } -} - -function validateValue(value: any) { - try { - const string = JSON.stringify(value); - // the length correspondes to 50MB - maybe this needs to be restricted further in the future - if (string.length > 52428800) { - return "stringified value must not be longer than 52428800 characters"; - } - } catch (e) { - if (e instanceof SyntaxError) { - return "must be a valid JSON object"; - } - - return "could not validate value"; - } - - return undefined; -} diff --git a/packages/content/src/attributes/types/index.ts b/packages/content/src/attributes/types/index.ts index 17b345572..e7f261d7d 100644 --- a/packages/content/src/attributes/types/index.ts +++ b/packages/content/src/attributes/types/index.ts @@ -17,4 +17,3 @@ export * from "./relationship"; export * from "./statement"; export * from "./strings"; export * from "./VerifiableCredential"; -export * from "./VerifiablePresentation"; diff --git a/packages/content/src/attributes/types/proprietary/index.ts b/packages/content/src/attributes/types/proprietary/index.ts index 1fda99123..7735ade31 100644 --- a/packages/content/src/attributes/types/proprietary/index.ts +++ b/packages/content/src/attributes/types/proprietary/index.ts @@ -1,3 +1,4 @@ +export * from "./ProprietaryAttributeValue"; export * from "./ProprietaryBoolean"; export * from "./ProprietaryCountry"; export * from "./ProprietaryEMailAddress"; diff --git a/packages/content/src/index.ts b/packages/content/src/index.ts index d77129ba9..2e7996f81 100644 --- a/packages/content/src/index.ts +++ b/packages/content/src/index.ts @@ -5,4 +5,5 @@ export * from "./messages"; export * from "./notifications"; export * from "./relationships"; export * from "./requests"; +export * from "./tokens"; export * from "./ValidationErrorWithoutProperty"; diff --git a/packages/content/src/tokens/TokenContentVerifiablePresentation.ts b/packages/content/src/tokens/TokenContentVerifiablePresentation.ts new file mode 100644 index 000000000..c16ab844c --- /dev/null +++ b/packages/content/src/tokens/TokenContentVerifiablePresentation.ts @@ -0,0 +1,57 @@ +import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; +import { ContentJSON } from "../ContentJSON"; +import { PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH } from "../attributes"; + +export interface TokenContentVerifiablePresentationJSON extends ContentJSON { + "@type": "TokenContentVerifiablePresentation"; + value: string | Record; + type: string; + displayInformation?: Record[]; +} + +export interface ITokenContentVerifiablePresentation extends ISerializable { + value: string | Record; + type: string; + displayInformation?: Record[]; +} + +@type("TokenContentVerifiablePresentation") +export class TokenContentVerifiablePresentation extends Serializable implements ITokenContentVerifiablePresentation { + @serialize({ any: true }) + @validate({ customValidator: validateValue }) + public value: string | Record; + + @serialize() + @validate({ nullable: true }) + public type: string; + + @serialize() + @validate({ nullable: true, max: PROPRIETARY_ATTRIBUTE_MAX_DESCRIPTION_LENGTH }) + public displayInformation?: Record[]; + + public static from(value: ITokenContentVerifiablePresentation | Omit): TokenContentVerifiablePresentation { + return this.fromAny(value); + } + + public override toJSON(verbose?: boolean | undefined, serializeAsString?: boolean | undefined): TokenContentVerifiablePresentationJSON { + return super.toJSON(verbose, serializeAsString) as TokenContentVerifiablePresentationJSON; + } +} + +function validateValue(value: any) { + try { + const string = JSON.stringify(value); + // the length corresponds to 50MB - maybe this needs to be restricted further in the future + if (string.length > 52428800) { + return "stringified value must not be longer than 52428800 characters"; + } + } catch (e) { + if (e instanceof SyntaxError) { + return "must be a valid JSON object"; + } + + return "could not validate value"; + } + + return undefined; +} diff --git a/packages/content/src/tokens/index.ts b/packages/content/src/tokens/index.ts new file mode 100644 index 000000000..8420ddbe0 --- /dev/null +++ b/packages/content/src/tokens/index.ts @@ -0,0 +1 @@ +export * from "./TokenContentVerifiablePresentation"; diff --git a/packages/runtime/test/consumption/openid4vc.test.ts b/packages/runtime/test/consumption/openid4vc.test.ts index 9f0f67462..2a9614fe8 100644 --- a/packages/runtime/test/consumption/openid4vc.test.ts +++ b/packages/runtime/test/consumption/openid4vc.test.ts @@ -1,7 +1,7 @@ import { SdJwtVcRecord } from "@credo-ts/core"; import { EudiploClient } from "@eudiplo/sdk-core"; import { AcceptProposeAttributeRequestItemParametersWithNewAttributeJSON, AcceptShareAuthorizationRequestRequestItemParametersJSON, decodeRecord } from "@nmshd/consumption"; -import { RequestJSON, ShareAuthorizationRequestRequestItemJSON, VerifiableCredentialJSON, VerifiablePresentation } from "@nmshd/content"; +import { RequestJSON, ShareAuthorizationRequestRequestItemJSON, TokenContentVerifiablePresentation, VerifiableCredentialJSON } from "@nmshd/content"; import { CoreDate } from "@nmshd/core-types"; import axios, { AxiosInstance } from "axios"; import * as client from "openid-client"; @@ -284,10 +284,10 @@ describe("EUDIPLO", () => { const presentationTokenContent = presentationTokenResult.value.content; expect(presentationTokenContent).toBeDefined(); - expect(presentationTokenContent["@type"]).toBe("VerifiablePresentation"); - expect((presentationTokenContent as VerifiablePresentation).value).toBeDefined(); - expect((presentationTokenContent as VerifiablePresentation).displayInformation).toBeDefined(); - expect((presentationTokenContent as VerifiablePresentation).displayInformation![0].name).toBe("test"); + expect(presentationTokenContent["@type"]).toBe("TokenContentVerifiablePresentation"); + expect((presentationTokenContent as TokenContentVerifiablePresentation).value).toBeDefined(); + expect((presentationTokenContent as TokenContentVerifiablePresentation).displayInformation).toBeDefined(); + expect((presentationTokenContent as TokenContentVerifiablePresentation).displayInformation![0].name).toBe("test"); }); });