From 7847dbd23b0c26396220c78274b670a2da4db705 Mon Sep 17 00:00:00 2001 From: Sagar Khole Date: Wed, 13 May 2026 17:49:15 +0530 Subject: [PATCH 1/5] feat: implement W3C JSON-LD sign and verify logic Signed-off-by: Sagar Khole --- src/cliAgent.ts | 3 +- src/controllers/agent/AgentController.ts | 277 ++++++++++++++--------- src/routes/routes.ts | 126 ++++++++++- src/routes/swagger.json | 220 +++++++++++++++++- 4 files changed, 512 insertions(+), 114 deletions(-) diff --git a/src/cliAgent.ts b/src/cliAgent.ts index c2575ddd..9998e221 100644 --- a/src/cliAgent.ts +++ b/src/cliAgent.ts @@ -7,7 +7,7 @@ import type { AskarModuleConfigStoreOptions } from '@credo-ts/askar' import type { InitConfig } from '@credo-ts/core' import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr' -import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' +// import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' import { AnonCredsDidCommCredentialFormatService, AnonCredsModule, @@ -84,6 +84,7 @@ import { getX509CertsByClientToken, getX509CertsByUrl, } from './utils/oid4vc-agent' +import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' export type Transports = 'ws' | 'http' export type InboundTransport = { diff --git a/src/controllers/agent/AgentController.ts b/src/controllers/agent/AgentController.ts index 891ad5a2..72798cdb 100644 --- a/src/controllers/agent/AgentController.ts +++ b/src/controllers/agent/AgentController.ts @@ -1,13 +1,30 @@ -import type { AgentInfo, AgentToken, SafeW3cJsonLdVerifyCredentialOptions } from '../types' +import type { + AgentInfo, + AgentToken, + SafeW3cJsonLdVerifyCredentialOptions, + CustomW3cJsonLdSignCredentialOptions, + SignDataOptions, + VerifyDataOptions +} from '../types' -import { JsonTransformer, W3cJsonLdVerifiableCredential } from '@credo-ts/core' +import { + JsonTransformer, + W3cJsonLdVerifiableCredential, + TypedArrayEncoder, + ClaimFormat, + W3cCredentialRecord, + DidDocument, + verkeyToPublicJwk +} from '@credo-ts/core' +import { getKmsKeyIdForVerifiacationMethod } from '@credo-ts/core' import { Request as Req } from 'express' import jwt from 'jsonwebtoken' -import { Controller, Get, Route, Tags, Security, Request, Post, Body } from 'tsoa' +import { Controller, Get, Route, Tags, Security, Request, Post, Body, Query } from 'tsoa' import { injectable } from 'tsyringe' import { AgentRole, SCOPES } from '../../enums' import ErrorHandlingService from '../../errorHandlingService' +import { BadRequestError } from '../../errors/errors' @Tags('Agent') @Route('/agent') @@ -54,108 +71,158 @@ export class AgentController extends Controller { } } - // /** - // * Delete wallet - // */ - // @Security('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) - // @Delete('/wallet') - // public async deleteWallet(@Request() request: Req) { - // try { - // const deleteWallet = await request.agent.wallet.delete() - // return deleteWallet - // } 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('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) - // @Post('/verify') - // public async verify(@Request() request: Req, @Body() body: VerifyDataOptions) { - // try { - // assertAskarWallet(request.agent.context.wallet) - // const isValidSignature = await request.agent.context.wallet.verify({ - // data: TypedArrayEncoder.fromBase64(body.data), - // key: Key.fromPublicKeyBase58(body.publicKeyBase58, body.keyType), - // signature: TypedArrayEncoder.fromBase64(body.signature), - // }) - // return isValidSignature - // } catch (error) { - // throw ErrorHandlingService.handle(error) - // } - // } - - // //Triage: Do we want the BW to be able to sign and verify as well? - // @Security('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) - // @Post('/credential/sign') - // public async signCredential( - // @Request() request: Req, - // @Query('storeCredential') storeCredential: boolean, - // @Query('dataTypeToSign') dataTypeToSign: 'rawData' | 'jsonLd', - // @Body() data: CustomW3cJsonLdSignCredentialOptions | SignDataOptions | unknown, - // ) { - // try { - // // JSON-LD VC Signing - // if (dataTypeToSign === 'jsonLd') { - // const credentialData = data as unknown as W3cJsonLdSignCredentialOptions - // credentialData.format = ClaimFormat.LdpVc - // const signedCredential = (await request.agent.w3cCredentials.signCredential( - // credentialData, - // )) as W3cJsonLdVerifiableCredential - // if (storeCredential) { - // return await request.agent.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 request.agent.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 request.agent.context.wallet.sign({ - // data: TypedArrayEncoder.fromBase64(rawData.data), - // key: keyToUse, - // }) - - // return TypedArrayEncoder.toBase64(signature) - // } catch (error) { - // throw ErrorHandlingService.handle(error) - // } - // } + /** + * Verify data using a key + * + * @param body 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('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) + @Post('/verify') + public async verify(@Request() request: Req, @Body() body: VerifyDataOptions) { + try { + const algorithmMap: Record = { + 'ed25519': 'EdDSA', + 'p256': 'ES256', + 'secp256k1': 'ES256K' + } + + // Convert verkey to JWK + const publicJwkWrapper = verkeyToPublicJwk(body.publicKeyBase58) as any + const publicJwk = publicJwkWrapper.jwk?.jwk || publicJwkWrapper.jwk || publicJwkWrapper + + const result = await request.agent.kms.verify({ + data: TypedArrayEncoder.fromBase64(body.data), + signature: TypedArrayEncoder.fromBase64(body.signature), + key: { + publicJwk: publicJwk as any + }, + algorithm: (algorithmMap[body.keyType.toLowerCase()] || body.keyType) as any + }) + + return result.verified + } catch (error) { + throw ErrorHandlingService.handle(error) + } + } + + /** + * Sign credential or raw data + */ + @Security('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) + @Post('/credential/sign') + public async signCredential( + @Request() request: Req, + @Query('storeCredential') storeCredential: boolean, + @Query('dataTypeToSign') dataTypeToSign: 'rawData' | 'jsonLd' | string, + @Body() data: any, + ) { + try { + const typeToSign = (dataTypeToSign || 'rawData').toLowerCase() + request.agent.config.logger.info(`[SignCredential] dataTypeToSign: ${dataTypeToSign}, typeToSign: ${typeToSign}, storeCredential: ${storeCredential}`); + + // JSON-LD VC Signing + if (typeToSign === 'jsonld') { + const credentialData = data as any + + // Ensure signerOptions is populated if top-level fields are provided + if (!credentialData.signerOptions && (credentialData.verificationMethod || credentialData.proofType)) { + credentialData.signerOptions = { + verificationMethod: credentialData.verificationMethod, + type: credentialData.proofType, + method: 'did' // default to did if not specified + } + } + + credentialData.format = ClaimFormat.LdpVc + const signedCredential = (await request.agent.w3cCredentials.signCredential( + credentialData, + )) as W3cJsonLdVerifiableCredential + + if (storeCredential) { + const record = W3cCredentialRecord.fromCredential(signedCredential) + return await request.agent.w3cCredentials.store({ record }) + } + 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 kmsKeyId: string | undefined = undefined + if (hasDidOrMethod) { + let didDocument: DidDocument | undefined | null = undefined + const dids = await request.agent.dids.getCreatedDids({ + method: rawData.method || undefined, + did: rawData.did || undefined, + }) + + const didRecord = dids[0] + if (didRecord) { + didDocument = didRecord.didDocument + if (didRecord.keys && didRecord.keys.length > 0) { + kmsKeyId = didRecord.keys[0].kmsKeyId + } + } + + if (!didDocument && rawData.did) { + const resolution = await request.agent.dids.resolve(rawData.did) + didDocument = resolution.didDocument + } + + if (!didDocument) { + throw new BadRequestError('No DID document found.') + } + + if (!kmsKeyId) { + const verificationMethod = didDocument.verificationMethod?.[0] + if (!verificationMethod) { + throw new BadRequestError('No verification method found on DID document.') + } + + // Try multiple ways to get the kmsKeyId + const derivedKeyId = getKmsKeyIdForVerifiacationMethod(verificationMethod) + const publicKeyBase58 = (verificationMethod as any).publicKeyBase58 + const vmId = verificationMethod.id || '' + const idPart = vmId.includes('#') ? vmId.split('#')[1] : undefined + + kmsKeyId = (derivedKeyId || publicKeyBase58 || idPart || vmId) as string + + request.agent.config.logger.info(`[SignCredential] Resolved kmsKeyId via fallback: ${kmsKeyId}`); + } + } else { + kmsKeyId = rawData.publicKeyBase58 + } + + const algorithmMap: Record = { + 'ed25519': 'EdDSA', + 'p256': 'ES256', + 'secp256k1': 'ES256K' + } + + const signature = await request.agent.kms.sign({ + data: TypedArrayEncoder.fromBase64(rawData.data), + keyId: kmsKeyId as string, + algorithm: (rawData.keyType ? (algorithmMap[rawData.keyType.toLowerCase()] || rawData.keyType) : 'EdDSA') as any + }) + + return TypedArrayEncoder.toBase64(signature.signature) + } catch (error) { + const err = error as any + request.agent.config.logger.error(`[SignCredential] Error: ${err.message}`, { stack: err.stack }); + throw ErrorHandlingService.handle(error) + } + } @Security('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) @Post('/credential/verify') diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 3285a6ec..c97e88a8 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -1428,6 +1428,51 @@ 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 + "VerifyDataOptions": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"signature":{"dataType":"string","required":true},"publicKeyBase58":{"dataType":"string","required":true},"keyType":{"dataType":"any","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 + "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 + "W3cJsonIssuer": { + "dataType": "refObject", + "properties": { + "id": {"dataType":"string","required":true}, + }, + "additionalProperties": {"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 + "W3cJsonCredentialSubject": { + "dataType": "refObject", + "properties": { + "id": {"dataType":"string"}, + }, + "additionalProperties": {"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 + "SingleOrArray_W3cJsonCredentialSubject_": { + "dataType": "refAlias", + "type": {"dataType":"union","subSchemas":[{"ref":"W3cJsonCredentialSubject"},{"dataType":"array","array":{"dataType":"refObject","ref":"W3cJsonCredentialSubject"}}],"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 + "W3cJsonCredential": { + "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":"W3cJsonIssuer"}],"required":true}, + "issuanceDate": {"dataType":"string","required":true}, + "expirationDate": {"dataType":"string"}, + "credentialSubject": {"ref":"SingleOrArray_W3cJsonCredentialSubject_","required":true}, + }, + "additionalProperties": {"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 "Partial_W3cCredentialValidations_": { "dataType": "refAlias", "type": {"dataType":"nestedObjectLiteral","nestedProperties":{},"validators":{}}, @@ -1747,11 +1792,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 "DidCommCredentialExchangeRecord": { "dataType": "refAlias", "type": {"ref":"Record_string.unknown_","validators":{}}, @@ -4703,6 +4743,82 @@ 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 + const argsAgentController_verify: Record = { + request: {"in":"request","name":"request","required":true,"dataType":"object"}, + body: {"in":"body","name":"body","required":true,"ref":"VerifyDataOptions"}, + }; + app.post('/agent/verify', + authenticateMiddleware([{"jwt":["tenant","dedicated"]}]), + ...(fetchMiddlewares(AgentController)), + ...(fetchMiddlewares(AgentController.prototype.verify)), + + async function AgentController_verify(request: ExRequest, response: ExResponse, next: 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: argsAgentController_verify, request, response }); + + const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; + + const controller: any = await container.get(AgentController); + 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 + const argsAgentController_signCredential: Record = { + request: {"in":"request","name":"request","required":true,"dataType":"object"}, + storeCredential: {"in":"query","name":"storeCredential","required":true,"dataType":"boolean"}, + dataTypeToSign: {"in":"query","name":"dataTypeToSign","required":true,"dataType":"union","subSchemas":[{"dataType":"enum","enums":["rawData"]},{"dataType":"enum","enums":["jsonLd"]},{"dataType":"string"}]}, + data: {"in":"body","name":"data","required":true,"dataType":"any"}, + }; + app.post('/agent/credential/sign', + authenticateMiddleware([{"jwt":["tenant","dedicated"]}]), + ...(fetchMiddlewares(AgentController)), + ...(fetchMiddlewares(AgentController.prototype.signCredential)), + + async function AgentController_signCredential(request: ExRequest, response: ExResponse, next: 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: argsAgentController_signCredential, request, response }); + + const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer; + + const controller: any = await container.get(AgentController); + 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 const argsAgentController_verifyCredential: Record = { request: {"in":"request","name":"request","required":true,"dataType":"object"}, credentialToVerify: {"in":"body","name":"credentialToVerify","required":true,"dataType":"union","subSchemas":[{"ref":"SafeW3cJsonLdVerifyCredentialOptions"},{"dataType":"any"}]}, diff --git a/src/routes/swagger.json b/src/routes/swagger.json index 4fceed67..97c803ad 100644 --- a/src/routes/swagger.json +++ b/src/routes/swagger.json @@ -3242,6 +3242,118 @@ "type": "object", "additionalProperties": false }, + "VerifyDataOptions": { + "properties": { + "signature": { + "type": "string" + }, + "publicKeyBase58": { + "type": "string" + }, + "keyType": {}, + "data": { + "type": "string" + } + }, + "required": [ + "signature", + "publicKeyBase58", + "keyType", + "data" + ], + "type": "object" + }, + "W3cCredentialRecord": { + "$ref": "#/components/schemas/Record_string.unknown_" + }, + "W3cJsonIssuer": { + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object", + "additionalProperties": {} + }, + "W3cJsonCredentialSubject": { + "properties": { + "id": { + "type": "string" + } + }, + "type": "object", + "additionalProperties": {} + }, + "SingleOrArray_W3cJsonCredentialSubject_": { + "anyOf": [ + { + "$ref": "#/components/schemas/W3cJsonCredentialSubject" + }, + { + "items": { + "$ref": "#/components/schemas/W3cJsonCredentialSubject" + }, + "type": "array" + } + ] + }, + "W3cJsonCredential": { + "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/W3cJsonIssuer" + } + ] + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialSubject": { + "$ref": "#/components/schemas/SingleOrArray_W3cJsonCredentialSubject_" + } + }, + "required": [ + "@context", + "type", + "issuer", + "issuanceDate", + "credentialSubject" + ], + "type": "object", + "additionalProperties": {} + }, "Partial_W3cCredentialValidations_": { "properties": {}, "type": "object", @@ -4041,9 +4153,6 @@ ], "type": "string" }, - "W3cCredentialRecord": { - "$ref": "#/components/schemas/Record_string.unknown_" - }, "DidCommCredentialExchangeRecord": { "$ref": "#/components/schemas/Record_string.unknown_" }, @@ -9057,6 +9166,111 @@ "parameters": [] } }, + "/agent/verify": { + "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": [ + "Agent" + ], + "security": [ + { + "jwt": [ + "tenant", + "dedicated" + ] + } + ], + "parameters": [], + "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" + } + } + } + } + } + }, + "/agent/credential/sign": { + "post": { + "operationId": "SignCredential", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/W3cCredentialRecord" + }, + { + "$ref": "#/components/schemas/W3cJsonCredential" + } + ] + } + } + } + } + }, + "description": "Sign credential or raw data", + "tags": [ + "Agent" + ], + "security": [ + { + "jwt": [ + "tenant", + "dedicated" + ] + } + ], + "parameters": [ + { + "in": "query", + "name": "storeCredential", + "required": true, + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "dataTypeToSign", + "required": true, + "schema": {} + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, "/agent/credential/verify": { "post": { "operationId": "VerifyCredential", From 11a77f930c4ce354c124979a7f9872109e8bda1b Mon Sep 17 00:00:00 2001 From: Sagar Khole Date: Wed, 13 May 2026 18:09:47 +0530 Subject: [PATCH 2/5] refactor: move Polygon module import to top-level Signed-off-by: Sagar Khole --- src/cliAgent.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cliAgent.ts b/src/cliAgent.ts index 9998e221..c2575ddd 100644 --- a/src/cliAgent.ts +++ b/src/cliAgent.ts @@ -7,7 +7,7 @@ import type { AskarModuleConfigStoreOptions } from '@credo-ts/askar' import type { InitConfig } from '@credo-ts/core' import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr' -// import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' +import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' import { AnonCredsDidCommCredentialFormatService, AnonCredsModule, @@ -84,7 +84,6 @@ import { getX509CertsByClientToken, getX509CertsByUrl, } from './utils/oid4vc-agent' -import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' export type Transports = 'ws' | 'http' export type InboundTransport = { From cca1e721996816e86828138489f0ab92d567a608 Mon Sep 17 00:00:00 2001 From: Sagar Khole Date: Wed, 13 May 2026 18:10:46 +0530 Subject: [PATCH 3/5] refactor: consolidate imports and remove unused symbols in AgentController Signed-off-by: Sagar Khole --- src/controllers/agent/AgentController.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/controllers/agent/AgentController.ts b/src/controllers/agent/AgentController.ts index 72798cdb..df8c4055 100644 --- a/src/controllers/agent/AgentController.ts +++ b/src/controllers/agent/AgentController.ts @@ -2,7 +2,6 @@ import type { AgentInfo, AgentToken, SafeW3cJsonLdVerifyCredentialOptions, - CustomW3cJsonLdSignCredentialOptions, SignDataOptions, VerifyDataOptions } from '../types' @@ -14,9 +13,9 @@ import { ClaimFormat, W3cCredentialRecord, DidDocument, - verkeyToPublicJwk + verkeyToPublicJwk, + getKmsKeyIdForVerifiacationMethod } from '@credo-ts/core' -import { getKmsKeyIdForVerifiacationMethod } from '@credo-ts/core' import { Request as Req } from 'express' import jwt from 'jsonwebtoken' import { Controller, Get, Route, Tags, Security, Request, Post, Body, Query } from 'tsoa' From 66ac90c6de75b289a6a83fbbba7c0cd559f7b38a Mon Sep 17 00:00:00 2001 From: Sagar Khole Date: Wed, 13 May 2026 19:08:50 +0530 Subject: [PATCH 4/5] refactor: improve sign/verify logic and fix TSOA validation Signed-off-by: Sagar Khole --- src/cliAgent.ts | 3 +- src/controllers/agent/AgentController.ts | 209 ++++++++++----------- src/controllers/types.ts | 15 +- src/routes/routes.ts | 48 ++++- src/routes/swagger.json | 220 ++++++++++++++++++++--- src/utils/constant.ts | 6 + 6 files changed, 364 insertions(+), 137 deletions(-) diff --git a/src/cliAgent.ts b/src/cliAgent.ts index 9998e221..c2575ddd 100644 --- a/src/cliAgent.ts +++ b/src/cliAgent.ts @@ -7,7 +7,7 @@ import type { AskarModuleConfigStoreOptions } from '@credo-ts/askar' import type { InitConfig } from '@credo-ts/core' import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr' -// import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' +import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' import { AnonCredsDidCommCredentialFormatService, AnonCredsModule, @@ -84,7 +84,6 @@ import { getX509CertsByClientToken, getX509CertsByUrl, } from './utils/oid4vc-agent' -import { PolygonDidRegistrar, PolygonDidResolver, PolygonModule } from '@ayanworks/credo-polygon-w3c-module' export type Transports = 'ws' | 'http' export type InboundTransport = { diff --git a/src/controllers/agent/AgentController.ts b/src/controllers/agent/AgentController.ts index 72798cdb..6d5ee6f6 100644 --- a/src/controllers/agent/AgentController.ts +++ b/src/controllers/agent/AgentController.ts @@ -2,7 +2,7 @@ import type { AgentInfo, AgentToken, SafeW3cJsonLdVerifyCredentialOptions, - CustomW3cJsonLdSignCredentialOptions, + CustomW3cJsonLdSignCredentialOptions, SignDataOptions, VerifyDataOptions } from '../types' @@ -14,9 +14,9 @@ import { ClaimFormat, W3cCredentialRecord, DidDocument, - verkeyToPublicJwk + verkeyToPublicJwk, + getKmsKeyIdForVerifiacationMethod } from '@credo-ts/core' -import { getKmsKeyIdForVerifiacationMethod } from '@credo-ts/core' import { Request as Req } from 'express' import jwt from 'jsonwebtoken' import { Controller, Get, Route, Tags, Security, Request, Post, Body, Query } from 'tsoa' @@ -25,6 +25,7 @@ import { injectable } from 'tsyringe' import { AgentRole, SCOPES } from '../../enums' import ErrorHandlingService from '../../errorHandlingService' import { BadRequestError } from '../../errors/errors' +import { ALGORITHM_MAP } from '../../utils/constant' @Tags('Agent') @Route('/agent') @@ -82,17 +83,15 @@ export class AgentController extends Controller { */ @Security('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) @Post('/verify') - public async verify(@Request() request: Req, @Body() body: VerifyDataOptions) { + public async verify(@Request() request: Req, @Body() body: VerifyDataOptions): Promise<{ verified: boolean }> { try { - const algorithmMap: Record = { - 'ed25519': 'EdDSA', - 'p256': 'ES256', - 'secp256k1': 'ES256K' + if (!body.data || !body.signature || !body.publicKeyBase58 || !body.keyType) { + throw new BadRequestError('Missing required fields: data, signature, publicKeyBase58, and keyType are required.') } // Convert verkey to JWK - const publicJwkWrapper = verkeyToPublicJwk(body.publicKeyBase58) as any - const publicJwk = publicJwkWrapper.jwk?.jwk || publicJwkWrapper.jwk || publicJwkWrapper + const publicJwkWrapper = verkeyToPublicJwk(body.publicKeyBase58) + const publicJwk = (publicJwkWrapper as any).jwk?.jwk ?? (publicJwkWrapper as any).jwk ?? publicJwkWrapper const result = await request.agent.kms.verify({ data: TypedArrayEncoder.fromBase64(body.data), @@ -100,10 +99,10 @@ export class AgentController extends Controller { key: { publicJwk: publicJwk as any }, - algorithm: (algorithmMap[body.keyType.toLowerCase()] || body.keyType) as any + algorithm: (ALGORITHM_MAP[body.keyType.toLowerCase()] || body.keyType) as any }) - return result.verified + return { verified: result.verified } } catch (error) { throw ErrorHandlingService.handle(error) } @@ -117,111 +116,119 @@ export class AgentController extends Controller { public async signCredential( @Request() request: Req, @Query('storeCredential') storeCredential: boolean, - @Query('dataTypeToSign') dataTypeToSign: 'rawData' | 'jsonLd' | string, - @Body() data: any, + @Query('dataTypeToSign') dataTypeToSign: 'rawData' | 'jsonLd', + @Body() body: CustomW3cJsonLdSignCredentialOptions | SignDataOptions, ) { try { const typeToSign = (dataTypeToSign || 'rawData').toLowerCase() - request.agent.config.logger.info(`[SignCredential] dataTypeToSign: ${dataTypeToSign}, typeToSign: ${typeToSign}, storeCredential: ${storeCredential}`); + request.agent.config.logger.info( + `[SignCredential] dataTypeToSign: ${dataTypeToSign}, typeToSign: ${typeToSign}, storeCredential: ${storeCredential}`, + ) - // JSON-LD VC Signing if (typeToSign === 'jsonld') { - const credentialData = data as any - - // Ensure signerOptions is populated if top-level fields are provided - if (!credentialData.signerOptions && (credentialData.verificationMethod || credentialData.proofType)) { - credentialData.signerOptions = { - verificationMethod: credentialData.verificationMethod, - type: credentialData.proofType, - method: 'did' // default to did if not specified - } - } - - credentialData.format = ClaimFormat.LdpVc - const signedCredential = (await request.agent.w3cCredentials.signCredential( - credentialData, - )) as W3cJsonLdVerifiableCredential - - if (storeCredential) { - const record = W3cCredentialRecord.fromCredential(signedCredential) - return await request.agent.w3cCredentials.store({ record }) - } - return signedCredential.toJson() + return await this.signJsonLd(request, body as CustomW3cJsonLdSignCredentialOptions, storeCredential) } - // Raw Data Signing - const rawData = data as SignDataOptions - if (!rawData.data) throw new BadRequestError('Missing "data" for raw data signing.') + return await this.signRawData(request, body as SignDataOptions) + } catch (error) { + const err = error as any + request.agent.config.logger.error(`[SignCredential] Error: ${err.message}`, { stack: err.stack }) + throw ErrorHandlingService.handle(error) + } + } + + private async signJsonLd(request: Req, body: CustomW3cJsonLdSignCredentialOptions, storeCredential: boolean) { + const credentialData = body as any - 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.') + // Ensure signerOptions is populated if top-level fields are provided + if (!credentialData.signerOptions && (credentialData.verificationMethod || credentialData.proofType)) { + credentialData.signerOptions = { + verificationMethod: credentialData.verificationMethod, + type: credentialData.proofType, + method: 'did', // default to did if not specified } + } + + credentialData.format = ClaimFormat.LdpVc + const signedCredential = (await request.agent.w3cCredentials.signCredential( + credentialData, + )) as W3cJsonLdVerifiableCredential + + if (storeCredential) { + const record = W3cCredentialRecord.fromCredential(signedCredential) + return await request.agent.w3cCredentials.store({ record }) + } + return signedCredential.toJson() + } + + private async signRawData(request: Req, body: SignDataOptions) { + if (!body.data) throw new BadRequestError('Missing "data" for raw data signing.') + + const kmsKeyId = await this.resolveKmsKeyId(request, body) + + const signature = await request.agent.kms.sign({ + data: TypedArrayEncoder.fromBase64(body.data), + keyId: kmsKeyId as string, + algorithm: (body.keyType ? ALGORITHM_MAP[body.keyType.toLowerCase()] || body.keyType : 'EdDSA') as any, + }) + + return TypedArrayEncoder.toBase64(signature.signature) + } - let kmsKeyId: string | undefined = undefined - if (hasDidOrMethod) { - let didDocument: DidDocument | undefined | null = undefined - const dids = await request.agent.dids.getCreatedDids({ - method: rawData.method || undefined, - did: rawData.did || undefined, - }) - - const didRecord = dids[0] - if (didRecord) { - didDocument = didRecord.didDocument - if (didRecord.keys && didRecord.keys.length > 0) { - kmsKeyId = didRecord.keys[0].kmsKeyId - } - } - - if (!didDocument && rawData.did) { - const resolution = await request.agent.dids.resolve(rawData.did) - didDocument = resolution.didDocument - } - - if (!didDocument) { - throw new BadRequestError('No DID document found.') - } - - if (!kmsKeyId) { - const verificationMethod = didDocument.verificationMethod?.[0] - if (!verificationMethod) { - throw new BadRequestError('No verification method found on DID document.') - } - - // Try multiple ways to get the kmsKeyId - const derivedKeyId = getKmsKeyIdForVerifiacationMethod(verificationMethod) - const publicKeyBase58 = (verificationMethod as any).publicKeyBase58 - const vmId = verificationMethod.id || '' - const idPart = vmId.includes('#') ? vmId.split('#')[1] : undefined - - kmsKeyId = (derivedKeyId || publicKeyBase58 || idPart || vmId) as string - - request.agent.config.logger.info(`[SignCredential] Resolved kmsKeyId via fallback: ${kmsKeyId}`); - } - } else { - kmsKeyId = rawData.publicKeyBase58 + private async resolveKmsKeyId(request: Req, body: SignDataOptions): Promise { + const hasDidOrMethod = body.did || body.method + const hasPublicKey = body.publicKeyBase58 && body.keyType + if (!hasDidOrMethod && !hasPublicKey) { + throw new BadRequestError('Either (did or method) OR (publicKeyBase58 and keyType) must be provided.') + } + + if (!hasDidOrMethod) { + return body.publicKeyBase58 + } + + let kmsKeyId: string | undefined = undefined + let didDocument: DidDocument | undefined | null = undefined + + const dids = await request.agent.dids.getCreatedDids({ + method: body.method || undefined, + did: body.did || undefined, + }) + + const didRecord = dids[0] + if (didRecord) { + didDocument = didRecord.didDocument + if (didRecord.keys && didRecord.keys.length > 0) { + kmsKeyId = didRecord.keys[0].kmsKeyId } + } + + if (!didDocument && body.did) { + const resolution = await request.agent.dids.resolve(body.did) + didDocument = resolution.didDocument + } - const algorithmMap: Record = { - 'ed25519': 'EdDSA', - 'p256': 'ES256', - 'secp256k1': 'ES256K' + if (!didDocument) { + throw new BadRequestError('No DID document found.') + } + + if (!kmsKeyId) { + const verificationMethod = didDocument.verificationMethod?.[0] + if (!verificationMethod) { + throw new BadRequestError('No verification method found on DID document.') } - const signature = await request.agent.kms.sign({ - data: TypedArrayEncoder.fromBase64(rawData.data), - keyId: kmsKeyId as string, - algorithm: (rawData.keyType ? (algorithmMap[rawData.keyType.toLowerCase()] || rawData.keyType) : 'EdDSA') as any - }) + // Try multiple ways to get the kmsKeyId + const derivedKeyId = getKmsKeyIdForVerifiacationMethod(verificationMethod) + const publicKeyBase58 = (verificationMethod as any).publicKeyBase58 + const vmId = verificationMethod.id || '' + const idPart = vmId.includes('#') ? vmId.split('#')[1] : undefined - return TypedArrayEncoder.toBase64(signature.signature) - } catch (error) { - const err = error as any - request.agent.config.logger.error(`[SignCredential] Error: ${err.message}`, { stack: err.stack }); - throw ErrorHandlingService.handle(error) + kmsKeyId = (derivedKeyId || publicKeyBase58 || idPart || vmId) as string + + request.agent.config.logger.info(`[SignCredential] Resolved kmsKeyId via fallback: ${kmsKeyId}`) } + + return kmsKeyId } @Security('jwt', [SCOPES.TENANT_AGENT, SCOPES.DEDICATED_AGENT]) diff --git a/src/controllers/types.ts b/src/controllers/types.ts index 1c5ef7b3..11673ee7 100644 --- a/src/controllers/types.ts +++ b/src/controllers/types.ts @@ -464,10 +464,15 @@ export type ExtensibleW3cCredential = W3cCredential & { credentialSubject: SingleOrArray } -export type CustomW3cJsonLdSignCredentialOptions = Omit & { +export type CustomW3cJsonLdSignCredentialOptions = Omit & { + credential: Omit & { + '@context': Array>, + credentialSubject: SingleOrArray + } [key: string]: unknown } + export type DisclosureFrame = { [key: string]: boolean | DisclosureFrame } @@ -550,10 +555,10 @@ export interface SchemaResponseDTO { export interface RegisterSchemaReturn { jobId?: string schemaState: - | RegisterSchemaReturnStateWait - | RegisterSchemaReturnStateAction - | RegisterSchemaReturnStateFinished - | RegisterSchemaReturnStateFailed + | RegisterSchemaReturnStateWait + | RegisterSchemaReturnStateAction + | RegisterSchemaReturnStateFinished + | RegisterSchemaReturnStateFailed schemaMetadata: Record registrationMetadata: Record } diff --git a/src/routes/routes.ts b/src/routes/routes.ts index c97e88a8..11840794 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -919,7 +919,7 @@ const models: TsoaRoute.Models = { "W3cCredential": { "dataType": "refObject", "properties": { - "context": {"dataType":"array","array":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"JsonObject"}]},"required":true}, + "@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}, @@ -1473,6 +1473,41 @@ const models: TsoaRoute.Models = { "additionalProperties": {"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 + "Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format-or-credential__": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"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-or-credential_": { + "dataType": "refAlias", + "type": {"ref":"Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format-or-credential__","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_W3cCredential.Exclude_keyofW3cCredential.context-or-credentialSubject__": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"issuer":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"W3cIssuer"}],"required":true},"type":{"dataType":"array","array":{"dataType":"string"},"required":true},"id":{"dataType":"string"},"@context":{"dataType":"array","array":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"JsonObject"}]},"required":true},"issuanceDate":{"dataType":"string","required":true},"expirationDate":{"dataType":"string"},"credentialSchema":{"ref":"SingleOrArray_W3cCredentialSchema_"},"credentialStatus":{"ref":"W3cCredentialStatus"}},"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_W3cCredential.context-or-credentialSubject_": { + "dataType": "refAlias", + "type": {"ref":"Pick_W3cCredential.Exclude_keyofW3cCredential.context-or-credentialSubject__","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_JsonObject_": { + "dataType": "refAlias", + "type": {"dataType":"union","subSchemas":[{"ref":"JsonObject"},{"dataType":"array","array":{"dataType":"refObject","ref":"JsonObject"}}],"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-or-credential_"},{"dataType":"nestedObjectLiteral","nestedProperties":{"credential":{"dataType":"intersection","subSchemas":[{"ref":"Omit_W3cCredential.context-or-credentialSubject_"},{"dataType":"nestedObjectLiteral","nestedProperties":{"credentialSubject":{"ref":"SingleOrArray_JsonObject_","required":true},"@context":{"dataType":"array","array":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"Record_string.unknown_"}]},"required":true}}}],"required":true}},"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":{"dataType":"any","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 "Partial_W3cCredentialValidations_": { "dataType": "refAlias", "type": {"dataType":"nestedObjectLiteral","nestedProperties":{},"validators":{}}, @@ -1568,7 +1603,7 @@ const models: TsoaRoute.Models = { "W3cJsonLdVerifiableCredential": { "dataType": "refObject", "properties": { - "context": {"dataType":"array","array":{"dataType":"union","subSchemas":[{"dataType":"string"},{"ref":"JsonObject"}]},"required":true}, + "@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}, @@ -1835,11 +1870,6 @@ 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 - "SingleOrArray_JsonObject_": { - "dataType": "refAlias", - "type": {"dataType":"union","subSchemas":[{"ref":"JsonObject"},{"dataType":"array","array":{"dataType":"refObject","ref":"JsonObject"}}],"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 "JsonCredential": { "dataType": "refObject", "properties": { @@ -4783,8 +4813,8 @@ export function RegisterRoutes(app: Router) { const argsAgentController_signCredential: Record = { request: {"in":"request","name":"request","required":true,"dataType":"object"}, storeCredential: {"in":"query","name":"storeCredential","required":true,"dataType":"boolean"}, - dataTypeToSign: {"in":"query","name":"dataTypeToSign","required":true,"dataType":"union","subSchemas":[{"dataType":"enum","enums":["rawData"]},{"dataType":"enum","enums":["jsonLd"]},{"dataType":"string"}]}, - data: {"in":"body","name":"data","required":true,"dataType":"any"}, + dataTypeToSign: {"in":"query","name":"dataTypeToSign","required":true,"dataType":"union","subSchemas":[{"dataType":"enum","enums":["rawData"]},{"dataType":"enum","enums":["jsonLd"]}]}, + body: {"in":"body","name":"body","required":true,"dataType":"union","subSchemas":[{"ref":"CustomW3cJsonLdSignCredentialOptions"},{"ref":"SignDataOptions"}]}, }; app.post('/agent/credential/sign', authenticateMiddleware([{"jwt":["tenant","dedicated"]}]), diff --git a/src/routes/swagger.json b/src/routes/swagger.json index 97c803ad..7f02a7c9 100644 --- a/src/routes/swagger.json +++ b/src/routes/swagger.json @@ -2016,7 +2016,7 @@ }, "W3cCredential": { "properties": { - "context": { + "@context": { "items": { "anyOf": [ { @@ -2065,7 +2065,7 @@ } }, "required": [ - "context", + "@context", "type", "issuer", "issuanceDate", @@ -3354,6 +3354,176 @@ "type": "object", "additionalProperties": {} }, + "Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format-or-credential__": { + "properties": { + "proofType": { + "type": "string", + "description": "The proofType to be used for signing the credential.\n\nMust be a valid Linked Data Signature suite." + }, + "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": [ + "proofType", + "verificationMethod" + ], + "type": "object", + "description": "From T, pick a set of properties whose keys are in the union K" + }, + "Omit_W3cJsonLdSignCredentialOptions.format-or-credential_": { + "$ref": "#/components/schemas/Pick_W3cJsonLdSignCredentialOptions.Exclude_keyofW3cJsonLdSignCredentialOptions.format-or-credential__", + "description": "Construct a type with the properties of T except for those in type K." + }, + "Pick_W3cCredential.Exclude_keyofW3cCredential.context-or-credentialSubject__": { + "properties": { + "issuer": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/W3cIssuer" + } + ] + }, + "type": { + "items": { + "type": "string" + }, + "type": "array" + }, + "id": { + "type": "string" + }, + "@context": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/JsonObject" + } + ] + }, + "type": "array" + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialSchema": { + "$ref": "#/components/schemas/SingleOrArray_W3cCredentialSchema_" + }, + "credentialStatus": { + "$ref": "#/components/schemas/W3cCredentialStatus" + } + }, + "required": [ + "issuer", + "type", + "@context", + "issuanceDate" + ], + "type": "object", + "description": "From T, pick a set of properties whose keys are in the union K" + }, + "Omit_W3cCredential.context-or-credentialSubject_": { + "$ref": "#/components/schemas/Pick_W3cCredential.Exclude_keyofW3cCredential.context-or-credentialSubject__", + "description": "Construct a type with the properties of T except for those in type K." + }, + "SingleOrArray_JsonObject_": { + "anyOf": [ + { + "$ref": "#/components/schemas/JsonObject" + }, + { + "items": { + "$ref": "#/components/schemas/JsonObject" + }, + "type": "array" + } + ] + }, + "CustomW3cJsonLdSignCredentialOptions": { + "allOf": [ + { + "$ref": "#/components/schemas/Omit_W3cJsonLdSignCredentialOptions.format-or-credential_" + }, + { + "properties": { + "credential": { + "allOf": [ + { + "$ref": "#/components/schemas/Omit_W3cCredential.context-or-credentialSubject_" + }, + { + "properties": { + "credentialSubject": { + "$ref": "#/components/schemas/SingleOrArray_JsonObject_" + }, + "@context": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/Record_string.unknown_" + } + ] + }, + "type": "array" + } + }, + "required": [ + "credentialSubject", + "@context" + ], + "type": "object" + } + ] + } + }, + "additionalProperties": {}, + "required": [ + "credential" + ], + "type": "object" + } + ] + }, + "SignDataOptions": { + "properties": { + "method": { + "type": "string" + }, + "did": { + "type": "string" + }, + "publicKeyBase58": { + "type": "string" + }, + "keyType": {}, + "data": { + "type": "string" + } + }, + "required": [ + "publicKeyBase58", + "keyType", + "data" + ], + "type": "object" + }, "Partial_W3cCredentialValidations_": { "properties": {}, "type": "object", @@ -3586,7 +3756,7 @@ }, "W3cJsonLdVerifiableCredential": { "properties": { - "context": { + "@context": { "items": { "anyOf": [ { @@ -3638,7 +3808,7 @@ } }, "required": [ - "context", + "@context", "type", "issuer", "issuanceDate", @@ -4233,19 +4403,6 @@ "type": "object", "additionalProperties": false }, - "SingleOrArray_JsonObject_": { - "anyOf": [ - { - "$ref": "#/components/schemas/JsonObject" - }, - { - "items": { - "$ref": "#/components/schemas/JsonObject" - }, - "type": "array" - } - ] - }, "JsonCredential": { "properties": { "@context": { @@ -9175,7 +9332,15 @@ "content": { "application/json": { "schema": { - "type": "boolean" + "properties": { + "verified": { + "type": "boolean" + } + }, + "required": [ + "verified" + ], + "type": "object" } } } @@ -9258,14 +9423,29 @@ "in": "query", "name": "dataTypeToSign", "required": true, - "schema": {} + "schema": { + "type": "string", + "enum": [ + "rawData", + "jsonLd" + ] + } } ], "requestBody": { "required": true, "content": { "application/json": { - "schema": {} + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/CustomW3cJsonLdSignCredentialOptions" + }, + { + "$ref": "#/components/schemas/SignDataOptions" + } + ] + } } } } diff --git a/src/utils/constant.ts b/src/utils/constant.ts index 179b75bf..9a53da47 100644 --- a/src/utils/constant.ts +++ b/src/utils/constant.ts @@ -29,3 +29,9 @@ export const curveToKty = { export const verkey = '#verkey' export const p521 = 'p521' export const STATUS_LISTS_PATH = 'status-lists' + +export const ALGORITHM_MAP: Record = { + ed25519: 'EdDSA', + p256: 'ES256', + secp256k1: 'ES256K', +} From fcf9592eeb4577b8a81e5239d15f8d69eb82f0d6 Mon Sep 17 00:00:00 2001 From: Sagar Khole Date: Wed, 13 May 2026 19:49:52 +0530 Subject: [PATCH 5/5] Solve lint errors Signed-off-by: Sagar Khole --- src/controllers/agent/AgentController.ts | 12 +++++++----- src/controllers/types.ts | 11 +++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/controllers/agent/AgentController.ts b/src/controllers/agent/AgentController.ts index af4b3817..f7da7807 100644 --- a/src/controllers/agent/AgentController.ts +++ b/src/controllers/agent/AgentController.ts @@ -4,7 +4,7 @@ import type { SafeW3cJsonLdVerifyCredentialOptions, CustomW3cJsonLdSignCredentialOptions, SignDataOptions, - VerifyDataOptions + VerifyDataOptions, } from '../types' import { @@ -15,7 +15,7 @@ import { W3cCredentialRecord, DidDocument, verkeyToPublicJwk, - getKmsKeyIdForVerifiacationMethod + getKmsKeyIdForVerifiacationMethod, } from '@credo-ts/core' import { Request as Req } from 'express' import jwt from 'jsonwebtoken' @@ -86,7 +86,9 @@ export class AgentController extends Controller { public async verify(@Request() request: Req, @Body() body: VerifyDataOptions): Promise<{ verified: boolean }> { try { if (!body.data || !body.signature || !body.publicKeyBase58 || !body.keyType) { - throw new BadRequestError('Missing required fields: data, signature, publicKeyBase58, and keyType are required.') + throw new BadRequestError( + 'Missing required fields: data, signature, publicKeyBase58, and keyType are required.', + ) } // Convert verkey to JWK @@ -97,9 +99,9 @@ export class AgentController extends Controller { data: TypedArrayEncoder.fromBase64(body.data), signature: TypedArrayEncoder.fromBase64(body.signature), key: { - publicJwk: publicJwk as any + publicJwk: publicJwk as any, }, - algorithm: (ALGORITHM_MAP[body.keyType.toLowerCase()] || body.keyType) as any + algorithm: (ALGORITHM_MAP[body.keyType.toLowerCase()] || body.keyType) as any, }) return { verified: result.verified } diff --git a/src/controllers/types.ts b/src/controllers/types.ts index 11673ee7..ce3b0a5a 100644 --- a/src/controllers/types.ts +++ b/src/controllers/types.ts @@ -466,13 +466,12 @@ export type ExtensibleW3cCredential = W3cCredential & { export type CustomW3cJsonLdSignCredentialOptions = Omit & { credential: Omit & { - '@context': Array>, + '@context': Array> credentialSubject: SingleOrArray } [key: string]: unknown } - export type DisclosureFrame = { [key: string]: boolean | DisclosureFrame } @@ -555,10 +554,10 @@ export interface SchemaResponseDTO { export interface RegisterSchemaReturn { jobId?: string schemaState: - | RegisterSchemaReturnStateWait - | RegisterSchemaReturnStateAction - | RegisterSchemaReturnStateFinished - | RegisterSchemaReturnStateFailed + | RegisterSchemaReturnStateWait + | RegisterSchemaReturnStateAction + | RegisterSchemaReturnStateFinished + | RegisterSchemaReturnStateFailed schemaMetadata: Record registrationMetadata: Record }