From be89f191739b853e3c41a7b4847711154a3f2ad4 Mon Sep 17 00:00:00 2001 From: Vafeiadis Nikos Date: Tue, 10 Dec 2024 17:08:57 +0200 Subject: [PATCH] Introduce sd-jwt-vc format in presentation definition preparation --- ...stations.ts => attestation-definitions.ts} | 17 +-- .../core/constants/attestations-per-format.ts | 134 +++++++++++++++++ src/app/core/data/MsoMdocDocuments.ts | 37 ----- src/app/core/models/FormSelectableField.ts | 3 +- ...ttestation.ts => AttestationDefinition.ts} | 2 +- .../core/models/attestation/Attestations.ts | 19 +++ .../models/attestation/MsoMdocAttestation.ts | 7 - .../models/presentation/FieldConstraint.ts | 3 +- .../models/presentation/InputDescriptor.ts | 4 + ...Attestation.ts => PresentedAttestation.ts} | 2 +- .../attestation-selectable-model.service.ts | 31 ---- .../services/decoders/AttestationDecoder.ts | 4 +- .../decoders/JwtVcJsonAttestationDecoder.ts | 4 +- .../decoders/MsoMdocAttestationDecoder.ts | 4 +- .../services/mso-mdoc-presentation.service.ts | 98 ------------- .../presentation-definition-service.ts | 135 ++++++++++++++++++ .../presentations-results.component.ts | 6 +- .../view-attestation/model/DialogData.ts | 2 +- .../view-attestation.component.ts | 5 +- .../wallet-response-processor.service.ts | 6 +- .../attestation-selection.component.ts | 6 +- .../attribute-selection.component.ts | 38 +++-- ...ctable-attestation-attributes.component.ts | 79 +++++----- .../supported-attestations.component.ts | 6 +- .../home/home.component.html | 12 +- .../home/home.component.scss | 9 ++ .../home/home.component.ts | 46 +++--- 27 files changed, 430 insertions(+), 289 deletions(-) rename src/app/core/constants/{attestations.ts => attestation-definitions.ts} (94%) create mode 100644 src/app/core/constants/attestations-per-format.ts delete mode 100644 src/app/core/data/MsoMdocDocuments.ts rename src/app/core/models/attestation/{Attestation.ts => AttestationDefinition.ts} (86%) create mode 100644 src/app/core/models/attestation/Attestations.ts delete mode 100644 src/app/core/models/attestation/MsoMdocAttestation.ts rename src/app/core/models/presentation/{SharedAttestation.ts => PresentedAttestation.ts} (87%) delete mode 100644 src/app/core/services/attestation-selectable-model.service.ts delete mode 100644 src/app/core/services/mso-mdoc-presentation.service.ts create mode 100644 src/app/core/services/presentation-definition-service.ts diff --git a/src/app/core/constants/attestations.ts b/src/app/core/constants/attestation-definitions.ts similarity index 94% rename from src/app/core/constants/attestations.ts rename to src/app/core/constants/attestation-definitions.ts index 3e24d9f..892e3b0 100644 --- a/src/app/core/constants/attestations.ts +++ b/src/app/core/constants/attestation-definitions.ts @@ -1,8 +1,7 @@ -import {Attestation} from "@core/models/attestation/Attestation"; +import {AttestationDefinition} from "@core/models/attestation/AttestationDefinition"; import {AttestationType} from "@core/models/attestation/AttestationType"; -import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; -export const PID_ATTESTATION: Attestation = { +export const PID_ATTESTATION: AttestationDefinition = { name: "Person Identification Data (PID)", type: AttestationType.PID, dataSet: [ @@ -37,7 +36,7 @@ export const PID_ATTESTATION: Attestation = { ] } -export const MDL_ATTESTATION: Attestation = { +export const MDL_ATTESTATION: AttestationDefinition = { name: "Mobile Driving Licence (MDL)", type: AttestationType.MDL, dataSet: [ @@ -75,7 +74,7 @@ export const MDL_ATTESTATION: Attestation = { ] } -export const AGE_OVER_18_ATTESTATION: Attestation = { +export const AGE_OVER_18_ATTESTATION: AttestationDefinition = { name: "Age Over 18", type: AttestationType.AGE_OVER_18, dataSet: [ @@ -89,7 +88,7 @@ export const AGE_OVER_18_ATTESTATION: Attestation = { ] } -export const PHOTO_ID_ATTESTATION: Attestation = { +export const PHOTO_ID_ATTESTATION: AttestationDefinition = { name: "Photo ID", type: AttestationType.PHOTO_ID, dataSet: [ @@ -128,14 +127,10 @@ export const PHOTO_ID_ATTESTATION: Attestation = { ] } -export const SUPPORTED_ATTESTATIONS: { [id: string]: Attestation } = { +export const SUPPORTED_ATTESTATIONS: { [id: string]: AttestationDefinition } = { "pid": PID_ATTESTATION, "mdl": MDL_ATTESTATION, "photo_id": PHOTO_ID_ATTESTATION, "age_over_18": AGE_OVER_18_ATTESTATION, } -export const SUPPORTED_FORMATS: AttestationFormat[] = [ - AttestationFormat.MSO_MDOC -] - diff --git a/src/app/core/constants/attestations-per-format.ts b/src/app/core/constants/attestations-per-format.ts new file mode 100644 index 0000000..3b24310 --- /dev/null +++ b/src/app/core/constants/attestations-per-format.ts @@ -0,0 +1,134 @@ +import {Attestation, MsoMdocAttestation, SdJwtVcAttestation} from "@core/models/attestation/Attestations"; +import {AGE_OVER_18_ATTESTATION, MDL_ATTESTATION, PHOTO_ID_ATTESTATION, PID_ATTESTATION} from "@core/constants/attestation-definitions"; +import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; +import {AttestationType} from "@core/models/attestation/AttestationType"; +import {DataElement} from "@core/models/attestation/AttestationDefinition"; + +export const SUPPORTED_FORMATS: AttestationFormat[] = [ + AttestationFormat.MSO_MDOC, + AttestationFormat.SD_JWT_VC +] + +/*---- MDL ATTESTATION INSTANCES PER FORMAT ----*/ +export const MDL_MSO_MDOC: MsoMdocAttestation = { + format: AttestationFormat.MSO_MDOC, + attestationDef: MDL_ATTESTATION, + doctype: 'org.iso.18013.5.1.mDL', + namespace: 'org.iso.18013.5.1', + attributePath: (attribute: DataElement) => { return msoMdocAttributePath(attribute, 'org.iso.18013.5.1') } +} +export const MDL_SD_JWT_VC: SdJwtVcAttestation = { + format: AttestationFormat.SD_JWT_VC, + vct: "urn:eu.europa.ec.eudi:pid:1", + attestationDef: MDL_ATTESTATION, + attributePath: (attribute: DataElement) => { return sdJwtVcAttributePath(attribute, AttestationType.MDL) } +} + +/*---- PID ATTESTATION INSTANCES PER FORMAT ----*/ +export const PID_MSO_MDOC: MsoMdocAttestation = { + format: AttestationFormat.MSO_MDOC, + attestationDef: PID_ATTESTATION, + doctype: 'eu.europa.ec.eudi.pid.1', + namespace: 'eu.europa.ec.eudi.pid.1', + attributePath: (attribute: DataElement) => { return msoMdocAttributePath(attribute, 'eu.europa.ec.eudi.pid.1') } +} +export const PID_SD_JWT_VC: SdJwtVcAttestation = { + format: AttestationFormat.SD_JWT_VC, + vct: "urn:eu.europa.ec.eudi:pid:1", + attestationDef: PID_ATTESTATION, + attributePath: (attribute: DataElement) => { return sdJwtVcAttributePath(attribute, AttestationType.PID) } +} + +/*---- AGE OVER 18 ATTESTATION INSTANCES PER FORMAT ----*/ +export const AGE_OVER_18_MSO_MDOC: MsoMdocAttestation = { + format: AttestationFormat.MSO_MDOC, + attestationDef: AGE_OVER_18_ATTESTATION, + doctype: 'eu.europa.ec.eudi.pseudonym.age_over_18.1', + namespace: 'eu.europa.ec.eudi.pseudonym.age_over_18.1', + attributePath: (attribute: DataElement) => { return msoMdocAttributePath(attribute, 'eu.europa.ec.eudi.pseudonym.age_over_18.1') } +} +export const AGE_OVER_18_SD_JWT_VC: SdJwtVcAttestation = { + format: AttestationFormat.SD_JWT_VC, + vct: "urn:eu.europa.ec.eudi:pid:1", + attestationDef: AGE_OVER_18_ATTESTATION, + attributePath: (attribute: DataElement) => { return sdJwtVcAttributePath(attribute, AttestationType.AGE_OVER_18) } +} + +/*---- PHOTO ID ATTESTATION INSTANCES PER FORMAT ----*/ +export const PHOTO_ID_MSO_MDOC: MsoMdocAttestation = { + format: AttestationFormat.MSO_MDOC, + attestationDef: PHOTO_ID_ATTESTATION, + doctype: 'org.iso.23220.2.photoid.1', + namespace: 'org.iso.23220.2.photoid.1', + attributePath: (attribute: DataElement) => { return msoMdocAttributePath(attribute, 'org.iso.23220.2.photoid.1') } +} +export const PHOTO_ID_SD_JWT_VC: SdJwtVcAttestation = { + format: AttestationFormat.SD_JWT_VC, + vct: "urn:eu.europa.ec.eudi:pid:1", + attestationDef: PHOTO_ID_ATTESTATION, + attributePath: (attribute: DataElement) => { return sdJwtVcAttributePath(attribute, AttestationType.PHOTO_ID) } +} + +function msoMdocAttributePath(attribute: DataElement, namespace: string): string { + return '$[\'' + namespace + '\'][\'' + attribute.identifier + '\']' +} + +function sdJwtVcAttributePath(attribute: DataElement, attestationType: AttestationType): string { + let resolvedAttribute = attribute.identifier + if (attestationType === AttestationType.PID) { + let mappedAttribute = PID_SD_JWT_VC_ATTRIBUTE_MAP[attribute.identifier]; + resolvedAttribute = mappedAttribute ? mappedAttribute : attribute.identifier; + } + return '$.' + resolvedAttribute; +} + +export const PID_SD_JWT_VC_ATTRIBUTE_MAP: { [id: string]: string } = { + "birth_date": "birthdate", + "age_over_18": "age_equal_or_over.18", + "age_over_NN": "age_equal_or_over.NN", + "family_name_birth": "birth_family_name", + "given_name_birth": "birth_given_name", + "birth_place": "place_of_birth.locality", + "birth_country": "place_of_birth.country", + "birth_state": "place_of_birth.region", + "birth_city": "place_of_birth.locality", + "resident_address": "address.formatted", + "resident_country": "address.country", + "resident_state": "address.region", + "resident_city": "address.locality", + "resident_postal_code": "address.postal_code", + "resident_street": "address.street_address", + "resident_house_number": "address.house_number", + "gender": "gender", + "nationality": "nationalities", + "issuance_date": "iat", + "expiry_date": "exp" +} + + +export const MSO_MDOC_ATTESTATIONS: { [id: string]: MsoMdocAttestation } = { + "pid": PID_MSO_MDOC, + "mdl": MDL_MSO_MDOC, + "photo_id": PHOTO_ID_MSO_MDOC, + "age_over_18": AGE_OVER_18_MSO_MDOC, +} + +export const ATTESTATIONS_BY_TYPE: { [id: string]: Attestation[] } = { + "pid": [PID_MSO_MDOC, PID_SD_JWT_VC], + "mdl": [MDL_MSO_MDOC, MDL_SD_JWT_VC], + "photo_id": [PHOTO_ID_MSO_MDOC, PHOTO_ID_SD_JWT_VC], + "age_over_18": [AGE_OVER_18_MSO_MDOC, AGE_OVER_18_SD_JWT_VC], +} + +export const ATTESTATIONS_BY_FORMAT: { [id: string]: Attestation[] } = { + "mso_mdoc": [PID_MSO_MDOC, MDL_MSO_MDOC, PHOTO_ID_MSO_MDOC, AGE_OVER_18_MSO_MDOC], + "vc+sd-jwt": [PID_SD_JWT_VC, MDL_SD_JWT_VC, PHOTO_ID_SD_JWT_VC, AGE_OVER_18_SD_JWT_VC] +} + +export const getAttestationByFormatAndType = + (type: AttestationType, format: AttestationFormat): Attestation | null => { + let filtered = ATTESTATIONS_BY_FORMAT[format as string].filter((attestation: Attestation) => + attestation.attestationDef.type === type + ); + return filtered ? filtered[0] : null; + } diff --git a/src/app/core/data/MsoMdocDocuments.ts b/src/app/core/data/MsoMdocDocuments.ts deleted file mode 100644 index b5f7610..0000000 --- a/src/app/core/data/MsoMdocDocuments.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {MsoMdocAttestation} from "@core/models/attestation/MsoMdocAttestation"; -import {AGE_OVER_18_ATTESTATION, MDL_ATTESTATION, PHOTO_ID_ATTESTATION, PID_ATTESTATION} from "@core/constants/attestations"; - -/* eslint-disable quotes */ -export const MDL_MSO_MDOC: MsoMdocAttestation = { - attestation: MDL_ATTESTATION, - doctype: 'org.iso.18013.5.1.mDL', - namespace: 'org.iso.18013.5.1' -} - -/* eslint-disable quotes */ -export const PID_MSO_MDOC: MsoMdocAttestation = { - attestation: PID_ATTESTATION, - doctype: 'eu.europa.ec.eudi.pid.1', - namespace: 'eu.europa.ec.eudi.pid.1' -} - -/* eslint-disable quotes */ -export const AGE_OVER_18_MSO_MDOC: MsoMdocAttestation = { - attestation: AGE_OVER_18_ATTESTATION, - doctype: 'eu.europa.ec.eudi.pseudonym.age_over_18.1', - namespace: 'eu.europa.ec.eudi.pseudonym.age_over_18.1' -} - -/* eslint-disable quotes */ -export const PHOTO_ID_MSO_MDOC: MsoMdocAttestation = { - attestation: PHOTO_ID_ATTESTATION, - doctype: 'org.iso.23220.2.photoid.1', - namespace: 'org.iso.23220.2.photoid.1' -} - -export const MSO_MDOC_BY_TYPE: { [id: string]: MsoMdocAttestation } = { - "pid": PID_MSO_MDOC, - "mdl": MDL_MSO_MDOC, - "photo_id": PHOTO_ID_MSO_MDOC, - "age_over_18": AGE_OVER_18_MSO_MDOC, -} diff --git a/src/app/core/models/FormSelectableField.ts b/src/app/core/models/FormSelectableField.ts index a0a97f5..5ee92ad 100644 --- a/src/app/core/models/FormSelectableField.ts +++ b/src/app/core/models/FormSelectableField.ts @@ -3,5 +3,6 @@ import {FieldConstraint} from "@core/models/presentation/FieldConstraint"; export type FormSelectableField = { id: number, label: string, - value: FieldConstraint + value: FieldConstraint, + visible?: boolean } diff --git a/src/app/core/models/attestation/Attestation.ts b/src/app/core/models/attestation/AttestationDefinition.ts similarity index 86% rename from src/app/core/models/attestation/Attestation.ts rename to src/app/core/models/attestation/AttestationDefinition.ts index 3c5122d..a37ff15 100644 --- a/src/app/core/models/attestation/Attestation.ts +++ b/src/app/core/models/attestation/AttestationDefinition.ts @@ -1,6 +1,6 @@ import {AttestationType} from "@core/models/attestation/AttestationType"; -export type Attestation = { +export type AttestationDefinition = { type: AttestationType, name: string, dataSet: DataElement[] diff --git a/src/app/core/models/attestation/Attestations.ts b/src/app/core/models/attestation/Attestations.ts new file mode 100644 index 0000000..7b2337f --- /dev/null +++ b/src/app/core/models/attestation/Attestations.ts @@ -0,0 +1,19 @@ +import {AttestationDefinition, DataElement} from "@core/models/attestation/AttestationDefinition"; +import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; + +export type Attestation = MsoMdocAttestation | SdJwtVcAttestation; + +export type MsoMdocAttestation = { + format: AttestationFormat.MSO_MDOC, + doctype: string, + namespace: string, + attestationDef: AttestationDefinition, + attributePath: (attribute: DataElement) => string, +} + +export type SdJwtVcAttestation = { + format: AttestationFormat.SD_JWT_VC, + vct: string, + attestationDef: AttestationDefinition + attributePath: (attribute: DataElement) => string, +} diff --git a/src/app/core/models/attestation/MsoMdocAttestation.ts b/src/app/core/models/attestation/MsoMdocAttestation.ts deleted file mode 100644 index 78e01b1..0000000 --- a/src/app/core/models/attestation/MsoMdocAttestation.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {Attestation} from "@core/models/attestation/Attestation"; - -export type MsoMdocAttestation = { - doctype: string, - namespace: string, - attestation: Attestation -} diff --git a/src/app/core/models/presentation/FieldConstraint.ts b/src/app/core/models/presentation/FieldConstraint.ts index 5bfe978..f4f2574 100644 --- a/src/app/core/models/presentation/FieldConstraint.ts +++ b/src/app/core/models/presentation/FieldConstraint.ts @@ -6,5 +6,6 @@ export type FieldConstraint = { export type Filter = { type: string, - contains: any + contains?: any, + const?: string } diff --git a/src/app/core/models/presentation/InputDescriptor.ts b/src/app/core/models/presentation/InputDescriptor.ts index e995891..8122a28 100644 --- a/src/app/core/models/presentation/InputDescriptor.ts +++ b/src/app/core/models/presentation/InputDescriptor.ts @@ -14,5 +14,9 @@ export type Format = { }, mso_mdoc?: { alg: string[] + }, + "vc+sd-jwt"?: { + "sd-jwt_alg_values": string[], + "kb-jwt_alg_values": string[] } } diff --git a/src/app/core/models/presentation/SharedAttestation.ts b/src/app/core/models/presentation/PresentedAttestation.ts similarity index 87% rename from src/app/core/models/presentation/SharedAttestation.ts rename to src/app/core/models/presentation/PresentedAttestation.ts index 79241e2..faf9a1b 100644 --- a/src/app/core/models/presentation/SharedAttestation.ts +++ b/src/app/core/models/presentation/PresentedAttestation.ts @@ -1,7 +1,7 @@ import {KeyValue} from "@angular/common"; import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; -export type SharedAttestation = Single | Enveloped +export type PresentedAttestation = Single | Enveloped export type Single = { kind: 'single', diff --git a/src/app/core/services/attestation-selectable-model.service.ts b/src/app/core/services/attestation-selectable-model.service.ts deleted file mode 100644 index 3688fcd..0000000 --- a/src/app/core/services/attestation-selectable-model.service.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {Injectable} from "@angular/core"; -import {PID_MSO_MDOC} from '@core/data/MsoMdocDocuments'; -import {MDL_MSO_MDOC} from '@core/data/MsoMdocDocuments'; -import {MsoMdocAttestation} from "@core/models/attestation/MsoMdocAttestation"; - -@Injectable({ - providedIn: 'root' -}) -export class AttestationSelectableModelService { - - private selectableModel: MsoMdocAttestation | null = null; - private presentationPurpose!: string; - - setPresentationPurpose(presentationPurpose: string) { - this.presentationPurpose = presentationPurpose; - } - - setModel(attestation: string) { - if (attestation == 'MDL') { - this.selectableModel = MDL_MSO_MDOC; - } else if (attestation == 'PID') { - this.selectableModel = PID_MSO_MDOC; - } - } - - getModel(): MsoMdocAttestation { - return JSON.parse(JSON.stringify(this.selectableModel)); - } - - getPresentationPurpose(): string { return this.presentationPurpose} -} diff --git a/src/app/core/services/decoders/AttestationDecoder.ts b/src/app/core/services/decoders/AttestationDecoder.ts index f0d3844..88aa2ff 100644 --- a/src/app/core/services/decoders/AttestationDecoder.ts +++ b/src/app/core/services/decoders/AttestationDecoder.ts @@ -1,7 +1,7 @@ import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; -import {SharedAttestation} from "@core/models/presentation/SharedAttestation"; +import {PresentedAttestation} from "@core/models/presentation/PresentedAttestation"; export interface AttestationDecoder { supports(format: AttestationFormat): boolean; - decode(attestation: string): SharedAttestation + decode(attestation: string): PresentedAttestation } diff --git a/src/app/core/services/decoders/JwtVcJsonAttestationDecoder.ts b/src/app/core/services/decoders/JwtVcJsonAttestationDecoder.ts index 6e23aba..89c60bd 100644 --- a/src/app/core/services/decoders/JwtVcJsonAttestationDecoder.ts +++ b/src/app/core/services/decoders/JwtVcJsonAttestationDecoder.ts @@ -1,6 +1,6 @@ import {Injectable} from "@angular/core"; import {AttestationDecoder} from "@core/services/decoders/AttestationDecoder"; -import {SharedAttestation, Single} from "@core/models/presentation/SharedAttestation"; +import {PresentedAttestation, Single} from "@core/models/presentation/PresentedAttestation"; import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; import {JWTService} from "@core/services/jwt.service"; import {KeyValue} from "@angular/common"; @@ -23,7 +23,7 @@ export class JwtVcJsonAttestationDecoder implements AttestationDecoder { return format === AttestationFormat.JWT_VC_JSON; } - decode(attestation: string): SharedAttestation { + decode(attestation: string): PresentedAttestation { let vp = this.jWTService.decodeToObject(attestation); let sharedCredentials = this.unWrapCredentials(vp) diff --git a/src/app/core/services/decoders/MsoMdocAttestationDecoder.ts b/src/app/core/services/decoders/MsoMdocAttestationDecoder.ts index ae3ed28..b510bf4 100644 --- a/src/app/core/services/decoders/MsoMdocAttestationDecoder.ts +++ b/src/app/core/services/decoders/MsoMdocAttestationDecoder.ts @@ -4,7 +4,7 @@ import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; import {decode} from "cbor-x"; import {Buffer} from 'buffer'; -import {SharedAttestation, Single} from "@core/models/presentation/SharedAttestation"; +import {PresentedAttestation, Single} from "@core/models/presentation/PresentedAttestation"; import {KeyValue} from "@angular/common"; import {elementAsString} from "@core/services/decoders/DecodingUtils"; @@ -17,7 +17,7 @@ export class MsoMdocAttestationDecoder implements AttestationDecoder { return format === AttestationFormat.MSO_MDOC; } - decode(attestation: string): SharedAttestation { + decode(attestation: string): PresentedAttestation { const buffer = this.decodeBase64OrHex(attestation); const decodedData = this.decodeCborData(buffer); if (decodedData.documents.length === 1) { diff --git a/src/app/core/services/mso-mdoc-presentation.service.ts b/src/app/core/services/mso-mdoc-presentation.service.ts deleted file mode 100644 index bf3be1c..0000000 --- a/src/app/core/services/mso-mdoc-presentation.service.ts +++ /dev/null @@ -1,98 +0,0 @@ -import {Injectable} from "@angular/core"; -import {MsoMdocAttestation} from "@core/models/attestation/MsoMdocAttestation"; -import {TransactionInitializationRequest} from "@core/models/TransactionInitializationRequest"; -import {v4 as uuidv4} from 'uuid'; -import {AGE_OVER_18_MSO_MDOC, MDL_MSO_MDOC, PID_MSO_MDOC} from "@core/data/MsoMdocDocuments"; -import {InputDescriptor} from "@core/models/presentation/InputDescriptor"; -import {FieldConstraint} from "@core/models/presentation/FieldConstraint"; -import {DataElement} from "@core/models/attestation/Attestation"; - -@Injectable({ - providedIn: 'root' -}) -export class MsoMdocPresentationService { - - presentationOfFullPid(): TransactionInitializationRequest { - const presentationPurpose = 'We need to verify your identity'; - return this.presentationOf(PID_MSO_MDOC, presentationPurpose); - } - - presentationOfFullMdl(): TransactionInitializationRequest { - const presentationPurpose = 'We need to verify your mobile driving licence'; - return this.presentationOf(MDL_MSO_MDOC, presentationPurpose) - } - - presentationOfPidOver18(): TransactionInitializationRequest { - const presentationPurpose = 'We need to verify you are over 18 using your PID'; - return this.presentationOf(PID_MSO_MDOC, presentationPurpose, ["age_over_18"]) - } - - presentationOfAgeAttestationOver18(): TransactionInitializationRequest { - const presentationPurpose = 'We need to verify you are over 18'; - return this.presentationOf(AGE_OVER_18_MSO_MDOC, presentationPurpose, ["age_over_18"]) - } - - presentationOf( - document: MsoMdocAttestation, - presentationPurpose: string, - includeAttributes?: string[] - ): TransactionInitializationRequest { - return { - type: 'vp_token', - presentation_definition: { - id: uuidv4(), - input_descriptors: [ - this.msoMdocInputDescriptorOf(document, presentationPurpose, includeAttributes) - ] - }, - nonce: uuidv4() - }; - } - - msoMdocInputDescriptorOf( - document: MsoMdocAttestation, - presentationPurpose: string, - includeAttributes?: string[] - ): InputDescriptor { - return { - id: document.doctype, - name: document.attestation.name, - purpose: presentationPurpose, - format: { - mso_mdoc: { - alg: [ - "ES256", - "ES384", - "ES512" - ] - } - }, - constraints: { - fields: this.msoMdocFieldConstraints(document, includeAttributes) - } - }; - } - - msoMdocFieldConstraints(document: MsoMdocAttestation, includeAttributes?: string[]): FieldConstraint[] { - const fieldConstraints: FieldConstraint[] = []; - document.attestation.dataSet.forEach((dataElement: DataElement) => { - if (typeof includeAttributes == 'undefined' || includeAttributes.includes(dataElement.identifier)) { - fieldConstraints.push(this.fieldConstraint(document.namespace, dataElement.identifier)); - } - }) - return fieldConstraints; - } - - fieldConstraint(namespace: string, attribute: string, intentToRetainOptional?: boolean): FieldConstraint { - let intentToRetain = false; - if (typeof intentToRetainOptional !== 'undefined' && intentToRetainOptional) { - intentToRetain = true - } - return { - path: ['$[\'' + namespace + '\'][\'' + attribute + '\']'], - intent_to_retain: intentToRetain - } - } - - -} diff --git a/src/app/core/services/presentation-definition-service.ts b/src/app/core/services/presentation-definition-service.ts new file mode 100644 index 0000000..234081a --- /dev/null +++ b/src/app/core/services/presentation-definition-service.ts @@ -0,0 +1,135 @@ +import {AttestationType} from "@core/models/attestation/AttestationType"; +import {InputDescriptor} from "@core/models/presentation/InputDescriptor"; +import {Injectable} from "@angular/core"; +import {Attestation, MsoMdocAttestation, SdJwtVcAttestation} from "@core/models/attestation/Attestations"; +import {TransactionInitializationRequest} from "@core/models/TransactionInitializationRequest"; +import {v4 as uuidv4} from "uuid"; +import {FieldConstraint} from "@core/models/presentation/FieldConstraint"; +import {DataElement} from "@core/models/attestation/AttestationDefinition"; +import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; +import {getAttestationByFormatAndType} from "@core/constants/attestations-per-format"; + +@Injectable({ + providedIn: 'root' +}) +export class PresentationDefinitionService { + + presentationDefinitionOf( + document: MsoMdocAttestation, + presentationPurpose: string, + includeAttributes?: string[] + ): TransactionInitializationRequest { + return { + type: 'vp_token', + presentation_definition: { + id: uuidv4(), + input_descriptors: [ + this.msoMdocInputDescriptorOf(document, presentationPurpose, includeAttributes) + ] + }, + nonce: uuidv4() + }; + } + + inputDescriptorOf( + type: AttestationType, + format: AttestationFormat, + presentationPurpose: string, + includeAttributes?: string[] + ): InputDescriptor | null { + let attestation = getAttestationByFormatAndType(type, format); + if (attestation) { + switch (attestation.format) { + case AttestationFormat.MSO_MDOC: + return this.msoMdocInputDescriptorOf(attestation, presentationPurpose, includeAttributes); + case AttestationFormat.SD_JWT_VC: + return this.sdJwtVcInputDescriptorOf(attestation, presentationPurpose, includeAttributes); + } + } else { + console.error(`No attestation found with type ${type} and format: ${format}`); + return null; + } + } + + private msoMdocInputDescriptorOf( + attestation: MsoMdocAttestation, + presentationPurpose: string, + includeAttributes?: string[] + ): InputDescriptor { + return { + id: attestation.doctype, + name: attestation.attestationDef.name, + purpose: presentationPurpose, + format: { + mso_mdoc: { + alg: [ + "ES256", + "ES384", + "ES512" + ] + } + }, + constraints: { + fields: this.fieldConstraints(attestation, includeAttributes) + } + }; + } + + private sdJwtVcInputDescriptorOf( + attestation: SdJwtVcAttestation, + presentationPurpose: string, + includeAttributes: string[] | undefined + ) { + let filedConstraints: FieldConstraint[] = [ + this.sdJwtVCVctFieldConstraint(attestation), + ...this.fieldConstraints(attestation, includeAttributes) + ] + return { + id: uuidv4(), + name: attestation.attestationDef.name, + purpose: presentationPurpose, + format: { + "vc+sd-jwt": { + "sd-jwt_alg_values": ["ES256", "ES384"], + "kb-jwt_alg_values": ["ES256", "ES384"] + } + }, + constraints: { + fields: filedConstraints + } + }; + } + + sdJwtVCVctFieldConstraint(attestation: SdJwtVcAttestation): FieldConstraint { + return { + path: ["$.vct"], + filter: { + type: "string", + const: attestation.vct + } + } + } + + private fieldConstraints(attestation: Attestation, includeAttributes?: string[]): FieldConstraint[] { + const fieldConstraints: FieldConstraint[] = []; + attestation.attestationDef.dataSet.forEach((dataElement: DataElement) => { + if (typeof includeAttributes == 'undefined' || includeAttributes.includes(dataElement.identifier)) { + fieldConstraints.push(this.fieldConstraint(attestation.attributePath(dataElement))); + } + }) + return fieldConstraints; + } + + fieldConstraint(path: string, intentToRetainOptional?: boolean): FieldConstraint { + let intentToRetain = false; + if (typeof intentToRetainOptional !== 'undefined' && intentToRetainOptional) { + intentToRetain = true + } + return { + path: [path], + intent_to_retain: intentToRetain + } + } + + +} diff --git a/src/app/features/invoke-wallet/components/presentations-results/presentations-results.component.ts b/src/app/features/invoke-wallet/components/presentations-results/presentations-results.component.ts index 8565e55..f88bad7 100644 --- a/src/app/features/invoke-wallet/components/presentations-results/presentations-results.component.ts +++ b/src/app/features/invoke-wallet/components/presentations-results/presentations-results.component.ts @@ -6,7 +6,7 @@ import {MatExpansionModule} from "@angular/material/expansion"; import {ConcludedTransaction} from "@core/models/ConcludedTransaction"; import {PresentationDefinition} from "@core/models/presentation/PresentationDefinition"; import {ViewAttestationComponent} from "@features/invoke-wallet/components/view-attestation/view-attestation.component"; -import {SharedAttestation, Single} from "@core/models/presentation/SharedAttestation"; +import {PresentedAttestation, Single} from "@core/models/presentation/PresentedAttestation"; import {WalletResponseProcessorService} from "@features/invoke-wallet/services/wallet-response-processor.service"; import {MatCardModule} from "@angular/material/card"; import {MatButtonModule} from "@angular/material/button"; @@ -42,11 +42,11 @@ export class PresentationsResultsComponent implements OnInit { ngOnInit(): void { this.presentationRequest = this.concludedTransaction.presentationDefinition; - let sharedAttestations: SharedAttestation[] = this.responseProcessor.mapVpTokenToAttestations(this.concludedTransaction); + let sharedAttestations: PresentedAttestation[] = this.responseProcessor.mapVpTokenToAttestations(this.concludedTransaction); this.attestations = this.flatten(sharedAttestations) } - flatten(sharedAttestations: SharedAttestation[]): Single[] { + flatten(sharedAttestations: PresentedAttestation[]): Single[] { let singles: Single[] = [] sharedAttestations.forEach(it => { switch (it.kind) { diff --git a/src/app/features/invoke-wallet/components/view-attestation/model/DialogData.ts b/src/app/features/invoke-wallet/components/view-attestation/model/DialogData.ts index 438e175..4f55f1a 100644 --- a/src/app/features/invoke-wallet/components/view-attestation/model/DialogData.ts +++ b/src/app/features/invoke-wallet/components/view-attestation/model/DialogData.ts @@ -1,4 +1,4 @@ -import {Single} from "@core/models/presentation/SharedAttestation"; +import {Single} from "@core/models/presentation/PresentedAttestation"; export interface DialogData { attestation: Single diff --git a/src/app/features/invoke-wallet/components/view-attestation/view-attestation.component.ts b/src/app/features/invoke-wallet/components/view-attestation/view-attestation.component.ts index 817a4c3..3356366 100644 --- a/src/app/features/invoke-wallet/components/view-attestation/view-attestation.component.ts +++ b/src/app/features/invoke-wallet/components/view-attestation/view-attestation.component.ts @@ -1,11 +1,10 @@ -import {ChangeDetectionStrategy, Component, inject, Input, OnInit} from "@angular/core"; +import {ChangeDetectionStrategy, Component, inject, OnInit} from "@angular/core"; import {CommonModule} from "@angular/common"; import {SharedModule} from "@shared/shared.module"; import {JWTService} from "@core/services/jwt.service"; -import {SharedAttestation, Single} from "@core/models/presentation/SharedAttestation"; +import {Single} from "@core/models/presentation/PresentedAttestation"; import {MatExpansionModule} from "@angular/material/expansion"; import {MatListModule} from "@angular/material/list"; -import {LogData} from "@shared/elements/open-logs/interfaces/LogData"; import {MAT_DIALOG_DATA, MatDialogModule} from "@angular/material/dialog"; import {DialogData} from "@features/invoke-wallet/components/view-attestation/model/DialogData"; import {MatButtonModule} from "@angular/material/button"; diff --git a/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts b/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts index 12b2f81..52ab4e8 100644 --- a/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts +++ b/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts @@ -1,5 +1,5 @@ import {Injectable} from "@angular/core"; -import {SharedAttestation} from "@core/models/presentation/SharedAttestation"; +import {PresentedAttestation} from "@core/models/presentation/PresentedAttestation"; import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; import {JSONPath} from "jsonpath-plus"; import {ConcludedTransaction} from "@core/models/ConcludedTransaction"; @@ -13,11 +13,11 @@ export class WalletResponseProcessorService { ) { } - mapVpTokenToAttestations(concludedTransaction: ConcludedTransaction): SharedAttestation[] { + mapVpTokenToAttestations(concludedTransaction: ConcludedTransaction): PresentedAttestation[] { let presentationSubmission = concludedTransaction.walletResponse.presentation_submission; let vpToken: string[] = concludedTransaction.walletResponse.vp_token!!; let arrayAsJson = JSON.parse(JSON.stringify(vpToken)) - let result: SharedAttestation[] = [] + let result: PresentedAttestation[] = [] let formatsPerPath = this.deductVpTokenItemsFormats(presentationSubmission.descriptor_map) for (let [path, format] of Object.entries(formatsPerPath)) { diff --git a/src/app/features/presentation-request-preparation/components/attestation-selection/attestation-selection.component.ts b/src/app/features/presentation-request-preparation/components/attestation-selection/attestation-selection.component.ts index 5b289ff..e5e0780 100644 --- a/src/app/features/presentation-request-preparation/components/attestation-selection/attestation-selection.component.ts +++ b/src/app/features/presentation-request-preparation/components/attestation-selection/attestation-selection.component.ts @@ -7,12 +7,12 @@ import {MatSelectModule} from "@angular/material/select"; import {MatRadioModule} from "@angular/material/radio"; import {FormControl, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms"; import {AttributeSelectionMethod} from "@features/presentation-request-preparation/models/AttestationSelection"; -import {SUPPORTED_FORMATS} from "@core/constants/attestations"; import {MatExpansionModule} from "@angular/material/expansion"; import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; import {AttestationSelection} from "@features/presentation-request-preparation/models/AttestationSelection"; import {FormatSelectOption} from "@features/presentation-request-preparation/components/attestation-selection/model/format-select-option"; -import {Attestation} from "@core/models/attestation/Attestation"; +import {AttestationDefinition} from "@core/models/attestation/AttestationDefinition"; +import {SUPPORTED_FORMATS} from "@core/constants/attestations-per-format"; @Component({ selector: 'vc-attestation-selection', @@ -32,7 +32,7 @@ import {Attestation} from "@core/models/attestation/Attestation"; }) export class AttestationSelectionComponent { - @Input() attestation!: Attestation; + @Input() attestation!: AttestationDefinition; @Output() attestationSelectionEvent = new EventEmitter(); protected readonly supportedFormats: FormatSelectOption[] = this.formatOptions() diff --git a/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.ts b/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.ts index 326ff20..6049378 100644 --- a/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.ts +++ b/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.ts @@ -4,7 +4,7 @@ import {SharedModule} from "@shared/shared.module"; import {WalletLayoutComponent} from "@core/layout/wallet-layout/wallet-layout.component"; import {AttestationSelection, AttributeSelectionMethod} from "@features/presentation-request-preparation/models/AttestationSelection"; import {AttestationType} from "@core/models/attestation/AttestationType"; -import {SUPPORTED_ATTESTATIONS} from "@core/constants/attestations"; +import {SUPPORTED_ATTESTATIONS} from "@core/constants/attestation-definitions"; import {MatButtonModule} from "@angular/material/button"; import {MatCardModule} from "@angular/material/card"; import {MatDialog} from "@angular/material/dialog"; @@ -14,11 +14,11 @@ import { import {AttestationFormat} from "@core/models/attestation/AttestationFormat"; import {InputDescriptor} from "@core/models/presentation/InputDescriptor"; import {DialogResult} from "@features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogResult"; -import {MSO_MDOC_BY_TYPE} from "@core/data/MsoMdocDocuments"; -import {MsoMdocPresentationService} from "@core/services/mso-mdoc-presentation.service"; import {MatBadgeModule} from "@angular/material/badge"; +import {PresentationDefinitionService} from "@core/services/presentation-definition-service"; @Component({ + templateUrl: './attribute-selection.component.html', selector: 'vc-attribute-selection', standalone: true, imports: [ @@ -27,16 +27,14 @@ import {MatBadgeModule} from "@angular/material/badge"; WalletLayoutComponent, MatButtonModule, MatCardModule, - MatBadgeModule, + MatBadgeModule ], - providers: [MsoMdocPresentationService], - templateUrl: './attribute-selection.component.html', - styleUrls: ['./attribute-selection.component.scss'], + styleUrls: ['./attribute-selection.component.scss'] }) export class AttributeSelectionComponent implements OnInit, OnChanges { constructor( - private readonly msoMdocPresentationService: MsoMdocPresentationService, + private readonly presentationDefinitionService: PresentationDefinitionService, ) { } @Input() attestationsSelection!: AttestationSelection[]; @@ -54,20 +52,16 @@ export class AttributeSelectionComponent implements OnInit, OnChanges { let allAttributesSelections = this.attestationsSelection.filter((selection: AttestationSelection) => selection.attributeSelectionMethod === AttributeSelectionMethod.ALL_ATTRIBUTES ) - allAttributesSelections.forEach((selection: AttestationSelection) => { - switch (selection.format) { - case AttestationFormat.MSO_MDOC: - let msoMdoc = MSO_MDOC_BY_TYPE[selection.type as string]; - let inputDescriptor = this.msoMdocPresentationService.msoMdocInputDescriptorOf(msoMdoc, "") - this.inputDescriptorsByType[selection.type] = inputDescriptor; - break; - case AttestationFormat.SD_JWT_VC: - console.error("Format " + AttestationFormat.SD_JWT_VC + " not suppoerted yet"); - break; - case AttestationFormat.JWT_VC_JSON: - console.error("Format " + AttestationFormat.JWT_VC_JSON + " not suppoerted yet"); - break; - } + allAttributesSelections.forEach((selectedAttestation: AttestationSelection) => { + let inputDescriptor = this.presentationDefinitionService.inputDescriptorOf( + selectedAttestation.type, + selectedAttestation.format!, + "" + ) + inputDescriptor + ? this.inputDescriptorsByType[selectedAttestation.type] = inputDescriptor + : console.warn("No input descriptor created for selection " + selectedAttestation + "."); + }) } diff --git a/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.ts b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.ts index db38743..7ab58d9 100644 --- a/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.ts +++ b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.ts @@ -1,6 +1,4 @@ import {Component, inject, OnInit} from '@angular/core'; -import {MsoMdocPresentationService} from "@app/core/services/mso-mdoc-presentation.service"; -import {VerifierEndpointService} from "@core/services/verifier-endpoint.service"; import {FieldConstraint, Filter} from "@core/models/presentation/FieldConstraint"; import {FormSelectableField} from "@core/models/FormSelectableField"; import {InputDescriptor} from "@core/models/presentation/InputDescriptor"; @@ -9,11 +7,13 @@ import {MatCheckboxModule} from "@angular/material/checkbox"; import {MatExpansionModule} from "@angular/material/expansion"; import {SharedModule} from "@shared/shared.module"; import {AttestationType} from "@core/models/attestation/AttestationType"; -import {MSO_MDOC_BY_TYPE} from "@core/data/MsoMdocDocuments"; +import {getAttestationByFormatAndType} from "@core/constants/attestations-per-format"; import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog"; import {CommonModule} from "@angular/common"; import {MatButtonModule} from "@angular/material/button"; import {DialogData} from "@features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogData"; +import {PresentationDefinitionService} from "@core/services/presentation-definition-service"; +import {SdJwtVcAttestation} from "@core/models/attestation/Attestations"; @Component({ standalone: true, @@ -27,8 +27,7 @@ import {DialogData} from "@features/presentation-request-preparation/components/ SharedModule, MatDialogModule, MatButtonModule - ], - providers: [VerifierEndpointService] + ] }) export class SelectableAttestationAttributesComponent implements OnInit { @@ -44,7 +43,7 @@ export class SelectableAttestationAttributesComponent implements OnInit { selectedFields: FieldConstraint[] = []; constructor( - private readonly msoMdocPresentationService: MsoMdocPresentationService, + private readonly presentationDefinitionService: PresentationDefinitionService, private dialogRef: MatDialogRef ) { } @@ -64,35 +63,49 @@ export class SelectableAttestationAttributesComponent implements OnInit { } initEmptyInputDescriptor() { - switch (this.attestationFormat) { - case AttestationFormat.MSO_MDOC: - let msomdoc = MSO_MDOC_BY_TYPE[this.attestationType as string]; - this.draftInputDescriptor = this.msoMdocPresentationService.msoMdocInputDescriptorOf(msomdoc, "", []) - return - case AttestationFormat.SD_JWT_VC: - console.error("Format " + AttestationFormat.SD_JWT_VC + " not suppoerted yet"); - return [] - case AttestationFormat.JWT_VC_JSON: - console.error("Format " + AttestationFormat.JWT_VC_JSON + " not suppoerted yet"); - return [] + let inputDescriptorMaybe = this.presentationDefinitionService.inputDescriptorOf( + this.attestationType, + this.attestationFormat, + "" + ); + if (inputDescriptorMaybe) { + this.draftInputDescriptor = inputDescriptorMaybe + } else { + console.log("Could not initialize InputDescriptor"); } } handle(data: FormSelectableField) { - const value = data?.value; + const value = data.value; if (!this.exists(value.path[0])) { this.selectedFields.push(value); + } else if (this.exists(value.path[0])) { this.selectedFields = this.selectedFields.filter((item: FieldConstraint) => { return String(item.path) !== String(value.path[0]); }); } // Update draft presentation with selected fields - this.draftInputDescriptor.constraints.fields = this.selectedFields; + this.draftInputDescriptor.constraints.fields = this.handleSdJwtVcVCTAttribute(this.selectedFields); // refresh descriptor text from model this.inputDescriptorText = this.convertJSONtoString(this.draftInputDescriptor); } + private handleSdJwtVcVCTAttribute(constraints: FieldConstraint[]): FieldConstraint[] { + if (this.attestationFormat !== AttestationFormat.SD_JWT_VC) { + return constraints + } + let attestation = getAttestationByFormatAndType(this.attestationType, this.attestationFormat) as SdJwtVcAttestation; + let vctIncluded = constraints.filter((item: FieldConstraint) => { + item.path.includes("$.vct") + }).length > 0; + if (!vctIncluded) { + return [this.presentationDefinitionService.sdJwtVCVctFieldConstraint(attestation), ...constraints] + } else { + return constraints + } + } + convertJSONtoString(obj: object) { return JSON.stringify(obj, null, '\t'); } @@ -103,23 +116,18 @@ export class SelectableAttestationAttributesComponent implements OnInit { } extractFormFieldsFromModel(): FormSelectableField[] { - switch (this.attestationFormat) { - case AttestationFormat.MSO_MDOC: - let msomdoc = MSO_MDOC_BY_TYPE[this.attestationType as string]; - return msomdoc.attestation.dataSet.map((attr, index) => { - return { - id: index, - label: attr.attribute, - value: this.msoMdocPresentationService.fieldConstraint(msomdoc.namespace, attr.identifier) - } - }) - case AttestationFormat.SD_JWT_VC: - console.error("Format " + AttestationFormat.SD_JWT_VC + " not suppoerted yet"); - return [] - case AttestationFormat.JWT_VC_JSON: - console.error("Format " + AttestationFormat.JWT_VC_JSON + " not suppoerted yet"); - return [] + let attestation = getAttestationByFormatAndType(this.attestationType, this.attestationFormat); + if (!attestation) { + return [] } + return attestation.attestationDef.dataSet.map((attr, index) => { + return { + id: index, + label: attr.attribute, + value: this.presentationDefinitionService.fieldConstraint(attestation!.attributePath(attr)), + visible: true + } + }) } trackByFn(_index: number, data: FormSelectableField) { @@ -165,4 +173,5 @@ export class SelectableAttestationAttributesComponent implements OnInit { pathsAreEqual(one.path, other.path) && filtersAreEqual(one.filter, other.filter) } + } diff --git a/src/app/features/presentation-request-preparation/components/supported-attestations/supported-attestations.component.ts b/src/app/features/presentation-request-preparation/components/supported-attestations/supported-attestations.component.ts index 12073f3..e2cb925 100644 --- a/src/app/features/presentation-request-preparation/components/supported-attestations/supported-attestations.component.ts +++ b/src/app/features/presentation-request-preparation/components/supported-attestations/supported-attestations.component.ts @@ -9,8 +9,8 @@ import {MatRadioModule} from "@angular/material/radio"; import {AttestationSelectionComponent} from "@features/presentation-request-preparation/components/attestation-selection/attestation-selection.component"; import {MatExpansionModule} from "@angular/material/expansion"; import {AttestationSelection} from "@features/presentation-request-preparation/models/AttestationSelection"; -import {Attestation} from "@core/models/attestation/Attestation"; -import {SUPPORTED_ATTESTATIONS} from "@core/constants/attestations"; +import {AttestationDefinition} from "@core/models/attestation/AttestationDefinition"; +import {SUPPORTED_ATTESTATIONS} from "@core/constants/attestation-definitions"; @Component({ selector: 'vc-supported-attestations', @@ -34,7 +34,7 @@ export class SupportedAttestationsComponent implements OnInit { @Output() selectionChangedEvent = new EventEmitter(); - attestations: Attestation[] = [] + attestations: AttestationDefinition[] = [] attestationSelections: {[id: string]: AttestationSelection} = {}; ngOnInit(): void { diff --git a/src/app/features/presentation-request-preparation/home/home.component.html b/src/app/features/presentation-request-preparation/home/home.component.html index e96d34d..344a5f7 100644 --- a/src/app/features/presentation-request-preparation/home/home.component.html +++ b/src/app/features/presentation-request-preparation/home/home.component.html @@ -37,13 +37,21 @@

Define your presentation request

Your presentation request so far... -
{{ initializationRequest | json }}
+ + + {{ initializationRequest | json }} +
..nothing prepared yet...
- +
diff --git a/src/app/features/presentation-request-preparation/home/home.component.scss b/src/app/features/presentation-request-preparation/home/home.component.scss index 95922aa..f9402a7 100644 --- a/src/app/features/presentation-request-preparation/home/home.component.scss +++ b/src/app/features/presentation-request-preparation/home/home.component.scss @@ -16,4 +16,13 @@ padding: 2px; } } + + span#as-pre { + white-space: pre-wrap; + word-break: break-word; + font-family: inherit; + unicode-bidi: isolate; + margin: 1em 0; + } + } diff --git a/src/app/features/presentation-request-preparation/home/home.component.ts b/src/app/features/presentation-request-preparation/home/home.component.ts index 7e4ba77..aafa214 100644 --- a/src/app/features/presentation-request-preparation/home/home.component.ts +++ b/src/app/features/presentation-request-preparation/home/home.component.ts @@ -27,29 +27,35 @@ import {v4 as uuidv4} from "uuid"; import {VerifierEndpointService} from "@core/services/verifier-endpoint.service"; import {MatExpansionModule} from "@angular/material/expansion"; import {AttestationSelectionComponent} from "@features/presentation-request-preparation/components/attestation-selection/attestation-selection.component"; +import {MatIconModule} from "@angular/material/icon"; +import {ClipboardModule} from "@angular/cdk/clipboard"; +import {MatTooltipModule} from "@angular/material/tooltip"; @Component({ standalone: true, - imports: [ - CommonModule, - MatTabsModule, - RadioGroupComponent, - SharedModule, - InputSchemeComponent, - WalletLayoutComponent, - OpenLogsComponent, - MatDialogModule, - RouterOutlet, - SupportedAttestationsComponent, - MatStepperModule, - ReactiveFormsModule, - MatButtonModule, - AttributeSelectionComponent, - MatExpansionModule, - RouterLinkActive, - RouterLink, - AttestationSelectionComponent - ], + imports: [ + CommonModule, + MatTabsModule, + RadioGroupComponent, + SharedModule, + InputSchemeComponent, + WalletLayoutComponent, + OpenLogsComponent, + MatDialogModule, + RouterOutlet, + SupportedAttestationsComponent, + MatStepperModule, + ReactiveFormsModule, + MatButtonModule, + AttributeSelectionComponent, + MatExpansionModule, + RouterLinkActive, + RouterLink, + AttestationSelectionComponent, + MatIconModule, + ClipboardModule, + MatTooltipModule + ], providers: [VerifierEndpointService], selector: 'vc-presentation-preparation-home', templateUrl: './home.component.html',