From a4b18ca84f9e69126cb4fb13080aa7ab801b8627 Mon Sep 17 00:00:00 2001 From: Ankita Patidar Date: Mon, 9 Jun 2025 16:24:56 +0530 Subject: [PATCH 1/8] feat:sign and verify raw data with the provided key Signed-off-by: Ankita Patidar --- .../multi-tenancy/MultiTenancyController.ts | 61 ++++++++++++++++++- src/controllers/types.ts | 13 ++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index 739463d8..fdfd6ae8 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -27,6 +27,7 @@ import { parseIndyCredentialDefinitionId, parseIndySchemaId, } from '@credo-ts/anoncreds' +import { assertAskarWallet } from '@credo-ts/askar/build/utils/assertAskarWallet' import { AcceptCredentialOfferOptions, Agent, @@ -89,7 +90,7 @@ import { CreateProofRequestOobOptions, CreateOfferOobOptions, CreateSchemaInput, -} from '../types' + VerifyDataOptions , SignDataOptions } from '../types' import { Body, Controller, Delete, Get, Post, Query, Route, Tags, Path, Example, Security, Response } from 'tsoa' @@ -1913,4 +1914,62 @@ export class MultiTenancyController extends Controller { throw ErrorHandlingService.handle(error) } } + + + /** + * Sign data using a key + * + * @param tenantId Tenant identifier + * @param request Sign options + * data - Data has to be in base64 format + * publicKeyBase58 - Public key in base58 format + * @returns Signature in base64 format + */ + @Security('apiKey') + @Post('/sign/:tenantId') + public async sign(@Path('tenantId') tenantId: string, @Body() request: SignDataOptions) { + try { + const signature = await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { + assertAskarWallet(tenantAgent.context.wallet) + const signature = await tenantAgent.context.wallet.sign({ + data: TypedArrayEncoder.fromBase64(request.data), + key: Key.fromPublicKeyBase58(request.publicKeyBase58, request.keyType), + }) + return TypedArrayEncoder.toBase64(signature) + }) + return signature + } catch (error) { + throw ErrorHandlingService.handle(error) + } + } + + /** + * Verify data using a key + * + * @param tenantId Tenant identifier + * @param request Verify options + * data - Data has to be in base64 format + * publicKeyBase58 - Public key in base58 format + * signature - Signature in base64 format + * @returns isValidSignature - true if signature is valid, false otherwise + */ + @Security('apiKey') + @Post('/verify/:tenantId') + public async verify(@Path('tenantId') tenantId: string, @Body() request: VerifyDataOptions) { + try { + const isValidSignature = await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { + assertAskarWallet(tenantAgent.context.wallet) + const isValidSignature = await tenantAgent.context.wallet.verify({ + data: TypedArrayEncoder.fromBase64(request.data), + key: Key.fromPublicKeyBase58(request.publicKeyBase58, request.keyType), + signature: TypedArrayEncoder.fromBase64(request.signature), + }) + return isValidSignature + }) + return isValidSignature + } catch (error) { + throw ErrorHandlingService.handle(error) + } + } } + diff --git a/src/controllers/types.ts b/src/controllers/types.ts index 3753d6f2..c0810cba 100644 --- a/src/controllers/types.ts +++ b/src/controllers/types.ts @@ -388,3 +388,16 @@ export interface SchemaMetadata { * @example "ea4e5e69-fc04-465a-90d2-9f8ff78aa71d" */ export type ThreadId = string + +export type SignDataOptions = { + data: string + keyType: KeyType + publicKeyBase58: string +} + +export type VerifyDataOptions = { + data: string + keyType: KeyType + publicKeyBase58: string + signature: string +} From f84bd189c807fb0fccb8a007826aff4836c3d879 Mon Sep 17 00:00:00 2001 From: Ankita Patidar Date: Mon, 9 Jun 2025 18:19:36 +0530 Subject: [PATCH 2/8] sign and verify credential payload Signed-off-by: Ankita Patidar --- .../multi-tenancy/MultiTenancyController.ts | 59 ++++++++++++++++++- src/controllers/types.ts | 19 ++++-- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index fdfd6ae8..52bb3cdb 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -15,6 +15,8 @@ import type { ProofExchangeRecordProps, ProofsProtocolVersionType, Routing, + W3cJsonLdSignCredentialOptions, + W3cJwtSignCredentialOptions } from '@credo-ts/core' import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '@credo-ts/indy-vdr' import type { QuestionAnswerRecord, ValidResponse } from '@credo-ts/question-answer' @@ -46,7 +48,10 @@ import { injectable, createPeerDidDocumentFromServices, PeerDidNumAlgo, -} from '@credo-ts/core' + W3cJsonLdVerifiableCredential, + W3cCredential, + + W3cVerifyCredentialOptions} from '@credo-ts/core' import { QuestionAnswerRole, QuestionAnswerState } from '@credo-ts/question-answer' import axios from 'axios' import * as fs from 'fs' @@ -1931,6 +1936,9 @@ export class MultiTenancyController extends Controller { try { const signature = await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { assertAskarWallet(tenantAgent.context.wallet) + + tenantAgent.w3cCredentials + const signature = await tenantAgent.context.wallet.sign({ data: TypedArrayEncoder.fromBase64(request.data), key: Key.fromPublicKeyBase58(request.publicKeyBase58, request.keyType), @@ -1971,5 +1979,54 @@ export class MultiTenancyController extends Controller { throw ErrorHandlingService.handle(error) } } + + @Security('apiKey') + @Post('/credential/sign/:tenantId') + public async signCredential( + @Path('tenantId') tenantId: string, + @Query('storeCredential') storeCredential: boolean, + @Body() credentialToSign: W3cJsonLdSignCredentialOptions | W3cJwtSignCredentialOptions + ) { + let storedCredential + let formattedCredential + try { + await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { + if(credentialToSign.credential instanceof W3cCredential) { + const signedCred = await tenantAgent.w3cCredentials.signCredential(credentialToSign) + formattedCredential = JsonTransformer.fromJSON(signedCred, W3cJsonLdVerifiableCredential) //TODO: add support for JWT format as well + if(storeCredential) { + storedCredential = await tenantAgent.w3cCredentials.storeCredential({ credential: formattedCredential }) + } + } else { + throw new Error('Credential Type not supported') + } + }) + return storedCredential ?? formattedCredential + } catch (error) { + throw ErrorHandlingService.handle(error) + } + } + + @Security('apiKey') + @Post('/credential/verify/:tenantId') + public async verifyCredential( + @Path('tenantId') tenantId: string, + @Body() credentialToVerify: W3cVerifyCredentialOptions + ) { + let formattedCredential + try { + await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { + if(credentialToVerify.credential instanceof W3cCredential) { + const signedCred = await tenantAgent.w3cCredentials.verifyCredential(credentialToVerify) + formattedCredential = JsonTransformer.fromJSON(signedCred, W3cJsonLdVerifiableCredential) //TODO: add support for other credential format as well + } else { + throw new Error('Credential Type not supported') + } + }) + return formattedCredential + } catch (error) { + throw ErrorHandlingService.handle(error) + } + } } diff --git a/src/controllers/types.ts b/src/controllers/types.ts index c0810cba..b74056fb 100644 --- a/src/controllers/types.ts +++ b/src/controllers/types.ts @@ -25,7 +25,9 @@ import type { Attachment, KeyType, JsonLdCredentialFormat, + JsonObject, } from '@credo-ts/core' +import type { SingleOrArray } from '@credo-ts/core/build/utils' import type { DIDDocument } from 'did-resolver' export type TenantConfig = Pick & { @@ -37,10 +39,6 @@ export interface AgentInfo { endpoints: string[] isInitialized: boolean publicDid: void - // publicDid?: { - // did: string - // verkey: string - // } } export interface AgentMessageType { @@ -401,3 +399,16 @@ export type VerifyDataOptions = { publicKeyBase58: string signature: string } + +export interface jsonLdCredentialOptions { + '@context': Array + type: Array + credentialSubject: SingleOrArray + proofType: string +} + +export interface credentialPayloadToSign { + issuerDID: string + method: string + credential: jsonLdCredentialOptions // TODO: add support for other credential format +} From 111ef3f670efaa2f9f6a51c2ad3123668ab53959 Mon Sep 17 00:00:00 2001 From: Krishna Waske Date: Tue, 10 Jun 2025 19:29:31 +0530 Subject: [PATCH 3/8] fix: update sign API Signed-off-by: Krishna Waske --- .../multi-tenancy/MultiTenancyController.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index 52bb3cdb..dbff746e 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -1935,9 +1935,9 @@ export class MultiTenancyController extends Controller { public async sign(@Path('tenantId') tenantId: string, @Body() request: SignDataOptions) { try { const signature = await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { - assertAskarWallet(tenantAgent.context.wallet) + // assertAskarWallet(tenantAgent.context.wallet) - tenantAgent.w3cCredentials + // tenantAgent.w3cCredentials const signature = await tenantAgent.context.wallet.sign({ data: TypedArrayEncoder.fromBase64(request.data), @@ -1985,21 +1985,27 @@ export class MultiTenancyController extends Controller { public async signCredential( @Path('tenantId') tenantId: string, @Query('storeCredential') storeCredential: boolean, - @Body() credentialToSign: W3cJsonLdSignCredentialOptions | W3cJwtSignCredentialOptions + @Body() credentialToSign: any ) { let storedCredential let formattedCredential try { await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { - if(credentialToSign.credential instanceof W3cCredential) { - const signedCred = await tenantAgent.w3cCredentials.signCredential(credentialToSign) - formattedCredential = JsonTransformer.fromJSON(signedCred, W3cJsonLdVerifiableCredential) //TODO: add support for JWT format as well - if(storeCredential) { - storedCredential = await tenantAgent.w3cCredentials.storeCredential({ credential: formattedCredential }) - } - } else { - throw new Error('Credential Type not supported') - } + // if(credentialToSign.credential instanceof W3cCredential) { + const {credential, ...credentialObject} = credentialToSign + console.log("THis is credential", JSON.stringify(credential, null, 2)) + const transformedCredential = JsonTransformer.fromJSON(credentialToSign.credential, W3cCredential) + console.log("THis is transformedCredential", JSON.stringify(transformedCredential, null, 2)) + const signedCred = await tenantAgent.w3cCredentials.signCredential({credential: transformedCredential, ...credentialObject}) as W3cJsonLdVerifiableCredential + formattedCredential = signedCred.toJson() + // const signedJson = JsonTransformer.toJSON(signedCred, W3cJsonLdVerifiableCredential) + // formattedCredential = JsonTransformer.fromJSON(signedCred, W3cJsonLdVerifiableCredential) //TODO: add support for JWT format as well + // if(storeCredential) { + // storedCredential = await tenantAgent.w3cCredentials.storeCredential({ credential: formattedCredential }) + // } + // } else { + // throw new Error('Credential Type not supported') + // } }) return storedCredential ?? formattedCredential } catch (error) { From ba70c10df7b241c63645e4965210e59087203381 Mon Sep 17 00:00:00 2001 From: Krishna Waske Date: Wed, 11 Jun 2025 14:20:12 +0530 Subject: [PATCH 4/8] fix: verification logic Signed-off-by: Krishna Waske --- .../multi-tenancy/MultiTenancyController.ts | 31 +-- src/routes/routes.ts | 159 ++++++++++++ src/routes/swagger.json | 230 ++++++++++++++++++ 3 files changed, 395 insertions(+), 25 deletions(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index dbff746e..0ce5ef0d 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -14,10 +14,7 @@ import type { PeerDidNumAlgo2CreateOptions, ProofExchangeRecordProps, ProofsProtocolVersionType, - Routing, - W3cJsonLdSignCredentialOptions, - W3cJwtSignCredentialOptions -} from '@credo-ts/core' + Routing} from '@credo-ts/core' import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '@credo-ts/indy-vdr' import type { QuestionAnswerRecord, ValidResponse } from '@credo-ts/question-answer' import type { TenantRecord } from '@credo-ts/tenants' @@ -49,9 +46,7 @@ import { createPeerDidDocumentFromServices, PeerDidNumAlgo, W3cJsonLdVerifiableCredential, - W3cCredential, - - W3cVerifyCredentialOptions} from '@credo-ts/core' + W3cCredential} from '@credo-ts/core' import { QuestionAnswerRole, QuestionAnswerState } from '@credo-ts/question-answer' import axios from 'axios' import * as fs from 'fs' @@ -1991,21 +1986,10 @@ export class MultiTenancyController extends Controller { let formattedCredential try { await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { - // if(credentialToSign.credential instanceof W3cCredential) { const {credential, ...credentialObject} = credentialToSign - console.log("THis is credential", JSON.stringify(credential, null, 2)) const transformedCredential = JsonTransformer.fromJSON(credentialToSign.credential, W3cCredential) - console.log("THis is transformedCredential", JSON.stringify(transformedCredential, null, 2)) const signedCred = await tenantAgent.w3cCredentials.signCredential({credential: transformedCredential, ...credentialObject}) as W3cJsonLdVerifiableCredential formattedCredential = signedCred.toJson() - // const signedJson = JsonTransformer.toJSON(signedCred, W3cJsonLdVerifiableCredential) - // formattedCredential = JsonTransformer.fromJSON(signedCred, W3cJsonLdVerifiableCredential) //TODO: add support for JWT format as well - // if(storeCredential) { - // storedCredential = await tenantAgent.w3cCredentials.storeCredential({ credential: formattedCredential }) - // } - // } else { - // throw new Error('Credential Type not supported') - // } }) return storedCredential ?? formattedCredential } catch (error) { @@ -2017,17 +2001,14 @@ export class MultiTenancyController extends Controller { @Post('/credential/verify/:tenantId') public async verifyCredential( @Path('tenantId') tenantId: string, - @Body() credentialToVerify: W3cVerifyCredentialOptions + @Body() credentialToVerify: any ) { let formattedCredential try { await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { - if(credentialToVerify.credential instanceof W3cCredential) { - const signedCred = await tenantAgent.w3cCredentials.verifyCredential(credentialToVerify) - formattedCredential = JsonTransformer.fromJSON(signedCred, W3cJsonLdVerifiableCredential) //TODO: add support for other credential format as well - } else { - throw new Error('Credential Type not supported') - } + const transformedCredential = JsonTransformer.fromJSON(credentialToVerify?.credential, W3cJsonLdVerifiableCredential) + const signedCred = await tenantAgent.w3cCredentials.verifyCredential({credential: transformedCredential}) + formattedCredential = signedCred }) return formattedCredential } catch (error) { diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 4566e855..3ad45f0b 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -693,6 +693,16 @@ const models: TsoaRoute.Models = { "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"content":{"dataType":"string","required":true}},"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "SignDataOptions": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"ref":"KeyType","required":true},"data":{"dataType":"string","required":true}},"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "VerifyDataOptions": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"signature":{"dataType":"string","required":true},"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"ref":"KeyType","required":true},"data":{"dataType":"string","required":true}},"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "DidRegistrationSecretOptions": { "dataType": "refAlias", "type": {"ref":"Record_string.unknown_","validators":{}}, @@ -3546,6 +3556,155 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.post('/multi-tenancy/sign/:tenantId', + authenticateMiddleware([{"apiKey":[]}]), + ...(fetchMiddlewares(MultiTenancyController)), + ...(fetchMiddlewares(MultiTenancyController.prototype.sign)), + + async function MultiTenancyController_sign(request: ExRequest, response: ExResponse, next: any) { + const args: Record = { + tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, + request: {"in":"body","name":"request","required":true,"ref":"SignDataOptions"}, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ args, request, response }); + + const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; + + const controller: any = await container.get(MultiTenancyController); + if (typeof controller['setStatus'] === 'function') { + controller.setStatus(undefined); + } + + await templateService.apiHandler({ + methodName: 'sign', + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + }); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.post('/multi-tenancy/verify/:tenantId', + authenticateMiddleware([{"apiKey":[]}]), + ...(fetchMiddlewares(MultiTenancyController)), + ...(fetchMiddlewares(MultiTenancyController.prototype.verify)), + + async function MultiTenancyController_verify(request: ExRequest, response: ExResponse, next: any) { + const args: Record = { + tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, + request: {"in":"body","name":"request","required":true,"ref":"VerifyDataOptions"}, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ args, request, response }); + + const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; + + const controller: any = await container.get(MultiTenancyController); + if (typeof controller['setStatus'] === 'function') { + controller.setStatus(undefined); + } + + await templateService.apiHandler({ + methodName: 'verify', + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + }); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.post('/multi-tenancy/credential/sign/:tenantId', + authenticateMiddleware([{"apiKey":[]}]), + ...(fetchMiddlewares(MultiTenancyController)), + ...(fetchMiddlewares(MultiTenancyController.prototype.signCredential)), + + async function MultiTenancyController_signCredential(request: ExRequest, response: ExResponse, next: any) { + const args: Record = { + tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, + storeCredential: {"in":"query","name":"storeCredential","required":true,"dataType":"boolean"}, + credentialToSign: {"in":"body","name":"credentialToSign","required":true,"dataType":"any"}, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ args, request, response }); + + const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; + + const controller: any = await container.get(MultiTenancyController); + if (typeof controller['setStatus'] === 'function') { + controller.setStatus(undefined); + } + + await templateService.apiHandler({ + methodName: 'signCredential', + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + }); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.post('/multi-tenancy/credential/verify/:tenantId', + authenticateMiddleware([{"apiKey":[]}]), + ...(fetchMiddlewares(MultiTenancyController)), + ...(fetchMiddlewares(MultiTenancyController.prototype.verifyCredential)), + + async function MultiTenancyController_verifyCredential(request: ExRequest, response: ExResponse, next: any) { + const args: Record = { + tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, + credentialToVerify: {"in":"body","name":"credentialToVerify","required":true,"dataType":"any"}, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ args, request, response }); + + const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; + + const controller: any = await container.get(MultiTenancyController); + if (typeof controller['setStatus'] === 'function') { + controller.setStatus(undefined); + } + + await templateService.apiHandler({ + methodName: 'verifyCredential', + controller, + response, + next, + validatedArgs, + successStatus: undefined, + }); + } catch (err) { + return next(err); + } + }); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.post('/transactions/endorse', authenticateMiddleware([{"apiKey":[]}]), ...(fetchMiddlewares(EndorserTransactionController)), diff --git a/src/routes/swagger.json b/src/routes/swagger.json index 8eea6fe5..23dda59b 100644 --- a/src/routes/swagger.json +++ b/src/routes/swagger.json @@ -1625,6 +1625,48 @@ "type": "object", "description": "Construct a type with a set of properties K of type T" }, + "SignDataOptions": { + "properties": { + "publicKeyBase58": { + "type": "string" + }, + "keyType": { + "$ref": "#/components/schemas/KeyType" + }, + "data": { + "type": "string" + } + }, + "required": [ + "publicKeyBase58", + "keyType", + "data" + ], + "type": "object" + }, + "VerifyDataOptions": { + "properties": { + "signature": { + "type": "string" + }, + "publicKeyBase58": { + "type": "string" + }, + "keyType": { + "$ref": "#/components/schemas/KeyType" + }, + "data": { + "type": "string" + } + }, + "required": [ + "signature", + "publicKeyBase58", + "keyType", + "data" + ], + "type": "object" + }, "DidRegistrationSecretOptions": { "$ref": "#/components/schemas/Record_string.unknown_" }, @@ -6336,6 +6378,194 @@ } } }, + "/multi-tenancy/sign/{tenantId}": { + "post": { + "operationId": "Sign", + "responses": { + "200": { + "description": "Signature in base64 format", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + }, + "description": "Sign data using a key", + "tags": [ + "MultiTenancy" + ], + "security": [ + { + "apiKey": [] + } + ], + "parameters": [ + { + "description": "Tenant identifier", + "in": "path", + "name": "tenantId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Sign options\ndata - Data has to be in base64 format\npublicKeyBase58 - Public key in base58 format", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignDataOptions", + "description": "Sign options\ndata - Data has to be in base64 format\npublicKeyBase58 - Public key in base58 format" + } + } + } + } + } + }, + "/multi-tenancy/verify/{tenantId}": { + "post": { + "operationId": "Verify", + "responses": { + "200": { + "description": "isValidSignature - true if signature is valid, false otherwise", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + } + }, + "description": "Verify data using a key", + "tags": [ + "MultiTenancy" + ], + "security": [ + { + "apiKey": [] + } + ], + "parameters": [ + { + "description": "Tenant identifier", + "in": "path", + "name": "tenantId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Verify options\ndata - Data has to be in base64 format\npublicKeyBase58 - Public key in base58 format\nsignature - Signature in base64 format", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerifyDataOptions", + "description": "Verify options\ndata - Data has to be in base64 format\npublicKeyBase58 - Public key in base58 format\nsignature - Signature in base64 format" + } + } + } + } + } + }, + "/multi-tenancy/credential/sign/{tenantId}": { + "post": { + "operationId": "SignCredential", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "MultiTenancy" + ], + "security": [ + { + "apiKey": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "tenantId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "storeCredential", + "required": true, + "schema": { + "type": "boolean" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "/multi-tenancy/credential/verify/{tenantId}": { + "post": { + "operationId": "VerifyCredential", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "MultiTenancy" + ], + "security": [ + { + "apiKey": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "tenantId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, "/transactions/endorse": { "post": { "operationId": "EndorserTransaction", From dacedd3c2cfb86e7a3cc7b1668af34ec2bab99a5 Mon Sep 17 00:00:00 2001 From: Krishna Waske Date: Wed, 11 Jun 2025 18:49:24 +0530 Subject: [PATCH 5/8] fix: final touches Signed-off-by: Krishna Waske --- .../multi-tenancy/MultiTenancyController.ts | 5 +- src/routes/routes.ts | 103 ++++++- src/routes/swagger.json | 268 +++++++++++++++++- 3 files changed, 373 insertions(+), 3 deletions(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index 0ce5ef0d..0e07d6db 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -1980,7 +1980,7 @@ export class MultiTenancyController extends Controller { public async signCredential( @Path('tenantId') tenantId: string, @Query('storeCredential') storeCredential: boolean, - @Body() credentialToSign: any + @Body() credentialToSign: W3cJsonLdVerifiableCredential | any ) { let storedCredential let formattedCredential @@ -1990,6 +1990,9 @@ export class MultiTenancyController extends Controller { const transformedCredential = JsonTransformer.fromJSON(credentialToSign.credential, W3cCredential) const signedCred = await tenantAgent.w3cCredentials.signCredential({credential: transformedCredential, ...credentialObject}) as W3cJsonLdVerifiableCredential formattedCredential = signedCred.toJson() + if (storeCredential) { + storedCredential = await tenantAgent.w3cCredentials.storeCredential({ credential: signedCred }) + } }) return storedCredential ?? formattedCredential } catch (error) { diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 3ad45f0b..c67d2d68 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -703,6 +703,107 @@ const models: TsoaRoute.Models = { "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"signature":{"dataType":"string","required":true},"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"ref":"KeyType","required":true},"data":{"dataType":"string","required":true}},"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "LinkedDataProof": { + "dataType": "refObject", + "properties": { + "type": {"dataType":"string","required":true}, + "proofPurpose": {"dataType":"string","required":true}, + "verificationMethod": {"dataType":"string","required":true}, + "created": {"dataType":"string","required":true}, + "domain": {"dataType":"string"}, + "challenge": {"dataType":"string"}, + "jws": {"dataType":"string"}, + "proofValue": {"dataType":"string"}, + "nonce": {"dataType":"string"}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "DataIntegrityProof": { + "dataType": "refObject", + "properties": { + "type": {"dataType":"string","required":true}, + "cryptosuite": {"dataType":"string","required":true}, + "proofPurpose": {"dataType":"string","required":true}, + "verificationMethod": {"dataType":"string","required":true}, + "domain": {"dataType":"string"}, + "challenge": {"dataType":"string"}, + "nonce": {"dataType":"string"}, + "created": {"dataType":"string"}, + "expires": {"dataType":"string"}, + "proofValue": {"dataType":"string"}, + "previousProof": {"dataType":"string"}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "SingleOrArray_LinkedDataProof-or-DataIntegrityProof_": { + "dataType": "refAlias", + "type": {"dataType":"union","subSchemas":[{"dataType":"union","subSchemas":[{"ref":"LinkedDataProof"},{"ref":"DataIntegrityProof"}]},{"dataType":"array","array":{"dataType":"union","subSchemas":[{"ref":"LinkedDataProof"},{"ref":"DataIntegrityProof"}]}}],"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "W3cIssuer": { + "dataType": "refObject", + "properties": { + "id": {"dataType":"string","required":true}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "W3cCredentialSubject": { + "dataType": "refObject", + "properties": { + "id": {"dataType":"string"}, + "claims": {"ref":"Record_string.unknown_"}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "SingleOrArray_W3cCredentialSubject_": { + "dataType": "refAlias", + "type": {"dataType":"union","subSchemas":[{"ref":"W3cCredentialSubject"},{"dataType":"array","array":{"dataType":"refObject","ref":"W3cCredentialSubject"}}],"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "W3cCredentialSchema": { + "dataType": "refObject", + "properties": { + "id": {"dataType":"string","required":true}, + "type": {"dataType":"string","required":true}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "SingleOrArray_W3cCredentialSchema_": { + "dataType": "refAlias", + "type": {"dataType":"union","subSchemas":[{"ref":"W3cCredentialSchema"},{"dataType":"array","array":{"dataType":"refObject","ref":"W3cCredentialSchema"}}],"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "W3cCredentialStatus": { + "dataType": "refObject", + "properties": { + "id": {"dataType":"string","required":true}, + "type": {"dataType":"string","required":true}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "W3cJsonLdVerifiableCredential": { + "dataType": "refObject", + "properties": { + "context": {"dataType":"array","array":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"JsonObject"}]},"required":true}, + "id": {"dataType":"string"}, + "type": {"dataType":"array","array":{"dataType":"string"},"required":true}, + "issuer": {"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"W3cIssuer"}],"required":true}, + "issuanceDate": {"dataType":"string","required":true}, + "expirationDate": {"dataType":"string"}, + "credentialSubject": {"ref":"SingleOrArray_W3cCredentialSubject_","required":true}, + "credentialSchema": {"ref":"SingleOrArray_W3cCredentialSchema_"}, + "credentialStatus": {"ref":"W3cCredentialStatus"}, + "proof": {"ref":"SingleOrArray_LinkedDataProof-or-DataIntegrityProof_","required":true}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "DidRegistrationSecretOptions": { "dataType": "refAlias", "type": {"ref":"Record_string.unknown_","validators":{}}, @@ -3639,7 +3740,7 @@ export function RegisterRoutes(app: Router) { const args: Record = { tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, storeCredential: {"in":"query","name":"storeCredential","required":true,"dataType":"boolean"}, - credentialToSign: {"in":"body","name":"credentialToSign","required":true,"dataType":"any"}, + credentialToSign: {"in":"body","name":"credentialToSign","required":true,"dataType":"union","subSchemas":[{"ref":"W3cJsonLdVerifiableCredential"},{"dataType":"any"}]}, }; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/src/routes/swagger.json b/src/routes/swagger.json index 23dda59b..56b0e254 100644 --- a/src/routes/swagger.json +++ b/src/routes/swagger.json @@ -1667,6 +1667,265 @@ ], "type": "object" }, + "LinkedDataProof": { + "description": "Linked Data Proof", + "properties": { + "type": { + "type": "string" + }, + "proofPurpose": { + "type": "string" + }, + "verificationMethod": { + "type": "string" + }, + "created": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "jws": { + "type": "string" + }, + "proofValue": { + "type": "string" + }, + "nonce": { + "type": "string" + } + }, + "required": [ + "type", + "proofPurpose", + "verificationMethod", + "created" + ], + "type": "object", + "additionalProperties": false + }, + "DataIntegrityProof": { + "description": "Linked Data Proof", + "properties": { + "type": { + "type": "string" + }, + "cryptosuite": { + "type": "string" + }, + "proofPurpose": { + "type": "string" + }, + "verificationMethod": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "created": { + "type": "string" + }, + "expires": { + "type": "string" + }, + "proofValue": { + "type": "string" + }, + "previousProof": { + "type": "string" + } + }, + "required": [ + "type", + "cryptosuite", + "proofPurpose", + "verificationMethod" + ], + "type": "object", + "additionalProperties": false + }, + "SingleOrArray_LinkedDataProof-or-DataIntegrityProof_": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/LinkedDataProof" + }, + { + "$ref": "#/components/schemas/DataIntegrityProof" + } + ] + }, + { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/LinkedDataProof" + }, + { + "$ref": "#/components/schemas/DataIntegrityProof" + } + ] + }, + "type": "array" + } + ] + }, + "W3cIssuer": { + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object", + "additionalProperties": false + }, + "W3cCredentialSubject": { + "properties": { + "id": { + "type": "string" + }, + "claims": { + "$ref": "#/components/schemas/Record_string.unknown_" + } + }, + "type": "object", + "additionalProperties": false + }, + "SingleOrArray_W3cCredentialSubject_": { + "anyOf": [ + { + "$ref": "#/components/schemas/W3cCredentialSubject" + }, + { + "items": { + "$ref": "#/components/schemas/W3cCredentialSubject" + }, + "type": "array" + } + ] + }, + "W3cCredentialSchema": { + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ], + "type": "object", + "additionalProperties": false + }, + "SingleOrArray_W3cCredentialSchema_": { + "anyOf": [ + { + "$ref": "#/components/schemas/W3cCredentialSchema" + }, + { + "items": { + "$ref": "#/components/schemas/W3cCredentialSchema" + }, + "type": "array" + } + ] + }, + "W3cCredentialStatus": { + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ], + "type": "object", + "additionalProperties": false + }, + "W3cJsonLdVerifiableCredential": { + "properties": { + "context": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/JsonObject" + } + ] + }, + "type": "array" + }, + "id": { + "type": "string" + }, + "type": { + "items": { + "type": "string" + }, + "type": "array" + }, + "issuer": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/W3cIssuer" + } + ] + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialSubject": { + "$ref": "#/components/schemas/SingleOrArray_W3cCredentialSubject_" + }, + "credentialSchema": { + "$ref": "#/components/schemas/SingleOrArray_W3cCredentialSchema_" + }, + "credentialStatus": { + "$ref": "#/components/schemas/W3cCredentialStatus" + }, + "proof": { + "$ref": "#/components/schemas/SingleOrArray_LinkedDataProof-or-DataIntegrityProof_" + } + }, + "required": [ + "context", + "type", + "issuer", + "issuanceDate", + "credentialSubject", + "proof" + ], + "type": "object", + "additionalProperties": false + }, "DidRegistrationSecretOptions": { "$ref": "#/components/schemas/Record_string.unknown_" }, @@ -6519,7 +6778,14 @@ "required": true, "content": { "application/json": { - "schema": {} + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/W3cJsonLdVerifiableCredential" + }, + {} + ] + } } } } From bade5cd23ea80c3d9a5faaa7721090bdfcc49af4 Mon Sep 17 00:00:00 2001 From: Krishna Waske Date: Thu, 12 Jun 2025 12:23:50 +0530 Subject: [PATCH 6/8] fix: verify credential even with credential status object present in it Signed-off-by: Krishna Waske --- .../multi-tenancy/MultiTenancyController.ts | 7 +- src/controllers/types.ts | 7 + src/routes/routes.ts | 51 +++++- src/routes/swagger.json | 148 +++++++++++++++++- 4 files changed, 208 insertions(+), 5 deletions(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index 0e07d6db..04c02508 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -1,7 +1,7 @@ /* eslint-disable prettier/prettier */ import type { RestAgentModules, RestMultiTenantAgentModules } from '../../cliAgent' import type { Version } from '../examples' -import type { RecipientKeyOption, SchemaMetadata } from '../types' +import type { RecipientKeyOption, SafeW3cJsonLdVerifyCredentialOptions, SchemaMetadata } from '../types' import type { PolygonDidCreateOptions } from '@ayanworks/credo-polygon-w3c-module/build/dids' import type { AcceptProofRequestOptions, @@ -2004,13 +2004,14 @@ export class MultiTenancyController extends Controller { @Post('/credential/verify/:tenantId') public async verifyCredential( @Path('tenantId') tenantId: string, - @Body() credentialToVerify: any + @Body() credentialToVerify: SafeW3cJsonLdVerifyCredentialOptions | any ) { let formattedCredential try { await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { + const {credential, ...credentialOptions}= credentialToVerify const transformedCredential = JsonTransformer.fromJSON(credentialToVerify?.credential, W3cJsonLdVerifiableCredential) - const signedCred = await tenantAgent.w3cCredentials.verifyCredential({credential: transformedCredential}) + const signedCred = await tenantAgent.w3cCredentials.verifyCredential({credential: transformedCredential, ...credentialOptions}) formattedCredential = signedCred }) return formattedCredential diff --git a/src/controllers/types.ts b/src/controllers/types.ts index b74056fb..f3acb079 100644 --- a/src/controllers/types.ts +++ b/src/controllers/types.ts @@ -26,9 +26,12 @@ import type { KeyType, JsonLdCredentialFormat, JsonObject, + W3cJsonLdVerifyCredentialOptions, + DataIntegrityProofOptions, } from '@credo-ts/core' import type { SingleOrArray } from '@credo-ts/core/build/utils' import type { DIDDocument } from 'did-resolver' +import { LinkedDataProofOptions } from '@credo-ts/core/build/modules/vc/data-integrity/models/LinkedDataProof' export type TenantConfig = Pick & { walletConfig: Pick @@ -412,3 +415,7 @@ export interface credentialPayloadToSign { method: string credential: jsonLdCredentialOptions // TODO: add support for other credential format } +export interface SafeW3cJsonLdVerifyCredentialOptions extends W3cJsonLdVerifyCredentialOptions { + // Ommited due to issues with TSOA + proof: SingleOrArray | DataIntegrityProofOptions> +} \ No newline at end of file diff --git a/src/routes/routes.ts b/src/routes/routes.ts index c67d2d68..75264ac6 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -804,6 +804,55 @@ const models: TsoaRoute.Models = { "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "Pick_LinkedDataProofOptions.Exclude_keyofLinkedDataProofOptions.cryptosuite__": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"type":{"dataType":"string","required":true},"proofPurpose":{"dataType":"string","required":true},"verificationMethod":{"dataType":"string","required":true},"created":{"dataType":"string","required":true},"domain":{"dataType":"string"},"challenge":{"dataType":"string"},"jws":{"dataType":"string"},"proofValue":{"dataType":"string"},"nonce":{"dataType":"string"}},"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "Omit_LinkedDataProofOptions.cryptosuite_": { + "dataType": "refAlias", + "type": {"ref":"Pick_LinkedDataProofOptions.Exclude_keyofLinkedDataProofOptions.cryptosuite__","validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "DataIntegrityProofOptions": { + "dataType": "refObject", + "properties": { + "type": {"dataType":"string","required":true}, + "cryptosuite": {"dataType":"string","required":true}, + "verificationMethod": {"dataType":"string","required":true}, + "proofPurpose": {"dataType":"string","required":true}, + "domain": {"dataType":"string"}, + "challenge": {"dataType":"string"}, + "nonce": {"dataType":"string"}, + "created": {"dataType":"string"}, + "expires": {"dataType":"string"}, + "proofValue": {"dataType":"string"}, + "previousProof": {"dataType":"string"}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "SingleOrArray_Omit_LinkedDataProofOptions.cryptosuite_-or-DataIntegrityProofOptions_": { + "dataType": "refAlias", + "type": {"dataType":"union","subSchemas":[{"dataType":"union","subSchemas":[{"ref":"Omit_LinkedDataProofOptions.cryptosuite_"},{"ref":"DataIntegrityProofOptions"}]},{"dataType":"array","array":{"dataType":"union","subSchemas":[{"ref":"Omit_LinkedDataProofOptions.cryptosuite_"},{"ref":"DataIntegrityProofOptions"}]}}],"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "ProofPurpose": { + "dataType": "refAlias", + "type": {"dataType":"any","validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "safeW3cJsonLdVerifyCredentialOptions": { + "dataType": "refObject", + "properties": { + "credential": {"ref":"W3cJsonLdVerifiableCredential","required":true}, + "verifyCredentialStatus": {"dataType":"boolean"}, + "proofPurpose": {"ref":"ProofPurpose"}, + "proof": {"ref":"SingleOrArray_Omit_LinkedDataProofOptions.cryptosuite_-or-DataIntegrityProofOptions_","required":true}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "DidRegistrationSecretOptions": { "dataType": "refAlias", "type": {"ref":"Record_string.unknown_","validators":{}}, @@ -3777,7 +3826,7 @@ export function RegisterRoutes(app: Router) { async function MultiTenancyController_verifyCredential(request: ExRequest, response: ExResponse, next: any) { const args: Record = { tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, - credentialToVerify: {"in":"body","name":"credentialToVerify","required":true,"dataType":"any"}, + credentialToVerify: {"in":"body","name":"credentialToVerify","required":true,"dataType":"union","subSchemas":[{"ref":"safeW3cJsonLdVerifyCredentialOptions"},{"dataType":"any"}]}, }; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/src/routes/swagger.json b/src/routes/swagger.json index 56b0e254..7e42311d 100644 --- a/src/routes/swagger.json +++ b/src/routes/swagger.json @@ -1926,6 +1926,145 @@ "type": "object", "additionalProperties": false }, + "Pick_LinkedDataProofOptions.Exclude_keyofLinkedDataProofOptions.cryptosuite__": { + "properties": { + "type": { + "type": "string" + }, + "proofPurpose": { + "type": "string" + }, + "verificationMethod": { + "type": "string" + }, + "created": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "jws": { + "type": "string" + }, + "proofValue": { + "type": "string" + }, + "nonce": { + "type": "string" + } + }, + "required": [ + "type", + "proofPurpose", + "verificationMethod", + "created" + ], + "type": "object", + "description": "From T, pick a set of properties whose keys are in the union K" + }, + "Omit_LinkedDataProofOptions.cryptosuite_": { + "$ref": "#/components/schemas/Pick_LinkedDataProofOptions.Exclude_keyofLinkedDataProofOptions.cryptosuite__", + "description": "Construct a type with the properties of T except for those in type K." + }, + "DataIntegrityProofOptions": { + "properties": { + "type": { + "type": "string" + }, + "cryptosuite": { + "type": "string" + }, + "verificationMethod": { + "type": "string" + }, + "proofPurpose": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "created": { + "type": "string" + }, + "expires": { + "type": "string" + }, + "proofValue": { + "type": "string" + }, + "previousProof": { + "type": "string" + } + }, + "required": [ + "type", + "cryptosuite", + "verificationMethod", + "proofPurpose" + ], + "type": "object", + "additionalProperties": false + }, + "SingleOrArray_Omit_LinkedDataProofOptions.cryptosuite_-or-DataIntegrityProofOptions_": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/Omit_LinkedDataProofOptions.cryptosuite_" + }, + { + "$ref": "#/components/schemas/DataIntegrityProofOptions" + } + ] + }, + { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Omit_LinkedDataProofOptions.cryptosuite_" + }, + { + "$ref": "#/components/schemas/DataIntegrityProofOptions" + } + ] + }, + "type": "array" + } + ] + }, + "ProofPurpose": {}, + "safeW3cJsonLdVerifyCredentialOptions": { + "properties": { + "credential": { + "$ref": "#/components/schemas/W3cJsonLdVerifiableCredential" + }, + "verifyCredentialStatus": { + "type": "boolean", + "description": "Whether to verify the credentialStatus, if present." + }, + "proofPurpose": { + "$ref": "#/components/schemas/ProofPurpose" + }, + "proof": { + "$ref": "#/components/schemas/SingleOrArray_Omit_LinkedDataProofOptions.cryptosuite_-or-DataIntegrityProofOptions_" + } + }, + "required": [ + "credential", + "proof" + ], + "type": "object", + "additionalProperties": false + }, "DidRegistrationSecretOptions": { "$ref": "#/components/schemas/Record_string.unknown_" }, @@ -6826,7 +6965,14 @@ "required": true, "content": { "application/json": { - "schema": {} + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/safeW3cJsonLdVerifyCredentialOptions" + }, + {} + ] + } } } } From 31ed240d16beed34afde26137ac4a3fcefe37af6 Mon Sep 17 00:00:00 2001 From: Krishna Waske Date: Thu, 12 Jun 2025 17:03:43 +0530 Subject: [PATCH 7/8] fix: final touches Signed-off-by: Krishna Waske --- .../multi-tenancy/MultiTenancyController.ts | 15 +- src/routes/routes.ts | 119 ++++-- src/routes/swagger.json | 354 ++++++++++++------ 3 files changed, 323 insertions(+), 165 deletions(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index 04c02508..1383c773 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -14,7 +14,9 @@ import type { PeerDidNumAlgo2CreateOptions, ProofExchangeRecordProps, ProofsProtocolVersionType, - Routing} from '@credo-ts/core' + Routing, + W3cJsonLdSignCredentialOptions, + W3cVerifiableCredential} from '@credo-ts/core' import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '@credo-ts/indy-vdr' import type { QuestionAnswerRecord, ValidResponse } from '@credo-ts/question-answer' import type { TenantRecord } from '@credo-ts/tenants' @@ -46,7 +48,8 @@ import { createPeerDidDocumentFromServices, PeerDidNumAlgo, W3cJsonLdVerifiableCredential, - W3cCredential} from '@credo-ts/core' + W3cCredential, + ClaimFormat} from '@credo-ts/core' import { QuestionAnswerRole, QuestionAnswerState } from '@credo-ts/question-answer' import axios from 'axios' import * as fs from 'fs' @@ -1980,15 +1983,15 @@ export class MultiTenancyController extends Controller { public async signCredential( @Path('tenantId') tenantId: string, @Query('storeCredential') storeCredential: boolean, - @Body() credentialToSign: W3cJsonLdVerifiableCredential | any + @Body() credentialToSign: W3cJsonLdSignCredentialOptions | any ) { let storedCredential let formattedCredential try { await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { - const {credential, ...credentialObject} = credentialToSign - const transformedCredential = JsonTransformer.fromJSON(credentialToSign.credential, W3cCredential) - const signedCred = await tenantAgent.w3cCredentials.signCredential({credential: transformedCredential, ...credentialObject}) as W3cJsonLdVerifiableCredential + credentialToSign.format = ClaimFormat.LdpVc + + const signedCred = await tenantAgent.w3cCredentials.signCredential(credentialToSign) as W3cJsonLdVerifiableCredential formattedCredential = signedCred.toJson() if (storeCredential) { storedCredential = await tenantAgent.w3cCredentials.storeCredential({ credential: signedCred }) diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 75264ac6..c2294d13 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -703,43 +703,19 @@ const models: TsoaRoute.Models = { "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"signature":{"dataType":"string","required":true},"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"ref":"KeyType","required":true},"data":{"dataType":"string","required":true}},"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "LinkedDataProof": { - "dataType": "refObject", - "properties": { - "type": {"dataType":"string","required":true}, - "proofPurpose": {"dataType":"string","required":true}, - "verificationMethod": {"dataType":"string","required":true}, - "created": {"dataType":"string","required":true}, - "domain": {"dataType":"string"}, - "challenge": {"dataType":"string"}, - "jws": {"dataType":"string"}, - "proofValue": {"dataType":"string"}, - "nonce": {"dataType":"string"}, - }, - "additionalProperties": false, + "ClaimFormat.LdpVc": { + "dataType": "refEnum", + "enums": ["ldp_vc"], }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "DataIntegrityProof": { - "dataType": "refObject", - "properties": { - "type": {"dataType":"string","required":true}, - "cryptosuite": {"dataType":"string","required":true}, - "proofPurpose": {"dataType":"string","required":true}, - "verificationMethod": {"dataType":"string","required":true}, - "domain": {"dataType":"string"}, - "challenge": {"dataType":"string"}, - "nonce": {"dataType":"string"}, - "created": {"dataType":"string"}, - "expires": {"dataType":"string"}, - "proofValue": {"dataType":"string"}, - "previousProof": {"dataType":"string"}, - }, - "additionalProperties": false, + "ProofPurpose": { + "dataType": "refAlias", + "type": {"dataType":"any","validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "SingleOrArray_LinkedDataProof-or-DataIntegrityProof_": { - "dataType": "refAlias", - "type": {"dataType":"union","subSchemas":[{"dataType":"union","subSchemas":[{"ref":"LinkedDataProof"},{"ref":"DataIntegrityProof"}]},{"dataType":"array","array":{"dataType":"union","subSchemas":[{"ref":"LinkedDataProof"},{"ref":"DataIntegrityProof"}]}}],"validators":{}}, + "ClaimFormat": { + "dataType": "refEnum", + "enums": ["jwt","jwt_vc","jwt_vp","ldp","ldp_vc","ldp_vp","di","di_vc","di_vp","vc+sd-jwt"], }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "W3cIssuer": { @@ -787,7 +763,7 @@ const models: TsoaRoute.Models = { "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "W3cJsonLdVerifiableCredential": { + "W3cCredential": { "dataType": "refObject", "properties": { "context": {"dataType":"array","array":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"JsonObject"}]},"required":true}, @@ -799,7 +775,19 @@ const models: TsoaRoute.Models = { "credentialSubject": {"ref":"SingleOrArray_W3cCredentialSubject_","required":true}, "credentialSchema": {"ref":"SingleOrArray_W3cCredentialSchema_"}, "credentialStatus": {"ref":"W3cCredentialStatus"}, - "proof": {"ref":"SingleOrArray_LinkedDataProof-or-DataIntegrityProof_","required":true}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "W3cJsonLdSignCredentialOptions": { + "dataType": "refObject", + "properties": { + "format": {"ref":"ClaimFormat.LdpVc","required":true}, + "credential": {"ref":"W3cCredential","required":true}, + "verificationMethod": {"dataType":"string","required":true}, + "proofType": {"dataType":"string","required":true}, + "proofPurpose": {"ref":"ProofPurpose"}, + "created": {"dataType":"string"}, }, "additionalProperties": false, }, @@ -837,12 +825,63 @@ const models: TsoaRoute.Models = { "type": {"dataType":"union","subSchemas":[{"dataType":"union","subSchemas":[{"ref":"Omit_LinkedDataProofOptions.cryptosuite_"},{"ref":"DataIntegrityProofOptions"}]},{"dataType":"array","array":{"dataType":"union","subSchemas":[{"ref":"Omit_LinkedDataProofOptions.cryptosuite_"},{"ref":"DataIntegrityProofOptions"}]}}],"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "ProofPurpose": { + "LinkedDataProof": { + "dataType": "refObject", + "properties": { + "type": {"dataType":"string","required":true}, + "proofPurpose": {"dataType":"string","required":true}, + "verificationMethod": {"dataType":"string","required":true}, + "created": {"dataType":"string","required":true}, + "domain": {"dataType":"string"}, + "challenge": {"dataType":"string"}, + "jws": {"dataType":"string"}, + "proofValue": {"dataType":"string"}, + "nonce": {"dataType":"string"}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "DataIntegrityProof": { + "dataType": "refObject", + "properties": { + "type": {"dataType":"string","required":true}, + "cryptosuite": {"dataType":"string","required":true}, + "proofPurpose": {"dataType":"string","required":true}, + "verificationMethod": {"dataType":"string","required":true}, + "domain": {"dataType":"string"}, + "challenge": {"dataType":"string"}, + "nonce": {"dataType":"string"}, + "created": {"dataType":"string"}, + "expires": {"dataType":"string"}, + "proofValue": {"dataType":"string"}, + "previousProof": {"dataType":"string"}, + }, + "additionalProperties": false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "SingleOrArray_LinkedDataProof-or-DataIntegrityProof_": { "dataType": "refAlias", - "type": {"dataType":"any","validators":{}}, + "type": {"dataType":"union","subSchemas":[{"dataType":"union","subSchemas":[{"ref":"LinkedDataProof"},{"ref":"DataIntegrityProof"}]},{"dataType":"array","array":{"dataType":"union","subSchemas":[{"ref":"LinkedDataProof"},{"ref":"DataIntegrityProof"}]}}],"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "W3cJsonLdVerifiableCredential": { + "dataType": "refObject", + "properties": { + "context": {"dataType":"array","array":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"JsonObject"}]},"required":true}, + "id": {"dataType":"string"}, + "type": {"dataType":"array","array":{"dataType":"string"},"required":true}, + "issuer": {"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"W3cIssuer"}],"required":true}, + "issuanceDate": {"dataType":"string","required":true}, + "expirationDate": {"dataType":"string"}, + "credentialSubject": {"ref":"SingleOrArray_W3cCredentialSubject_","required":true}, + "credentialSchema": {"ref":"SingleOrArray_W3cCredentialSchema_"}, + "credentialStatus": {"ref":"W3cCredentialStatus"}, + "proof": {"ref":"SingleOrArray_LinkedDataProof-or-DataIntegrityProof_","required":true}, + }, + "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "safeW3cJsonLdVerifyCredentialOptions": { + "SafeW3cJsonLdVerifyCredentialOptions": { "dataType": "refObject", "properties": { "credential": {"ref":"W3cJsonLdVerifiableCredential","required":true}, @@ -3789,7 +3828,7 @@ export function RegisterRoutes(app: Router) { const args: Record = { tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, storeCredential: {"in":"query","name":"storeCredential","required":true,"dataType":"boolean"}, - credentialToSign: {"in":"body","name":"credentialToSign","required":true,"dataType":"union","subSchemas":[{"ref":"W3cJsonLdVerifiableCredential"},{"dataType":"any"}]}, + credentialToSign: {"in":"body","name":"credentialToSign","required":true,"dataType":"union","subSchemas":[{"ref":"W3cJsonLdSignCredentialOptions"},{"dataType":"any"}]}, }; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -3826,7 +3865,7 @@ export function RegisterRoutes(app: Router) { async function MultiTenancyController_verifyCredential(request: ExRequest, response: ExResponse, next: any) { const args: Record = { tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, - credentialToVerify: {"in":"body","name":"credentialToVerify","required":true,"dataType":"union","subSchemas":[{"ref":"safeW3cJsonLdVerifyCredentialOptions"},{"dataType":"any"}]}, + credentialToVerify: {"in":"body","name":"credentialToVerify","required":true,"dataType":"union","subSchemas":[{"ref":"SafeW3cJsonLdVerifyCredentialOptions"},{"dataType":"any"}]}, }; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/src/routes/swagger.json b/src/routes/swagger.json index 7e42311d..c872bd20 100644 --- a/src/routes/swagger.json +++ b/src/routes/swagger.json @@ -1667,118 +1667,28 @@ ], "type": "object" }, - "LinkedDataProof": { - "description": "Linked Data Proof", - "properties": { - "type": { - "type": "string" - }, - "proofPurpose": { - "type": "string" - }, - "verificationMethod": { - "type": "string" - }, - "created": { - "type": "string" - }, - "domain": { - "type": "string" - }, - "challenge": { - "type": "string" - }, - "jws": { - "type": "string" - }, - "proofValue": { - "type": "string" - }, - "nonce": { - "type": "string" - } - }, - "required": [ - "type", - "proofPurpose", - "verificationMethod", - "created" + "ClaimFormat.LdpVc": { + "enum": [ + "ldp_vc" ], - "type": "object", - "additionalProperties": false + "type": "string" }, - "DataIntegrityProof": { - "description": "Linked Data Proof", - "properties": { - "type": { - "type": "string" - }, - "cryptosuite": { - "type": "string" - }, - "proofPurpose": { - "type": "string" - }, - "verificationMethod": { - "type": "string" - }, - "domain": { - "type": "string" - }, - "challenge": { - "type": "string" - }, - "nonce": { - "type": "string" - }, - "created": { - "type": "string" - }, - "expires": { - "type": "string" - }, - "proofValue": { - "type": "string" - }, - "previousProof": { - "type": "string" - } - }, - "required": [ - "type", - "cryptosuite", - "proofPurpose", - "verificationMethod" + "ProofPurpose": {}, + "ClaimFormat": { + "description": "Defines the claim format based on the formats registered in\n[DIF Claim Format Registry](https://identity.foundation/claim-format-registry/).", + "enum": [ + "jwt", + "jwt_vc", + "jwt_vp", + "ldp", + "ldp_vc", + "ldp_vp", + "di", + "di_vc", + "di_vp", + "vc+sd-jwt" ], - "type": "object", - "additionalProperties": false - }, - "SingleOrArray_LinkedDataProof-or-DataIntegrityProof_": { - "anyOf": [ - { - "anyOf": [ - { - "$ref": "#/components/schemas/LinkedDataProof" - }, - { - "$ref": "#/components/schemas/DataIntegrityProof" - } - ] - }, - { - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/LinkedDataProof" - }, - { - "$ref": "#/components/schemas/DataIntegrityProof" - } - ] - }, - "type": "array" - } - ] + "type": "string" }, "W3cIssuer": { "properties": { @@ -1862,7 +1772,7 @@ "type": "object", "additionalProperties": false }, - "W3cJsonLdVerifiableCredential": { + "W3cCredential": { "properties": { "context": { "items": { @@ -1910,9 +1820,6 @@ }, "credentialStatus": { "$ref": "#/components/schemas/W3cCredentialStatus" - }, - "proof": { - "$ref": "#/components/schemas/SingleOrArray_LinkedDataProof-or-DataIntegrityProof_" } }, "required": [ @@ -1920,8 +1827,41 @@ "type", "issuer", "issuanceDate", - "credentialSubject", - "proof" + "credentialSubject" + ], + "type": "object", + "additionalProperties": false + }, + "W3cJsonLdSignCredentialOptions": { + "properties": { + "format": { + "$ref": "#/components/schemas/ClaimFormat.LdpVc", + "description": "The format of the credential to be signed. Must be either `jwt_vc` or `ldp_vc`." + }, + "credential": { + "$ref": "#/components/schemas/W3cCredential", + "description": "The credential to be signed." + }, + "verificationMethod": { + "type": "string", + "description": "URI of the verificationMethod to be used for signing the credential.\n\nMust be a valid did url pointing to a key." + }, + "proofType": { + "type": "string", + "description": "The proofType to be used for signing the credential.\n\nMust be a valid Linked Data Signature suite." + }, + "proofPurpose": { + "$ref": "#/components/schemas/ProofPurpose" + }, + "created": { + "type": "string" + } + }, + "required": [ + "format", + "credential", + "verificationMethod", + "proofType" ], "type": "object", "additionalProperties": false @@ -2041,8 +1981,184 @@ } ] }, - "ProofPurpose": {}, - "safeW3cJsonLdVerifyCredentialOptions": { + "LinkedDataProof": { + "description": "Linked Data Proof", + "properties": { + "type": { + "type": "string" + }, + "proofPurpose": { + "type": "string" + }, + "verificationMethod": { + "type": "string" + }, + "created": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "jws": { + "type": "string" + }, + "proofValue": { + "type": "string" + }, + "nonce": { + "type": "string" + } + }, + "required": [ + "type", + "proofPurpose", + "verificationMethod", + "created" + ], + "type": "object", + "additionalProperties": false + }, + "DataIntegrityProof": { + "description": "Linked Data Proof", + "properties": { + "type": { + "type": "string" + }, + "cryptosuite": { + "type": "string" + }, + "proofPurpose": { + "type": "string" + }, + "verificationMethod": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "created": { + "type": "string" + }, + "expires": { + "type": "string" + }, + "proofValue": { + "type": "string" + }, + "previousProof": { + "type": "string" + } + }, + "required": [ + "type", + "cryptosuite", + "proofPurpose", + "verificationMethod" + ], + "type": "object", + "additionalProperties": false + }, + "SingleOrArray_LinkedDataProof-or-DataIntegrityProof_": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/LinkedDataProof" + }, + { + "$ref": "#/components/schemas/DataIntegrityProof" + } + ] + }, + { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/LinkedDataProof" + }, + { + "$ref": "#/components/schemas/DataIntegrityProof" + } + ] + }, + "type": "array" + } + ] + }, + "W3cJsonLdVerifiableCredential": { + "properties": { + "context": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/JsonObject" + } + ] + }, + "type": "array" + }, + "id": { + "type": "string" + }, + "type": { + "items": { + "type": "string" + }, + "type": "array" + }, + "issuer": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/W3cIssuer" + } + ] + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialSubject": { + "$ref": "#/components/schemas/SingleOrArray_W3cCredentialSubject_" + }, + "credentialSchema": { + "$ref": "#/components/schemas/SingleOrArray_W3cCredentialSchema_" + }, + "credentialStatus": { + "$ref": "#/components/schemas/W3cCredentialStatus" + }, + "proof": { + "$ref": "#/components/schemas/SingleOrArray_LinkedDataProof-or-DataIntegrityProof_" + } + }, + "required": [ + "context", + "type", + "issuer", + "issuanceDate", + "credentialSubject", + "proof" + ], + "type": "object", + "additionalProperties": false + }, + "SafeW3cJsonLdVerifyCredentialOptions": { "properties": { "credential": { "$ref": "#/components/schemas/W3cJsonLdVerifiableCredential" @@ -6920,7 +7036,7 @@ "schema": { "anyOf": [ { - "$ref": "#/components/schemas/W3cJsonLdVerifiableCredential" + "$ref": "#/components/schemas/W3cJsonLdSignCredentialOptions" }, {} ] @@ -6968,7 +7084,7 @@ "schema": { "anyOf": [ { - "$ref": "#/components/schemas/safeW3cJsonLdVerifyCredentialOptions" + "$ref": "#/components/schemas/SafeW3cJsonLdVerifyCredentialOptions" }, {} ] From ee51cd8bda64ea7f73250de51fb0e652c82a1ba7 Mon Sep 17 00:00:00 2001 From: Krishna Waske Date: Fri, 13 Jun 2025 15:41:13 +0530 Subject: [PATCH 8/8] fix: combine sign credential and sign raw data Signed-off-by: Krishna Waske --- .../multi-tenancy/MultiTenancyController.ts | 94 ++++---- src/controllers/types.ts | 20 +- src/routes/routes.ts | 100 +++------ src/routes/swagger.json | 203 ++++++++---------- 4 files changed, 188 insertions(+), 229 deletions(-) diff --git a/src/controllers/multi-tenancy/MultiTenancyController.ts b/src/controllers/multi-tenancy/MultiTenancyController.ts index 1383c773..c79510c2 100644 --- a/src/controllers/multi-tenancy/MultiTenancyController.ts +++ b/src/controllers/multi-tenancy/MultiTenancyController.ts @@ -1918,37 +1918,6 @@ export class MultiTenancyController extends Controller { } } - - /** - * Sign data using a key - * - * @param tenantId Tenant identifier - * @param request Sign options - * data - Data has to be in base64 format - * publicKeyBase58 - Public key in base58 format - * @returns Signature in base64 format - */ - @Security('apiKey') - @Post('/sign/:tenantId') - public async sign(@Path('tenantId') tenantId: string, @Body() request: SignDataOptions) { - try { - const signature = await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { - // assertAskarWallet(tenantAgent.context.wallet) - - // tenantAgent.w3cCredentials - - const signature = await tenantAgent.context.wallet.sign({ - data: TypedArrayEncoder.fromBase64(request.data), - key: Key.fromPublicKeyBase58(request.publicKeyBase58, request.keyType), - }) - return TypedArrayEncoder.toBase64(signature) - }) - return signature - } catch (error) { - throw ErrorHandlingService.handle(error) - } - } - /** * Verify data using a key * @@ -1983,21 +1952,66 @@ export class MultiTenancyController extends Controller { public async signCredential( @Path('tenantId') tenantId: string, @Query('storeCredential') storeCredential: boolean, - @Body() credentialToSign: W3cJsonLdSignCredentialOptions | any + @Query('dataTypeToSign') dataTypeToSign: 'rawData' | 'jsonLd', + @Body() data: CustomW3cJsonLdSignCredentialOptions | SignDataOptions | any ) { - let storedCredential - let formattedCredential try { - await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { - credentialToSign.format = ClaimFormat.LdpVc + return await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => { + // JSON-LD VC Signing + if (dataTypeToSign === 'jsonLd') { + const credentialData = data as unknown as W3cJsonLdSignCredentialOptions + credentialData.format = ClaimFormat.LdpVc + + const signedCredential = await tenantAgent.w3cCredentials.signCredential(credentialData) as W3cJsonLdVerifiableCredential - const signedCred = await tenantAgent.w3cCredentials.signCredential(credentialToSign) as W3cJsonLdVerifiableCredential - formattedCredential = signedCred.toJson() if (storeCredential) { - storedCredential = await tenantAgent.w3cCredentials.storeCredential({ credential: signedCred }) + return await tenantAgent.w3cCredentials.storeCredential({ credential: signedCredential }) } + + return signedCredential.toJson() + } + + // Raw Data Signing + const rawData = data as SignDataOptions + + if (!rawData.data) throw new BadRequestError('Missing "data" for raw data signing.') + + const hasDidOrMethod = rawData.did || rawData.method + const hasPublicKey = rawData.publicKeyBase58 && rawData.keyType + + if (!hasDidOrMethod && !hasPublicKey) { + throw new BadRequestError('Either (did or method) OR (publicKeyBase58 and keyType) must be provided.') + } + + let keyToUse: Key + + if (hasDidOrMethod) { + const dids = await tenantAgent.dids.getCreatedDids({ + method: rawData.method || undefined, + did: rawData.did || undefined, + }) + + const verificationMethod = dids[0]?.didDocument?.verificationMethod?.[0]?.publicKeyBase58 + if (!verificationMethod) { + throw new BadRequestError('No publicKeyBase58 found for the given DID or method.') + } + + keyToUse = Key.fromPublicKeyBase58(verificationMethod, rawData.keyType) + } else { + keyToUse = Key.fromPublicKeyBase58(rawData.publicKeyBase58, rawData.keyType) + } + + if (!keyToUse) { + throw new Error('Unable to construct signing key.') + } + + const signature = await tenantAgent.context.wallet.sign({ + data: TypedArrayEncoder.fromBase64(rawData.data), + key: keyToUse, + }) + + return TypedArrayEncoder.toBase64(signature) }) - return storedCredential ?? formattedCredential } catch (error) { throw ErrorHandlingService.handle(error) } diff --git a/src/controllers/types.ts b/src/controllers/types.ts index f3acb079..227d94a0 100644 --- a/src/controllers/types.ts +++ b/src/controllers/types.ts @@ -28,6 +28,9 @@ import type { JsonObject, W3cJsonLdVerifyCredentialOptions, DataIntegrityProofOptions, + W3cJsonLdSignCredentialOptions, + W3cCredential, + W3cCredentialSubject, } from '@credo-ts/core' import type { SingleOrArray } from '@credo-ts/core/build/utils' import type { DIDDocument } from 'did-resolver' @@ -394,6 +397,8 @@ export type SignDataOptions = { data: string keyType: KeyType publicKeyBase58: string + did?: string + method?: string } export type VerifyDataOptions = { @@ -418,4 +423,17 @@ export interface credentialPayloadToSign { export interface SafeW3cJsonLdVerifyCredentialOptions extends W3cJsonLdVerifyCredentialOptions { // Ommited due to issues with TSOA proof: SingleOrArray | DataIntegrityProofOptions> -} \ No newline at end of file +} + +export type ExtensibleW3cCredentialSubject = W3cCredentialSubject & { + [key: string]: unknown +} + +export type ExtensibleW3cCredential = W3cCredential & { + [key: string]: unknown + credentialSubject: SingleOrArray +} + +export type CustomW3cJsonLdSignCredentialOptions = Omit & { + [key: string]: unknown +} diff --git a/src/routes/routes.ts b/src/routes/routes.ts index c2294d13..68fbc8c9 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -693,29 +693,14 @@ const models: TsoaRoute.Models = { "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"content":{"dataType":"string","required":true}},"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "SignDataOptions": { - "dataType": "refAlias", - "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"ref":"KeyType","required":true},"data":{"dataType":"string","required":true}},"validators":{}}, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "VerifyDataOptions": { "dataType": "refAlias", "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"signature":{"dataType":"string","required":true},"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"ref":"KeyType","required":true},"data":{"dataType":"string","required":true}},"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "ClaimFormat.LdpVc": { - "dataType": "refEnum", - "enums": ["ldp_vc"], - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "ProofPurpose": { + "W3cCredentialRecord": { "dataType": "refAlias", - "type": {"dataType":"any","validators":{}}, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "ClaimFormat": { - "dataType": "refEnum", - "enums": ["jwt","jwt_vc","jwt_vp","ldp","ldp_vc","ldp_vp","di","di_vc","di_vp","vc+sd-jwt"], + "type": {"ref":"Record_string.unknown_","validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "W3cIssuer": { @@ -779,22 +764,29 @@ const models: TsoaRoute.Models = { "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "W3cJsonLdSignCredentialOptions": { - "dataType": "refObject", - "properties": { - "format": {"ref":"ClaimFormat.LdpVc","required":true}, - "credential": {"ref":"W3cCredential","required":true}, - "verificationMethod": {"dataType":"string","required":true}, - "proofType": {"dataType":"string","required":true}, - "proofPurpose": {"ref":"ProofPurpose"}, - "created": {"dataType":"string"}, - }, - "additionalProperties": false, + "Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format__": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"credential":{"ref":"W3cCredential","required":true},"proofType":{"dataType":"string","required":true},"proofPurpose":{"dataType":"any"},"created":{"dataType":"string"},"verificationMethod":{"dataType":"string","required":true}},"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "Omit_W3cJsonLdSignCredentialOptions.format_": { + "dataType": "refAlias", + "type": {"ref":"Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format__","validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "CustomW3cJsonLdSignCredentialOptions": { + "dataType": "refAlias", + "type": {"dataType":"intersection","subSchemas":[{"ref":"Omit_W3cJsonLdSignCredentialOptions.format_"},{"dataType":"nestedObjectLiteral","nestedProperties":{},"additionalProperties":{"dataType":"any"}}],"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "SignDataOptions": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"method":{"dataType":"string"},"did":{"dataType":"string"},"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"ref":"KeyType","required":true},"data":{"dataType":"string","required":true}},"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "Pick_LinkedDataProofOptions.Exclude_keyofLinkedDataProofOptions.cryptosuite__": { "dataType": "refAlias", - "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"type":{"dataType":"string","required":true},"proofPurpose":{"dataType":"string","required":true},"verificationMethod":{"dataType":"string","required":true},"created":{"dataType":"string","required":true},"domain":{"dataType":"string"},"challenge":{"dataType":"string"},"jws":{"dataType":"string"},"proofValue":{"dataType":"string"},"nonce":{"dataType":"string"}},"validators":{}}, + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"proofPurpose":{"dataType":"string","required":true},"created":{"dataType":"string","required":true},"verificationMethod":{"dataType":"string","required":true},"type":{"dataType":"string","required":true},"domain":{"dataType":"string"},"challenge":{"dataType":"string"},"jws":{"dataType":"string"},"proofValue":{"dataType":"string"},"nonce":{"dataType":"string"}},"validators":{}}, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "Omit_LinkedDataProofOptions.cryptosuite_": { @@ -881,6 +873,11 @@ const models: TsoaRoute.Models = { "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "ProofPurpose": { + "dataType": "refAlias", + "type": {"dataType":"any","validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "SafeW3cJsonLdVerifyCredentialOptions": { "dataType": "refObject", "properties": { @@ -1170,11 +1167,6 @@ const models: TsoaRoute.Models = { "enums": ["issuer","holder"], }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "W3cCredentialRecord": { - "dataType": "refAlias", - "type": {"ref":"Record_string.unknown_","validators":{}}, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "CredentialExchangeRecord": { "dataType": "refAlias", "type": {"ref":"Record_string.unknown_","validators":{}}, @@ -3745,43 +3737,6 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - app.post('/multi-tenancy/sign/:tenantId', - authenticateMiddleware([{"apiKey":[]}]), - ...(fetchMiddlewares(MultiTenancyController)), - ...(fetchMiddlewares(MultiTenancyController.prototype.sign)), - - async function MultiTenancyController_sign(request: ExRequest, response: ExResponse, next: any) { - const args: Record = { - tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, - request: {"in":"body","name":"request","required":true,"ref":"SignDataOptions"}, - }; - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - let validatedArgs: any[] = []; - try { - validatedArgs = templateService.getValidatedArgs({ args, request, response }); - - const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; - - const controller: any = await container.get(MultiTenancyController); - if (typeof controller['setStatus'] === 'function') { - controller.setStatus(undefined); - } - - await templateService.apiHandler({ - methodName: 'sign', - controller, - response, - next, - validatedArgs, - successStatus: undefined, - }); - } catch (err) { - return next(err); - } - }); - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.post('/multi-tenancy/verify/:tenantId', authenticateMiddleware([{"apiKey":[]}]), ...(fetchMiddlewares(MultiTenancyController)), @@ -3828,7 +3783,8 @@ export function RegisterRoutes(app: Router) { const args: Record = { tenantId: {"in":"path","name":"tenantId","required":true,"dataType":"string"}, storeCredential: {"in":"query","name":"storeCredential","required":true,"dataType":"boolean"}, - credentialToSign: {"in":"body","name":"credentialToSign","required":true,"dataType":"union","subSchemas":[{"ref":"W3cJsonLdSignCredentialOptions"},{"dataType":"any"}]}, + dataTypeToSign: {"in":"query","name":"dataTypeToSign","required":true,"dataType":"union","subSchemas":[{"dataType":"enum","enums":["rawData"]},{"dataType":"enum","enums":["jsonLd"]}]}, + data: {"in":"body","name":"data","required":true,"dataType":"union","subSchemas":[{"ref":"CustomW3cJsonLdSignCredentialOptions"},{"ref":"SignDataOptions"},{"dataType":"any"}]}, }; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/src/routes/swagger.json b/src/routes/swagger.json index c872bd20..d22382dd 100644 --- a/src/routes/swagger.json +++ b/src/routes/swagger.json @@ -1625,25 +1625,6 @@ "type": "object", "description": "Construct a type with a set of properties K of type T" }, - "SignDataOptions": { - "properties": { - "publicKeyBase58": { - "type": "string" - }, - "keyType": { - "$ref": "#/components/schemas/KeyType" - }, - "data": { - "type": "string" - } - }, - "required": [ - "publicKeyBase58", - "keyType", - "data" - ], - "type": "object" - }, "VerifyDataOptions": { "properties": { "signature": { @@ -1667,28 +1648,8 @@ ], "type": "object" }, - "ClaimFormat.LdpVc": { - "enum": [ - "ldp_vc" - ], - "type": "string" - }, - "ProofPurpose": {}, - "ClaimFormat": { - "description": "Defines the claim format based on the formats registered in\n[DIF Claim Format Registry](https://identity.foundation/claim-format-registry/).", - "enum": [ - "jwt", - "jwt_vc", - "jwt_vp", - "ldp", - "ldp_vc", - "ldp_vp", - "di", - "di_vc", - "di_vp", - "vc+sd-jwt" - ], - "type": "string" + "W3cCredentialRecord": { + "$ref": "#/components/schemas/Record_string.unknown_" }, "W3cIssuer": { "properties": { @@ -1832,52 +1793,86 @@ "type": "object", "additionalProperties": false }, - "W3cJsonLdSignCredentialOptions": { + "Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format__": { "properties": { - "format": { - "$ref": "#/components/schemas/ClaimFormat.LdpVc", - "description": "The format of the credential to be signed. Must be either `jwt_vc` or `ldp_vc`." - }, "credential": { "$ref": "#/components/schemas/W3cCredential", "description": "The credential to be signed." }, - "verificationMethod": { - "type": "string", - "description": "URI of the verificationMethod to be used for signing the credential.\n\nMust be a valid did url pointing to a key." - }, "proofType": { "type": "string", "description": "The proofType to be used for signing the credential.\n\nMust be a valid Linked Data Signature suite." }, - "proofPurpose": { - "$ref": "#/components/schemas/ProofPurpose" - }, + "proofPurpose": {}, "created": { "type": "string" + }, + "verificationMethod": { + "type": "string", + "description": "URI of the verificationMethod to be used for signing the credential.\n\nMust be a valid did url pointing to a key." } }, "required": [ - "format", "credential", - "verificationMethod", - "proofType" + "proofType", + "verificationMethod" ], "type": "object", - "additionalProperties": false + "description": "From T, pick a set of properties whose keys are in the union K" }, - "Pick_LinkedDataProofOptions.Exclude_keyofLinkedDataProofOptions.cryptosuite__": { + "Omit_W3cJsonLdSignCredentialOptions.format_": { + "$ref": "#/components/schemas/Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format__", + "description": "Construct a type with the properties of T except for those in type K." + }, + "CustomW3cJsonLdSignCredentialOptions": { + "allOf": [ + { + "$ref": "#/components/schemas/Omit_W3cJsonLdSignCredentialOptions.format_" + }, + { + "properties": {}, + "additionalProperties": {}, + "type": "object" + } + ] + }, + "SignDataOptions": { "properties": { - "type": { + "method": { "type": "string" }, + "did": { + "type": "string" + }, + "publicKeyBase58": { + "type": "string" + }, + "keyType": { + "$ref": "#/components/schemas/KeyType" + }, + "data": { + "type": "string" + } + }, + "required": [ + "publicKeyBase58", + "keyType", + "data" + ], + "type": "object" + }, + "Pick_LinkedDataProofOptions.Exclude_keyofLinkedDataProofOptions.cryptosuite__": { + "properties": { "proofPurpose": { "type": "string" }, + "created": { + "type": "string" + }, "verificationMethod": { "type": "string" }, - "created": { + "type": { "type": "string" }, "domain": { @@ -1897,10 +1892,10 @@ } }, "required": [ - "type", "proofPurpose", + "created", "verificationMethod", - "created" + "type" ], "type": "object", "description": "From T, pick a set of properties whose keys are in the union K" @@ -2158,6 +2153,7 @@ "type": "object", "additionalProperties": false }, + "ProofPurpose": {}, "SafeW3cJsonLdVerifyCredentialOptions": { "properties": { "credential": { @@ -2826,9 +2822,6 @@ ], "type": "string" }, - "W3cCredentialRecord": { - "$ref": "#/components/schemas/Record_string.unknown_" - }, "CredentialExchangeRecord": { "$ref": "#/components/schemas/Record_string.unknown_" }, @@ -6892,55 +6885,6 @@ } } }, - "/multi-tenancy/sign/{tenantId}": { - "post": { - "operationId": "Sign", - "responses": { - "200": { - "description": "Signature in base64 format", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - }, - "description": "Sign data using a key", - "tags": [ - "MultiTenancy" - ], - "security": [ - { - "apiKey": [] - } - ], - "parameters": [ - { - "description": "Tenant identifier", - "in": "path", - "name": "tenantId", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "Sign options\ndata - Data has to be in base64 format\npublicKeyBase58 - Public key in base58 format", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SignDataOptions", - "description": "Sign options\ndata - Data has to be in base64 format\npublicKeyBase58 - Public key in base58 format" - } - } - } - } - } - }, "/multi-tenancy/verify/{tenantId}": { "post": { "operationId": "Verify", @@ -6998,7 +6942,19 @@ "description": "Ok", "content": { "application/json": { - "schema": {} + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/Record_string.any_" + }, + { + "$ref": "#/components/schemas/W3cCredentialRecord" + } + ] + } } } } @@ -7027,6 +6983,18 @@ "schema": { "type": "boolean" } + }, + { + "in": "query", + "name": "dataTypeToSign", + "required": true, + "schema": { + "type": "string", + "enum": [ + "rawData", + "jsonLd" + ] + } } ], "requestBody": { @@ -7036,7 +7004,10 @@ "schema": { "anyOf": [ { - "$ref": "#/components/schemas/W3cJsonLdSignCredentialOptions" + "$ref": "#/components/schemas/CustomW3cJsonLdSignCredentialOptions" + }, + { + "$ref": "#/components/schemas/SignDataOptions" }, {} ]