{"version":3,"file":"v2-jwt-utils.mjs","names":[],"sources":["../../../src/modules/vc/v2-jwt-utils.ts"],"sourcesContent":["import { AgentContext } from '../../agent'\nimport { CredoError } from '../../error'\nimport { asArray, isDid } from '../../utils'\nimport {\n  type DidPurpose,\n  DidResolverService,\n  DidsApi,\n  getPublicJwkFromVerificationMethod,\n  parseDid,\n  VerificationMethod,\n} from '../dids'\nimport { getSupportedVerificationMethodTypesForPublicJwk } from '../dids/domain/key-type/keyDidMapping'\nimport { type KnownJwaSignatureAlgorithm, PublicJwk } from '../kms'\nimport { extractKeyFromHolderBinding, parseHolderBindingFromCredential } from '../sd-jwt-vc/utils'\nimport { W3cV2JwtVerifiableCredential, W3cV2JwtVerifiablePresentation } from './jwt-vc'\nimport { W3cV2Presentation } from './models'\nimport { W3cV2SdJwtVerifiableCredential, W3cV2SdJwtVerifiablePresentation } from './sd-jwt-vc'\n\nexport async function validateAndResolveVerificationMethod(\n  agentContext: AgentContext,\n  verificationMethod: string,\n  allowsPurposes?: DidPurpose[]\n) {\n  if (!isDid(verificationMethod)) {\n    throw new CredoError('Only did identifiers are supported as verification method')\n  }\n\n  const parsedDid = parseDid(verificationMethod)\n  if (!parsedDid.fragment) {\n    throw new CredoError(`didUrl '${parsedDid.didUrl}' does not contain a '#'. Unable to derive key from did document`)\n  }\n\n  const dids = agentContext.resolve(DidsApi)\n  const { didDocument, keys } = await dids.resolveCreatedDidDocumentWithKeys(parsedDid.did)\n  const verificationMethodObject = didDocument.dereferenceKey(verificationMethod, allowsPurposes)\n  const publicJwk = getPublicJwkFromVerificationMethod(verificationMethodObject)\n\n  publicJwk.keyId =\n    keys?.find(({ didDocumentRelativeKeyId }) => verificationMethodObject.id.endsWith(didDocumentRelativeKeyId))\n      ?.kmsKeyId ?? publicJwk.legacyKeyId\n\n  return publicJwk\n}\n\nexport async function extractHolderFromPresentationCredentials(\n  agentContext: AgentContext,\n  presentation: W3cV2Presentation\n) {\n  // At the moment, we only support signing presentations with a single credential\n  // Technically, it should be possible to support more than a single one. However,\n  // in the context we support (OID4VP) it doesn't make sense to support multiple ones.\n  const credentials = asArray(presentation.verifiableCredential)\n  if (credentials.length !== 1) {\n    throw new CredoError('Only a single verifiable credential is supported in a presentation')\n  }\n\n  const credential = credentials[0]\n  let claims: Record<string, unknown>\n\n  if (credential.envelopedCredential instanceof W3cV2SdJwtVerifiableCredential) {\n    claims = credential.envelopedCredential.sdJwt.prettyClaims\n  } else {\n    claims = credential.envelopedCredential.jwt.payload.toJson()\n  }\n\n  // We require the credential to include a holder binding in the form of a 'cnf' claim\n  const holderBinding = parseHolderBindingFromCredential(claims)\n  if (!holderBinding) {\n    throw new CredoError('No holder binding found in credential included in presentation')\n  }\n\n  return await extractKeyFromHolderBinding(agentContext, holderBinding, { forSigning: true })\n}\n\n/**\n * This method tries to find the verification method associated with the JWT credential or presentation.\n * This verification method can then be used to verify the credential or presentation signature.\n */\nexport async function getVerificationMethodForJwt(\n  agentContext: AgentContext,\n  credential:\n    | W3cV2JwtVerifiableCredential\n    | W3cV2JwtVerifiablePresentation\n    | W3cV2SdJwtVerifiableCredential\n    | W3cV2SdJwtVerifiablePresentation,\n  purpose?: DidPurpose[]\n) {\n  let alg: KnownJwaSignatureAlgorithm\n  let kid: string | undefined\n  let iss: string | undefined\n\n  // Determine the algorithm, kid and iss based on the type of credential / presentation\n  if (credential instanceof W3cV2JwtVerifiableCredential) {\n    alg = credential.jwt.header.alg as KnownJwaSignatureAlgorithm\n    kid = credential.jwt.header.kid\n    iss = credential.jwt.payload.iss ?? credential.resolvedCredential.issuerId\n  } else if (credential instanceof W3cV2JwtVerifiablePresentation) {\n    alg = credential.jwt.header.alg as KnownJwaSignatureAlgorithm\n    kid = credential.jwt.header.kid\n    iss = credential.jwt.payload.iss ?? credential.resolvedPresentation.holderId\n  } else if (credential instanceof W3cV2SdJwtVerifiableCredential) {\n    alg = credential.sdJwt.header.alg as KnownJwaSignatureAlgorithm\n    kid = credential.sdJwt.header.kid\n    iss = credential.sdJwt.payload.iss ?? credential.resolvedCredential.issuerId\n  } else {\n    alg = credential.sdJwt.header.alg as KnownJwaSignatureAlgorithm\n    kid = credential.sdJwt.header.kid\n    iss = credential.sdJwt.payload.iss ?? credential.resolvedPresentation.holderId\n  }\n\n  const didResolver = agentContext.dependencyManager.resolve(DidResolverService)\n  let verificationMethod: VerificationMethod\n\n  // If the kid starts with # we assume it is a relative did url, and we resolve\n  //it based on the `iss` and the `kid`\n  if (kid?.startsWith('#')) {\n    if (!iss || !isDid(iss)) {\n      throw new CredoError(`JWT 'iss' MUST be a did when 'kid' is a relative did url`)\n    }\n\n    const didDocument = await didResolver.resolveDidDocument(agentContext, iss)\n    verificationMethod = didDocument.dereferenceKey(`${iss}${kid}`, purpose)\n  } else if (kid && isDid(kid)) {\n    const didDocument = await didResolver.resolveDidDocument(agentContext, kid)\n    verificationMethod = didDocument.dereferenceKey(kid, purpose)\n\n    if (iss && didDocument.id !== iss) {\n      throw new CredoError(`kid '${kid}' does not match id of signer (holder/issuer) '${iss}'`)\n    }\n  } else {\n    if (!iss) {\n      throw new CredoError(`JWT 'iss' MUST be present in payload when no 'kid' is specified`)\n    }\n\n    // Find the verificationMethod in the did document based on the alg and proofPurpose\n    const jwkClass = PublicJwk.supportedPublicJwkClassForSignatureAlgorithm(alg)\n    const supportedVerificationMethodTypes = getSupportedVerificationMethodTypesForPublicJwk(jwkClass)\n\n    const didDocument = await didResolver.resolveDidDocument(agentContext, iss)\n    const verificationMethods =\n      didDocument.assertionMethod\n        ?.map((v) => (typeof v === 'string' ? didDocument.dereferenceVerificationMethod(v) : v))\n        .filter((v) => supportedVerificationMethodTypes.includes(v.type)) ?? []\n\n    if (verificationMethods.length === 0) {\n      throw new CredoError(\n        `No verification methods found for signer '${iss}' and key type '${jwkClass.name}' for alg '${alg}'. Unable to determine which public key is associated with the credential.`\n      )\n    }\n    if (verificationMethods.length > 1) {\n      throw new CredoError(\n        `Multiple verification methods found for signer '${iss}' and key type '${jwkClass.name}' for alg '${alg}'. Unable to determine which public key is associated with the credential.`\n      )\n    }\n\n    verificationMethod = verificationMethods[0]\n  }\n\n  // Verify the controller of the verificationMethod matches the signer of the credential\n  if (iss && verificationMethod.controller !== iss) {\n    throw new CredoError(\n      `Verification method controller '${verificationMethod.controller}' does not match the signer '${iss}'`\n    )\n  }\n\n  return verificationMethod\n}\n"],"mappings":"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"}