diff --git a/agent-swagger.json b/agent-swagger.json index 14412bef8a90088901894f7400cbba2ab4a066ab..1f69f9f096469f4f229325645aa7605f4e9d5f29 100644 --- a/agent-swagger.json +++ b/agent-swagger.json @@ -455,6 +455,89 @@ } } }, + "/api/v1/credentials/jsonld/offers": { + "post": { + "operationId": "RestController_offerJsonLdCredential", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OfferJsonCredentialRequests" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CredentialOfferResponseDto" + } + } + } + } + } + } + }, + "/api/v1/jsonld/sign": { + "post": { + "operationId": "RestController_signJsonLdCredential", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignJsonCredentialRequests" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/W3cJsonLdVerifiableCredentialDto" + } + } + } + } + } + } + }, + "/api/v1/jsonld/credentials/{cred_id}/prepare-verifiable-presentation": { + "post": { + "operationId": "RestController_signJsonLdPresentationByCredId", + "parameters": [ + { + "name": "cred_id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/W3cJsonLdVerifiablePresentationDto" + } + } + } + } + } + } + }, "/api/v1/credentials": { "get": { "operationId": "RestController_fetchCredentials", @@ -1414,6 +1497,175 @@ "credentialRecord" ] }, + "W3cCredentialStatusDto": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ] + }, + "W3cCredentialDto": { + "type": "object", + "properties": { + "context": { + "type": "array", + "items": { + "type": "object" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "issuer": { + "type": "object" + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialSubject": { + "type": "object" + }, + "credentialSchema": { + "type": "object" + }, + "credentialStatus": { + "$ref": "#/components/schemas/W3cCredentialStatusDto" + } + }, + "required": [ + "context", + "type", + "issuer", + "issuanceDate", + "credentialSubject" + ] + }, + "OfferJsonCredentialRequests": { + "type": "object", + "properties": { + "connectionId": { + "type": "string" + }, + "doc": { + "$ref": "#/components/schemas/W3cCredentialDto" + } + }, + "required": [ + "doc" + ] + }, + "SignJsonCredentialRequests": { + "type": "object", + "properties": { + "doc": { + "$ref": "#/components/schemas/W3cCredentialDto" + } + }, + "required": [ + "doc" + ] + }, + "W3cJsonLdVerifiableCredentialDto": { + "type": "object", + "properties": { + "context": { + "type": "array", + "items": { + "type": "object" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "issuer": { + "type": "object" + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialSubject": { + "type": "object" + }, + "credentialSchema": { + "type": "object" + }, + "credentialStatus": { + "$ref": "#/components/schemas/W3cCredentialStatusDto" + }, + "proof": { + "type": "object" + } + }, + "required": [ + "context", + "type", + "issuer", + "issuanceDate", + "credentialSubject", + "proof" + ] + }, + "W3cJsonLdVerifiablePresentationDto": { + "type": "object", + "properties": { + "proof": { + "type": "object" + }, + "context": { + "type": "array", + "items": { + "type": "object" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "holder": { + "type": "object" + }, + "verifiableCredential": { + "type": "object" + } + }, + "required": [ + "proof", + "context", + "type", + "verifiableCredential" + ] + }, "CredentialFormatDataDto": { "type": "object", "properties": { @@ -1440,6 +1692,9 @@ }, "anoncredsCredential": { "type": "object" + }, + "all": { + "type": "object" } } }, diff --git a/libs/askar/src/agent.utils.ts b/libs/askar/src/agent.utils.ts index 2bd89628296e027edbe67c245d5eda792fe50157..84444829e6494a98509b1c16b9a736732008f060 100644 --- a/libs/askar/src/agent.utils.ts +++ b/libs/askar/src/agent.utils.ts @@ -5,30 +5,31 @@ import { BaseEvent, ConnectionsModule, ConnectionStateChangedEvent, + CredentialEventTypes, CredentialsModule, + CredentialState, + CredentialStateChangedEvent, + DidDocument, DidExchangeRole, DidsModule, EncryptedMessage, + JsonTransformer, Key, KeyDidResolver, KeyType, + OutOfBandState, PeerDidResolver, ProofEventTypes, + ProofExchangeRecord, ProofsModule, ProofState, ProofStateChangedEvent, TypedArrayEncoder, V2CredentialProtocol, V2ProofProtocol, + W3cCredentialsModule, WalletError, WalletKeyExistsError, - OutOfBandState, - CredentialStateChangedEvent, - CredentialEventTypes, - CredentialState, - ProofExchangeRecord, - JsonTransformer, - DidDocument, WebDidResolver, JwkDidResolver, TrustPingResponseReceivedEvent, @@ -74,6 +75,7 @@ export type SubjectMessage = { }; import { Request, Response, Express } from "express"; import url from "url"; +import { JsonLdCredentialFormatService } from "./credo/JsonLdCredentialFormatService"; export const importDidsToWallet = async ( agent: Agent, @@ -216,7 +218,10 @@ export const getAskarAnonCredsIndyModules = (networks: any) => { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, credentialProtocols: [ new V2CredentialProtocol({ - credentialFormats: [new AnonCredsCredentialFormatService()], + credentialFormats: [ + new AnonCredsCredentialFormatService(), + new JsonLdCredentialFormatService(), + ], }), ], }), @@ -255,6 +260,7 @@ export const getAskarAnonCredsIndyModules = (networks: any) => { askar: new AskarModule({ ariesAskar, }), + w3c: new W3cCredentialsModule(), } as const; }; diff --git a/libs/askar/src/askar-rest/rest.controller.ts b/libs/askar/src/askar-rest/rest.controller.ts index adbc7697379bbf79965709327459c5b525b690a2..a9340e09baf02d09bd9c42465683d3983fed3c3d 100644 --- a/libs/askar/src/askar-rest/rest.controller.ts +++ b/libs/askar/src/askar-rest/rest.controller.ts @@ -26,6 +26,8 @@ import { CreateInvitationRequestDto, InvitationFilterDto, AcceptInvitationRequestDto, + OfferJsonCredentialRequests, + SignJsonCredentialRequests, DidRecordDto, } from "@ocm-engine/dtos"; import { AllExceptionsHandler } from "./exception.handler"; @@ -135,6 +137,23 @@ export class RestController { return this.agentService.offerCredential(dto); } + @Post("/credentials/jsonld/offers") + async offerJsonLdCredential(@Body() data: OfferJsonCredentialRequests) { + return this.agentService.offerJsonLdCredential(data.connectionId, data.doc); + } + + @Post("/jsonld/sign") + async signJsonLdCredential(@Body() data: SignJsonCredentialRequests) { + return this.agentService.signJsonLdCredential(data.doc); + } + + @Post("/jsonld/credentials/:cred_id/prepare-verifiable-presentation") + async signJsonLdPresentationByCredId(@Param("cred_id") credentialId: string) { + return this.agentService.prepareVerifiablePresentationByJsonLdCredId( + credentialId, + ); + } + @Get("/credentials") async fetchCredentials(@Query() credentialFilterDto: CredentialFilterDto) { return this.agentService.fetchCredentials(credentialFilterDto); diff --git a/libs/askar/src/askar/agent.service.ts b/libs/askar/src/askar/agent.service.ts index 9439fbba76dce855bf87829ce35bb8c1156a3acf..3fb35aa71487ad32c9c5ef78f68f36c317507c77 100644 --- a/libs/askar/src/askar/agent.service.ts +++ b/libs/askar/src/askar/agent.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable, Logger } from "@nestjs/common"; import { AskarService } from "./askar.service"; import { ConnectionRecordDto, @@ -28,19 +28,30 @@ import { CreateInvitationRequestDto, InvitationFilterDto, DidRecordDto, + OcmError, + W3cCredentialDto, BaseRecordDto, + W3cJsonLdVerifiableCredentialDto, + W3cJsonLdVerifiablePresentationDto, } from "@ocm-engine/dtos"; import { AutoAcceptCredential, BasicMessageRecord, BasicMessageRole, + ClaimFormat, ConnectionRecord, CredentialExchangeRecord, + JsonTransformer, CredentialState, ProofState, Query, ProofExchangeRecord, OutOfBandRecord, + W3cCredential, + W3cCredentialService, + DidRecord, + JsonCredential, + W3cJsonLdVerifiableCredential, } from "@aries-framework/core"; import { AnonCredsRequestedAttribute } from "@aries-framework/anoncreds"; import { uuid } from "@aries-framework/core/build/utils/uuid"; @@ -51,6 +62,7 @@ import { @Injectable() export class AgentService { + private readonly logger = new Logger(AgentService.name); constructor(private readonly askar: AskarService) {} createInvitation = async ( @@ -391,10 +403,238 @@ export class AgentService { return response; }; + signJsonLdCredential = async ( + credToSign: W3cCredentialDto, + ): Promise<W3cJsonLdVerifiableCredentialDto> => { + this.logger.log("Sign json ld credentials"); + + const didRecord = await this.getFirstDidWebRecord(); + const verificationMethodList = + didRecord.didDocument?.verificationMethod || []; + if (!verificationMethodList.length) { + throw new EntityNotFoundError( + "DidDocument does not exists or contains no verification methods", + ); + } + + const verificationMethod = verificationMethodList[0]; + + const w3cServ = + this.askar.agent.context.dependencyManager.resolve(W3cCredentialService); + + credToSign.id = didRecord.did + "?uuid=" + uuid(); + if (credToSign.credentialSubject) { + // @ts-ignore + credToSign.credentialSubject.id = credToSign.id; + } + credToSign.issuer = didRecord.did; + credToSign.issuanceDate = new Date().toISOString(); + let credential: W3cCredential; + try { + credential = JsonTransformer.fromJSON(credToSign, W3cCredential); + } catch (e) { + this.logger.log("Incorrect request parameter", e); + throw new OcmError( + "Invalid JSON-LD data format. Please ensure that your JSON-LD contains the following properties: @context, id, type, issuer, issuanceDate, expirationDate, and credentialSubject.", + ); + } + + const vc = await w3cServ.signCredential(this.askar.agent.context, { + format: ClaimFormat.LdpVc, + credential, + proofType: "Ed25519Signature2018", + verificationMethod: verificationMethod.id, + }); + + // @ts-ignore + const jsonVC = vc.toJson() as W3cJsonLdVerifiableCredentialDto; + this.logger.debug(JSON.stringify(jsonVC, null, 2)); + + return jsonVC; + }; + + prepareVerifiablePresentationByJsonLdCredId = async ( + credentialRecordId: string, + ): Promise<W3cJsonLdVerifiablePresentationDto> => { + const didRecord = await this.getFirstDidWebRecord(); + const verificationMethodList = + didRecord.didDocument?.verificationMethod || []; + if (!verificationMethodList.length) { + throw new EntityNotFoundError( + "DidDocument does not exists or contains no verification methods", + ); + } + + const verificationMethod = verificationMethodList[0]; + + const credFormatData = await this.askar.agent.credentials.getFormatData( + credentialRecordId, + ); + if (!credFormatData.credential?.jsonld) { + throw new OcmError( + "The JSON-LD credential is either not in your wallet, pending approval, or not in the JSON-LD format.", + ); + } + + const jsonLd = credFormatData.credential.jsonld; + const vc = JsonTransformer.fromJSON(jsonLd, W3cJsonLdVerifiableCredential); + + const w3cServ = + this.askar.agent.context.dependencyManager.resolve(W3cCredentialService); + const presentation = await w3cServ.createPresentation({ + credentials: [vc], + id: didRecord.did + "?uuid=" + uuid(), + }); + + const vp = await w3cServ.signPresentation(this.askar.agent.context, { + format: ClaimFormat.LdpVp, + presentation, + proofPurpose: null, + proofType: "Ed25519Signature2018", + challenge: uuid(), + verificationMethod: verificationMethod.id, + }); + + // @ts-ignore + const jsonVP = vp.toJson() as W3cJsonLdVerifiablePresentationDto; + this.logger.debug(JSON.stringify(jsonVP, null, 2)); + + return jsonVP; + }; + + offerJsonLdCredential = async ( + connectionId: string | undefined, + credToSign: W3cCredentialDto, + ): Promise<CredentialOfferResponseDto> => { + this.logger.log("offerJsonLdCredential", connectionId); + + const didRecord = await this.getFirstDidWebRecord(); + const verificationMethodList = + didRecord.didDocument?.verificationMethod || []; + if (!verificationMethodList.length) { + throw new EntityNotFoundError( + "DidDocument does not exists or contains no verification methods", + ); + } + + const verificationMethod = verificationMethodList[0]; + + const w3cServ = + this.askar.agent.context.dependencyManager.resolve(W3cCredentialService); + + credToSign.id = didRecord.did + "?uuid=" + uuid(); + if (credToSign.credentialSubject) { + // @ts-ignore + credToSign.credentialSubject.id = credToSign.id; + } + credToSign.issuer = didRecord.did; + credToSign.issuanceDate = new Date().toISOString(); + let credential: W3cCredential; + try { + credential = JsonTransformer.fromJSON(credToSign, W3cCredential); + } catch (e) { + this.logger.log("Incorrect request parameter", e); + throw new OcmError( + "Invalid JSON-LD data format. Please ensure that your JSON-LD contains the following properties: @context, id, type, issuer, issuanceDate, expirationDate, and credentialSubject.", + ); + } + + const vc = await w3cServ.signCredential(this.askar.agent.context, { + format: ClaimFormat.LdpVc, + credential, + proofType: "Ed25519Signature2018", + verificationMethod: verificationMethod.id, + }); + + // @ts-ignore + const jsonVC = vc.toJson() as JsonCredential; + + if (!connectionId) { + // create connection less credential + const { credentialRecord, message } = + await this.askar.agent.credentials.createOffer({ + protocolVersion: "v2", + credentialFormats: { + jsonld: { + credential: jsonVC, + options: { + proofType: "Ed25519Signature2018", + proofPurpose: "assertionMethod", + }, + }, + }, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }); + + credentialRecord.setTag("xRole", "issuer"); + await this.askar.agent.credentials.update(credentialRecord); + + const outOfBandRecord = await this.askar.agent.oob.createInvitation({ + messages: [message], + handshake: false, + }); + + const credentialUrl = outOfBandRecord.outOfBandInvitation.toUrl({ + domain: this.askar.agentConfig.agentPeerAddress, + }); + + const shortCredentialUrl = `${this.askar.agentConfig.agentPeerAddress}/invitations/${outOfBandRecord.outOfBandInvitation.id}`; + + const dto = new CredentialRecordDto(); + dto.id = credentialRecord.id; + dto.state = credentialRecord.state; + dto.connectionId = credentialRecord.connectionId; + dto.attributes = credentialRecord.credentialAttributes; + dto.createdAt = credentialRecord.createdAt; + dto.tags = credentialRecord.getTags(); + + return { + credentialUrl: credentialUrl, + shortCredentialUrl: shortCredentialUrl, + credentialRecord: dto, + }; + } + + const credentialExchangeRecord = + await this.askar.agent.credentials.offerCredential({ + connectionId: connectionId, + protocolVersion: "v2", + credentialFormats: { + jsonld: { + credential: jsonVC, + options: { + proofType: "Ed25519Signature2018", + proofPurpose: "assertionMethod", + }, + }, + }, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }); + + this.logger.log(credentialExchangeRecord); + + credentialExchangeRecord.setTag("xRole", "issuer"); + await this.askar.agent.credentials.update(credentialExchangeRecord); + + const dto = new CredentialRecordDto(); + dto.id = credentialExchangeRecord.id; + dto.state = credentialExchangeRecord.state; + dto.connectionId = credentialExchangeRecord.connectionId; + dto.attributes = credentialExchangeRecord.credentialAttributes; + dto.createdAt = credentialExchangeRecord.createdAt; + dto.tags = credentialExchangeRecord.getTags(); + + return { + credentialUrl: null, + shortCredentialUrl: null, + credentialRecord: dto, + }; + }; + offerCredential = async ( offerCredentialDto: OfferCredentialRequestDto, ): Promise<CredentialOfferResponseDto> => { - console.log( + this.logger.log( "Incoming request", JSON.stringify(offerCredentialDto, null, 2), ); @@ -636,13 +876,15 @@ export class AgentService { dto.anoncredsRequest = formatData.request?.anoncreds; dto.anoncredsCredential = formatData.credential?.anoncreds; + dto.all = formatData; + return dto; }; requestProof = async ( requestProofDto: RequestProofDto, ): Promise<RequestProofResponseDto> => { - console.log(JSON.stringify(requestProofDto, null, 2)); + this.logger.log(JSON.stringify(requestProofDto, null, 2)); const requestedAttributes: Record<string, AnonCredsRequestedAttribute> = {}; for (const attr of requestProofDto.attributes) { @@ -658,7 +900,7 @@ export class AgentService { } if (!requestProofDto.connectionId) { - console.log("connection Id not detected, creating oob proof"); + this.logger.log("connection Id not detected, creating oob proof"); const { proofRecord, message } = await this.askar.agent.proofs.createRequest({ protocolVersion: "v2", @@ -699,7 +941,7 @@ export class AgentService { }; } - console.log(`${requestProofDto.connectionId} detected, issuing proof`); + this.logger.log(`${requestProofDto.connectionId} detected, issuing proof`); const exchangeRecord = await this.askar.agent.proofs.requestProof({ protocolVersion: "v2", @@ -883,20 +1125,20 @@ export class AgentService { acceptConnectionProof = async ( proofRecordId: string, ): Promise<ProofRecordDto> => { - console.log(`accepting proof request for ${proofRecordId}`); + this.logger.log(`accepting proof request for ${proofRecordId}`); const requestedCredentials = await this.askar.agent.proofs.selectCredentialsForRequest({ proofRecordId, }); - console.log(JSON.stringify(requestedCredentials, null, 2)); + this.logger.log(JSON.stringify(requestedCredentials, null, 2)); const proof = await this.askar.agent.proofs.acceptRequest({ proofRecordId, proofFormats: requestedCredentials.proofFormats, }); - console.log(JSON.stringify(proof, null, 2)); + this.logger.log(JSON.stringify(proof, null, 2)); const response = new ProofRecordDto(); response.id = proof.id; @@ -1054,4 +1296,14 @@ export class AgentService { return response; }; + + private getFirstDidWebRecord = async (): Promise<DidRecord> => { + const didWebs = await this.askar.agent.dids.getCreatedDids({ + method: "web", + }); + if (!didWebs.length) { + throw new EntityNotFoundError("Agent does not have did:web"); + } + return didWebs[0]; + }; } diff --git a/libs/askar/src/credo/JsonLdCredentialFormatService.ts b/libs/askar/src/credo/JsonLdCredentialFormatService.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b35f6c7fcb096796cf6347ce1efb6dd7a2da19c --- /dev/null +++ b/libs/askar/src/credo/JsonLdCredentialFormatService.ts @@ -0,0 +1,582 @@ +import { + JsonLdCredentialFormat, + JsonCredential, + JsonLdFormatDataCredentialDetail, + JsonLdFormatDataVerifiableCredential, + AgentContext, + CredentialFormatService, + Attachment, + AttachmentData, + AriesFrameworkError, + JsonEncoder, + JsonTransformer, + findVerificationMethodByKeyType, + DidResolverService, + ClaimFormat, + W3cCredential, + W3cCredentialService, + W3cJsonLdVerifiableCredential, + CredentialFormatSpec, + JsonLdCredentialDetail, +} from "@aries-framework/core"; +import type { + CredentialFormatAcceptOfferOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatCreateOfferOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatCreateRequestOptions, + CredentialFormatCreateReturn, + CredentialFormatProcessCredentialOptions, + CredentialFormatProcessOptions, + CredentialFormatAutoRespondCredentialOptions, +} from "@aries-framework/core"; +import { areObjectsEqual } from "@aries-framework/core/build/utils"; +import { W3cJsonLdCredentialService } from "@aries-framework/core/build/modules/vc/data-integrity/W3cJsonLdCredentialService"; + +const JSONLD_VC_DETAIL = "aries/ld-proof-vc-detail@v1.0"; +const JSONLD_VC = "aries/ld-proof-vc@v1.0"; + +export class JsonLdCredentialFormatService + implements CredentialFormatService<JsonLdCredentialFormat> +{ + public readonly formatKey = "jsonld" as const; + public readonly credentialRecordType = "w3c" as const; + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @returns object containing associated attachment, formats and filtersAttach elements + * + */ + public async createProposal( + agentContext: AgentContext, + { + credentialFormats, + }: CredentialFormatCreateProposalOptions<JsonLdCredentialFormat>, + ): Promise<CredentialFormatCreateProposalReturn> { + const format = new CredentialFormatSpec({ + format: JSONLD_VC_DETAIL, + }); + + const jsonLdFormat = credentialFormats.jsonld; + if (!jsonLdFormat) { + throw new AriesFrameworkError("Missing jsonld payload in createProposal"); + } + + // this does the validation + JsonTransformer.fromJSON(jsonLdFormat.credential, JsonLdCredentialDetail); + + // jsonLdFormat is now of type JsonLdFormatDataCredentialDetail + const attachment = this.getFormatData(jsonLdFormat, format.attachmentId); + return { format, attachment }; + } + + /** + * Method called on reception of a propose credential message + * @param options the options needed to accept the proposal + */ + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions, + ): Promise<void> { + const credProposalJson = + attachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + + if (!credProposalJson) { + throw new AriesFrameworkError( + "Missing jsonld credential proposal data payload", + ); + } + + // validation is done in here + JsonTransformer.fromJSON(credProposalJson, JsonLdCredentialDetail); + } + + public async acceptProposal( + agentContext: AgentContext, + { + attachmentId, + proposalAttachment, + }: CredentialFormatAcceptProposalOptions<JsonLdCredentialFormat>, + ): Promise<CredentialFormatCreateOfferReturn> { + // if the offer has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachmentId, + format: JSONLD_VC_DETAIL, + }); + + const credentialProposal = + proposalAttachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + JsonTransformer.fromJSON(credentialProposal, JsonLdCredentialDetail); + + const offerData = credentialProposal; + + const attachment = this.getFormatData(offerData, format.attachmentId); + + return { format, attachment }; + } + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the credential offer + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer( + agentContext: AgentContext, + { + credentialFormats, + attachmentId, + }: CredentialFormatCreateOfferOptions<JsonLdCredentialFormat>, + ): Promise<CredentialFormatCreateOfferReturn> { + // if the offer has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachmentId, + format: JSONLD_VC_DETAIL, + }); + + const jsonLdFormat = credentialFormats?.jsonld; + if (!jsonLdFormat) { + throw new AriesFrameworkError("Missing jsonld payload in createOffer"); + } + + // validate + JsonTransformer.fromJSON(jsonLdFormat.credential, JsonLdCredentialDetail); + + const attachment = this.getFormatData(jsonLdFormat, format.attachmentId); + + return { format, attachment }; + } + + public async processOffer( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions, + ) { + const credentialOfferJson = + attachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + + if (!credentialOfferJson) { + throw new AriesFrameworkError( + "Missing jsonld credential offer data payload", + ); + } + + JsonTransformer.fromJSON(credentialOfferJson, JsonLdCredentialDetail); + } + + public async acceptOffer( + agentContext: AgentContext, + { + attachmentId, + offerAttachment, + }: CredentialFormatAcceptOfferOptions<JsonLdCredentialFormat>, + ): Promise<CredentialFormatCreateReturn> { + const credentialOffer = + offerAttachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + + // validate + JsonTransformer.fromJSON(credentialOffer, JsonLdCredentialDetail); + + const format = new CredentialFormatSpec({ + attachmentId, + format: JSONLD_VC_DETAIL, + }); + + const attachment = this.getFormatData(credentialOffer, format.attachmentId); + return { format, attachment }; + } + + /** + * Create a credential attachment format for a credential request. + * + * @param options The object containing all the options for the credential request is derived + * @returns object containing associated attachment, formats and requestAttach elements + * + */ + public async createRequest( + agentContext: AgentContext, + { + credentialFormats, + }: CredentialFormatCreateRequestOptions<JsonLdCredentialFormat>, + ): Promise<CredentialFormatCreateReturn> { + const jsonLdFormat = credentialFormats?.jsonld; + + const format = new CredentialFormatSpec({ + format: JSONLD_VC_DETAIL, + }); + + if (!jsonLdFormat) { + throw new AriesFrameworkError("Missing jsonld payload in createRequest"); + } + + // this does the validation + JsonTransformer.fromJSON(jsonLdFormat.credential, JsonLdCredentialDetail); + + const attachment = this.getFormatData(jsonLdFormat, format.attachmentId); + + return { format, attachment }; + } + + public async processRequest( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions, + ): Promise<void> { + const requestJson = + attachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + + if (!requestJson) { + throw new AriesFrameworkError( + "Missing jsonld credential request data payload", + ); + } + + // validate + JsonTransformer.fromJSON(requestJson, JsonLdCredentialDetail); + } + + public async acceptRequest( + agentContext: AgentContext, + { + credentialFormats, + attachmentId, + requestAttachment, + }: CredentialFormatAcceptRequestOptions<JsonLdCredentialFormat>, + ): Promise<CredentialFormatCreateReturn> { + const w3cJsonLdCredentialService = agentContext.dependencyManager.resolve( + W3cJsonLdCredentialService, + ); + + // sign credential here. credential to be signed is received as the request attachment + // (attachment in the request message from holder to issuer) + const credentialRequest = + requestAttachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + + const verificationMethod = + credentialFormats?.jsonld?.verificationMethod ?? + (await this.deriveVerificationMethod( + agentContext, + credentialRequest.credential, + credentialRequest, + )); + + if (!verificationMethod) { + throw new AriesFrameworkError( + "Missing verification method in credential data", + ); + } + const format = new CredentialFormatSpec({ + attachmentId, + format: JSONLD_VC, + }); + + const options = credentialRequest.options; + + // Get a list of fields found in the options that are not supported at the moment + const unsupportedFields = [ + "challenge", + "domain", + "credentialStatus", + "created", + ] as const; + const foundFields = unsupportedFields.filter( + (field) => options[field] !== undefined, + ); + + if (foundFields.length > 0) { + throw new AriesFrameworkError( + `Some fields are not currently supported in credential options: ${foundFields.join( + ", ", + )}`, + ); + } + + const credential = JsonTransformer.fromJSON( + credentialRequest.credential, + W3cCredential, + ); + + const verifiableCredential = + await w3cJsonLdCredentialService.signCredential(agentContext, { + format: ClaimFormat.LdpVc, + credential, + proofType: credentialRequest.options.proofType, + verificationMethod: verificationMethod, + }); + + const attachment = this.getFormatData( + JsonTransformer.toJSON(verifiableCredential), + format.attachmentId, + ); + return { format, attachment }; + } + + /** + * Derive a verification method using the issuer from the given verifiable credential + * @param credentialAsJson the verifiable credential we want to sign + * @return the verification method derived from this credential and its associated issuer did, keys etc. + */ + private async deriveVerificationMethod( + agentContext: AgentContext, + credentialAsJson: JsonCredential, + credentialRequest: JsonLdFormatDataCredentialDetail, + ): Promise<string> { + const didResolver = + agentContext.dependencyManager.resolve(DidResolverService); + const w3cJsonLdCredentialService = agentContext.dependencyManager.resolve( + W3cJsonLdCredentialService, + ); + + const credential = JsonTransformer.fromJSON( + credentialAsJson, + W3cCredential, + ); + + // extract issuer from vc (can be string or Issuer) + let issuerDid = credential.issuer; + + if (typeof issuerDid !== "string") { + issuerDid = issuerDid.id; + } + // this will throw an error if the issuer did is invalid + const issuerDidDocument = await didResolver.resolveDidDocument( + agentContext, + issuerDid, + ); + + // find first key which matches proof type + const proofType = credentialRequest.options.proofType; + + // actually gets the key type(s) + const keyType = + w3cJsonLdCredentialService.getVerificationMethodTypesByProofType( + proofType, + ); + + if (!keyType || keyType.length === 0) { + throw new AriesFrameworkError( + `No Key Type found for proofType ${proofType}`, + ); + } + + const verificationMethod = await findVerificationMethodByKeyType( + keyType[0], + issuerDidDocument, + ); + if (!verificationMethod) { + throw new AriesFrameworkError( + `Missing verification method for key type ${keyType}`, + ); + } + + return verificationMethod.id; + } + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + agentContext: AgentContext, + { + credentialRecord, + attachment, + requestAttachment, + }: CredentialFormatProcessCredentialOptions, + ): Promise<void> { + const w3cCredentialService = + agentContext.dependencyManager.resolve(W3cCredentialService); + + const credentialAsJson = attachment.getDataAsJson(); + const credential = JsonTransformer.fromJSON( + credentialAsJson, + W3cJsonLdVerifiableCredential, + ); + const requestAsJson = + requestAttachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + + // Verify the credential request matches the credential + this.verifyReceivedCredentialMatchesRequest(credential, requestAsJson); + + // verify signatures of the credential + const result = await w3cCredentialService.verifyCredential(agentContext, { + credential, + }); + if (result && !result.isValid) { + throw new AriesFrameworkError( + `Failed to validate credential, error = ${result.error}`, + ); + } + + const verifiableCredential = await w3cCredentialService.storeCredential( + agentContext, + { + credential, + }, + ); + + credentialRecord.credentials.push({ + credentialRecordType: this.credentialRecordType, + credentialRecordId: verifiableCredential.id, + }); + } + + private verifyReceivedCredentialMatchesRequest( + credential: W3cJsonLdVerifiableCredential, + request: JsonLdFormatDataCredentialDetail, + ): void { + const jsonCredential = JsonTransformer.toJSON(credential); + delete jsonCredential["proof"]; + + const credentialProof = Array.isArray(credential.proof) + ? credential.proof[credential.proof.length - 1] + : credential.proof; + + if ( + request.options.created && + credentialProof.created !== request.options.created + ) { + throw new AriesFrameworkError( + "Received credential proof created does not match created from credential request", + ); + } + + if (credentialProof.domain !== request.options.domain) { + throw new AriesFrameworkError( + "Received credential proof domain does not match domain from credential request", + ); + } + + if (credentialProof.challenge !== request.options.challenge) { + throw new AriesFrameworkError( + "Received credential proof challenge does not match challenge from credential request", + ); + } + + if (credentialProof.type !== request.options.proofType) { + throw new AriesFrameworkError( + "Received credential proof type does not match proof type from credential request", + ); + } + + if (credentialProof.proofPurpose !== request.options.proofPurpose) { + throw new AriesFrameworkError( + "Received credential proof purpose does not match proof purpose from credential request", + ); + } + + // Check whether the received credential (minus the proof) matches the credential request + const requestJsonCredential = JsonTransformer.toJSON(request.credential); + delete requestJsonCredential["proof"]; + if (!areObjectsEqual(jsonCredential, requestJsonCredential)) { + throw new AriesFrameworkError( + "Received credential does not match credential request", + ); + } + + // TODO: add check for the credentialStatus once this is supported in Credo + } + + public supportsFormat(format: string): boolean { + const supportedFormats = [JSONLD_VC_DETAIL, JSONLD_VC]; + + return supportedFormats.includes(format); + } + + public async deleteCredentialById(): Promise<void> { + throw new Error("Not implemented."); + } + + public areCredentialsEqual = ( + message1: Attachment, + message2: Attachment, + ): boolean => { + const obj1 = message1.getDataAsJson(); + const obj2 = message2.getDataAsJson(); + + return areObjectsEqual(obj1, obj2); + }; + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { + offerAttachment, + proposalAttachment, + }: CredentialFormatAutoRespondProposalOptions, + ) { + return this.areCredentialsEqual(proposalAttachment, offerAttachment); + } + + public async shouldAutoRespondToOffer( + agentContext: AgentContext, + { + offerAttachment, + proposalAttachment, + }: CredentialFormatAutoRespondOfferOptions, + ) { + return this.areCredentialsEqual(proposalAttachment, offerAttachment); + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { + offerAttachment, + requestAttachment, + }: CredentialFormatAutoRespondRequestOptions, + ) { + return this.areCredentialsEqual(offerAttachment, requestAttachment); + } + + public async shouldAutoRespondToCredential( + agentContext: AgentContext, + { + requestAttachment, + credentialAttachment, + }: CredentialFormatAutoRespondCredentialOptions, + ) { + const credentialJson = + credentialAttachment.getDataAsJson<JsonLdFormatDataVerifiableCredential>(); + const w3cCredential = JsonTransformer.fromJSON( + credentialJson, + W3cJsonLdVerifiableCredential, + ); + const request = + requestAttachment.getDataAsJson<JsonLdFormatDataCredentialDetail>(); + + try { + // This check is also done in the processCredential method, but we do it here as well + // to be certain we don't skip the check + this.verifyReceivedCredentialMatchesRequest(w3cCredential, request); + + return true; + } catch (error) { + return false; + } + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: "application/json", + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }); + + return attachment; + } +} diff --git a/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-schema.dto.ts b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-schema.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..8401d644094f7ff9bb3f2fe0c851530c3086ade4 --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-schema.dto.ts @@ -0,0 +1,10 @@ +import { IsString } from "class-validator"; +import { IsUri } from "@aries-framework/core/build/utils"; + +export class W3cCredentialSchemaDto { + @IsUri() + public id!: string; + + @IsString() + public type!: string; +} diff --git a/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-status.dto.ts b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-status.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa5026dee7d21ef2b334b1c215d7ae605153613f --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-status.dto.ts @@ -0,0 +1,10 @@ +import { IsString } from "class-validator"; +import { IsUri } from "@aries-framework/core/build/utils"; + +export class W3cCredentialStatusDto { + @IsUri() + public id!: string; + + @IsString() + public type!: string; +} diff --git a/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-subject.dto.ts b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-subject.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..72b17610b028dc5416e2c842f419fe72783bc7ee --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential-subject.dto.ts @@ -0,0 +1,8 @@ +import { IsOptional } from "class-validator"; +import { IsUri } from "@aries-framework/core/build/utils"; + +export class W3cCredentialSubjectDto { + @IsUri() + @IsOptional() + public id?: string; +} diff --git a/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential.dto.ts b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..61b43a26731de751d93452dd377eea94f5a467d3 --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/credential/w3c.credential.dto.ts @@ -0,0 +1,68 @@ +import { Expose, Type } from "class-transformer"; +import { + IsInstance, + IsOptional, + IsRFC3339, + ValidateNested, +} from "class-validator"; + +import { W3cCredentialSchemaDto } from "./w3c.credential-schema.dto"; +import { W3cCredentialStatusDto } from "./w3c.credential-status.dto"; +import { W3cCredentialSubjectDto } from "./w3c.credential-subject.dto"; +import { W3cIssuerDto } from "./w3c.issuer.dto"; +import { IsCredentialJsonLdContext } from "@aries-framework/core/build/modules/vc/validators"; +import { + IsCredentialType, + IsW3cIssuer, + JsonObject, + W3cIssuerTransformer, +} from "@aries-framework/core"; +import { + IsInstanceOrArrayOfInstances, + IsUri, + SingleOrArray, +} from "@aries-framework/core/build/utils"; + +export class W3cCredentialDto { + @Expose({ name: "@context" }) + @IsCredentialJsonLdContext() + public context!: Array<string | JsonObject>; + + @IsOptional() + @IsUri() + public id?: string; + + @IsCredentialType() + public type!: Array<string>; + + @W3cIssuerTransformer() + @IsW3cIssuer() + public issuer!: string | W3cIssuerDto; + + @IsRFC3339() + public issuanceDate!: string; + + @IsRFC3339() + @IsOptional() + public expirationDate?: string; + + @Type(() => W3cCredentialSubjectDto) + @ValidateNested({ each: true }) + @IsInstanceOrArrayOfInstances({ classType: W3cCredentialSubjectDto }) + public credentialSubject!: SingleOrArray<W3cCredentialSubjectDto>; + + @IsOptional() + @Type(() => W3cCredentialSchemaDto) + @ValidateNested({ each: true }) + @IsInstanceOrArrayOfInstances({ + classType: W3cCredentialSchemaDto, + allowEmptyArray: true, + }) + public credentialSchema?: SingleOrArray<W3cCredentialSchemaDto>; + + @IsOptional() + @Type(() => W3cCredentialStatusDto) + @ValidateNested({ each: true }) + @IsInstance(W3cCredentialStatusDto) + public credentialStatus?: W3cCredentialStatusDto; +} diff --git a/libs/dtos/src/dtos/credo/w3c/credential/w3c.issuer.dto.ts b/libs/dtos/src/dtos/credo/w3c/credential/w3c.issuer.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..23b19412df54eceb9eaa60181a095996746083b5 --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/credential/w3c.issuer.dto.ts @@ -0,0 +1,6 @@ +import { IsUri } from "@aries-framework/core/build/utils"; + +export class W3cIssuerDto { + @IsUri() + public id!: string; +} diff --git a/libs/dtos/src/dtos/credo/w3c/data-integrity/linked-data-proof.dto.ts b/libs/dtos/src/dtos/credo/w3c/data-integrity/linked-data-proof.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..645c9a588597ebe7f4a6f5e6ab1e927618ad8fd3 --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/data-integrity/linked-data-proof.dto.ts @@ -0,0 +1,36 @@ +import { IsOptional, IsString } from "class-validator"; +import { IsUri } from "@aries-framework/core/build/utils"; + +export class LinkedDataProofDto { + @IsString() + public type!: string; + + @IsString() + public proofPurpose!: string; + + @IsString() + public verificationMethod!: string; + + @IsString() + public created!: string; + + @IsUri() + @IsOptional() + public domain?: string; + + @IsString() + @IsOptional() + public challenge?: string; + + @IsString() + @IsOptional() + public jws?: string; + + @IsString() + @IsOptional() + public proofValue?: string; + + @IsString() + @IsOptional() + public nonce?: string; +} diff --git a/libs/dtos/src/dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-credential.dto.ts b/libs/dtos/src/dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-credential.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..627f5b309d5d614f069a031a6ef7e8d2aefb0adb --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-credential.dto.ts @@ -0,0 +1,15 @@ +import { ValidateNested } from "class-validator"; +import { + IsInstanceOrArrayOfInstances, + SingleOrArray, +} from "@aries-framework/core/build/utils"; +import { LinkedDataProofTransformer } from "@aries-framework/core/build/modules/vc/data-integrity/models/LinkedDataProof"; +import { LinkedDataProofDto } from "./linked-data-proof.dto"; +import { W3cCredentialDto } from "../credential/w3c.credential.dto"; + +export class W3cJsonLdVerifiableCredentialDto extends W3cCredentialDto { + @LinkedDataProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: LinkedDataProofDto }) + @ValidateNested() + public proof!: SingleOrArray<LinkedDataProofDto>; +} diff --git a/libs/dtos/src/dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-presentation.dto.ts b/libs/dtos/src/dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-presentation.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..b9526063f4b9ca1d269fffc72336985d57328205 --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-presentation.dto.ts @@ -0,0 +1,13 @@ +import { + IsInstanceOrArrayOfInstances, + SingleOrArray, +} from "@aries-framework/core/build/utils"; +import { LinkedDataProofTransformer } from "@aries-framework/core/build/modules/vc/data-integrity/models/LinkedDataProof"; +import { LinkedDataProofDto } from "./linked-data-proof.dto"; +import { W3cPresentationDto } from "../presentation/w3c.presentation.dto"; + +export class W3cJsonLdVerifiablePresentationDto extends W3cPresentationDto { + @LinkedDataProofTransformer() + @IsInstanceOrArrayOfInstances({ classType: LinkedDataProofDto }) + public proof!: SingleOrArray<LinkedDataProofDto>; +} diff --git a/libs/dtos/src/dtos/credo/w3c/presentation/w3c.holder.dto.ts b/libs/dtos/src/dtos/credo/w3c/presentation/w3c.holder.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..d439bcbfc06a85e36aa617c405754bfbff5c91b1 --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/presentation/w3c.holder.dto.ts @@ -0,0 +1,6 @@ +import { IsUri } from "@aries-framework/core/build/utils"; + +export class W3cHolderDto { + @IsUri() + public id!: string; +} diff --git a/libs/dtos/src/dtos/credo/w3c/presentation/w3c.presentation.dto.ts b/libs/dtos/src/dtos/credo/w3c/presentation/w3c.presentation.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f5969baae4a212b15b418b0c313201e3fa771cb --- /dev/null +++ b/libs/dtos/src/dtos/credo/w3c/presentation/w3c.presentation.dto.ts @@ -0,0 +1,45 @@ +import { Expose } from "class-transformer"; +import { ValidateNested, IsOptional } from "class-validator"; + +import { W3cHolderDto } from "./w3c.holder.dto"; +import { + IsVerifiablePresentationType, + JsonObject, + W3cVerifiableCredentialTransformer, +} from "@aries-framework/core"; +import { IsCredentialJsonLdContext } from "@aries-framework/core/build/modules/vc/validators"; +import { + IsInstanceOrArrayOfInstances, + IsUri, + SingleOrArray, +} from "@aries-framework/core/build/utils"; +import { + IsW3cHolder, + W3cHolderTransformer, +} from "@aries-framework/core/build/modules/vc/models/presentation/W3cHolder"; +import { W3cJsonLdVerifiableCredentialDto } from "../data-integrity/w3c.json-ld.verifiable-credential.dto"; + +export class W3cPresentationDto { + @Expose({ name: "@context" }) + @IsCredentialJsonLdContext() + public context!: Array<string | JsonObject>; + + @IsOptional() + @IsUri() + public id?: string; + + @IsVerifiablePresentationType() + public type!: Array<string>; + + @W3cHolderTransformer() + @IsW3cHolder() + @IsOptional() + public holder?: string | W3cHolderDto; + + @W3cVerifiableCredentialTransformer() + @IsInstanceOrArrayOfInstances({ + classType: [W3cJsonLdVerifiableCredentialDto], + }) + @ValidateNested({ each: true }) + public verifiableCredential!: SingleOrArray<W3cJsonLdVerifiableCredentialDto>; +} diff --git a/libs/dtos/src/dtos/generics/credential.formatData.dto.ts b/libs/dtos/src/dtos/generics/credential.formatData.dto.ts index 2060a05ae50e907bbb9335e5a488421ec81db75c..28bbc1b56a10da93eb4b756fc994842cda541899 100644 --- a/libs/dtos/src/dtos/generics/credential.formatData.dto.ts +++ b/libs/dtos/src/dtos/generics/credential.formatData.dto.ts @@ -13,4 +13,6 @@ export class CredentialFormatDataDto { public anoncredsOffer?: AnonCredsCredentialOffer; public anoncredsRequest?: AnonCredsCredentialRequest; public anoncredsCredential?: AnonCredsCredential; + + public all?: unknown; } diff --git a/libs/dtos/src/dtos/requests/offer.credential.request.dto.ts b/libs/dtos/src/dtos/requests/offer.credential.request.dto.ts index 2dc3595ab3fbb8ba6d9704d4f2a7909276adb46e..9cea64c90950d36aadd29ab0bf2765d7576d9e2a 100644 --- a/libs/dtos/src/dtos/requests/offer.credential.request.dto.ts +++ b/libs/dtos/src/dtos/requests/offer.credential.request.dto.ts @@ -7,6 +7,7 @@ import { ValidateNested, } from "class-validator"; import { Type } from "class-transformer"; +import { W3cCredentialDto } from "../credo/w3c/credential/w3c.credential.dto"; export class OfferCredentialAttributes { @IsString() @@ -34,3 +35,16 @@ export class OfferCredentialRequestDto { @Type(() => OfferCredentialAttributes) attributes: Array<OfferCredentialAttributes>; } + +export class OfferJsonCredentialRequests { + @IsString() + @IsNotEmpty() + @IsOptional() + connectionId?: string; + + doc: W3cCredentialDto; +} + +export class SignJsonCredentialRequests { + doc: W3cCredentialDto; +} diff --git a/libs/dtos/src/index.ts b/libs/dtos/src/index.ts index b9c5a35e1e3438b9057f02cfd751ba970342e02b..03062d4d6de377a29760704674c7c7b2d3bebd33 100644 --- a/libs/dtos/src/index.ts +++ b/libs/dtos/src/index.ts @@ -27,6 +27,17 @@ export * from "./dtos/requests/request.proof.dto"; export * from "./dtos/requests/make.basic.message.request.dto"; export * from "./dtos/requests/create.invitation.request.dto"; +export * from "./dtos/credo/w3c/credential/w3c.credential.dto"; +export * from "./dtos/credo/w3c/credential/w3c.credential-schema.dto"; +export * from "./dtos/credo/w3c/credential/w3c.credential-status.dto"; +export * from "./dtos/credo/w3c/credential/w3c.credential-subject.dto"; +export * from "./dtos/credo/w3c/credential/w3c.issuer.dto"; +export * from "./dtos/credo/w3c/presentation/w3c.holder.dto"; +export * from "./dtos/credo/w3c/presentation/w3c.presentation.dto"; +export * from "./dtos/credo/w3c/data-integrity/linked-data-proof.dto"; +export * from "./dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-credential.dto"; +export * from "./dtos/credo/w3c/data-integrity/w3c.json-ld.verifiable-presentation.dto"; + export * from "./dtos/responses/request.proof.response.dto"; export * from "./dtos/responses/credential.offer.response.dto"; export * from "./dtos/responses/create.invitation.response.dto";