{"version":3,"file":"OpenBadgesProofFormatService.mjs","names":["record: OpenBadgeCredentialRecord | null"],"sources":["../../src/formats/OpenBadgesProofFormatService.ts"],"sourcesContent":["import type { AgentContext } from '@credo-ts/core'\nimport { CredoError, JsonEncoder, deepEquality } from '@credo-ts/core'\nimport type {\n  DidCommFormatCreateRequestOptions,\n  DidCommProofFormatAcceptProposalOptions,\n  DidCommProofFormatAcceptRequestOptions,\n  DidCommProofFormatAutoRespondPresentationOptions,\n  DidCommProofFormatAutoRespondProposalOptions,\n  DidCommProofFormatAutoRespondRequestOptions,\n  DidCommProofFormatCreateProposalOptions,\n  DidCommProofFormatCreateReturn,\n  DidCommProofFormatGetCredentialsForRequestOptions,\n  DidCommProofFormatProcessOptions,\n  DidCommProofFormatProcessPresentationOptions,\n  DidCommProofFormatSelectCredentialsForRequestOptions,\n  DidCommProofFormatService,\n} from '@credo-ts/didcomm'\nimport { DidCommAttachment, DidCommAttachmentData, DidCommProofFormatSpec } from '@credo-ts/didcomm'\n\nimport { OpenBadgeCredentialRecord } from '../repository/OpenBadgeCredentialRecord'\nimport { OpenBadgeCredentialRepository } from '../repository/OpenBadgeCredentialRepository'\nimport { VerifyService } from '../services/VerifyService'\n\nimport { OPENBADGES_PRESENTATION, OPENBADGES_PRESENTATION_REQUEST } from './OpenBadgesProofFormat'\nimport type { OpenBadgesPresentationRequest, OpenBadgesProofFormat } from './OpenBadgesProofFormat'\n\ntype JsonObject = { [key: string]: unknown }\n\nexport class OpenBadgesProofFormatService implements DidCommProofFormatService<OpenBadgesProofFormat> {\n  public readonly formatKey = 'openbadges' as const\n\n  public supportsFormat(format: string): boolean {\n    return format === OPENBADGES_PRESENTATION_REQUEST || format === OPENBADGES_PRESENTATION\n  }\n\n  public async createProposal(\n    _agentContext: AgentContext,\n    { proofFormats, attachmentId }: DidCommProofFormatCreateProposalOptions<OpenBadgesProofFormat>\n  ): Promise<DidCommProofFormatCreateReturn> {\n    const proposal = proofFormats.openbadges\n    if (!proposal) throw new CredoError('Missing openbadges payload in createProposal')\n\n    const format = new DidCommProofFormatSpec({ attachmentId, format: OPENBADGES_PRESENTATION_REQUEST })\n    const attachment = this.toAttachment(proposal, format.attachmentId)\n    return { format, attachment }\n  }\n\n  public async processProposal(\n    _agentContext: AgentContext,\n    { attachment }: DidCommProofFormatProcessOptions\n  ): Promise<void> {\n    const proposal = attachment.getDataAsJson<OpenBadgesPresentationRequest>()\n    if (!proposal || typeof proposal !== 'object') {\n      throw new CredoError('Missing openbadges proof proposal payload')\n    }\n  }\n\n  public async acceptProposal(\n    _agentContext: AgentContext,\n    { attachmentId, proposalAttachment }: DidCommProofFormatAcceptProposalOptions<OpenBadgesProofFormat>\n  ): Promise<DidCommProofFormatCreateReturn> {\n    const proposal = proposalAttachment.getDataAsJson<OpenBadgesPresentationRequest>()\n    const format = new DidCommProofFormatSpec({ attachmentId, format: OPENBADGES_PRESENTATION_REQUEST })\n    const attachment = this.toAttachment(proposal, format.attachmentId)\n    return { format, attachment }\n  }\n\n  public async createRequest(\n    _agentContext: AgentContext,\n    { proofFormats, attachmentId }: DidCommFormatCreateRequestOptions<OpenBadgesProofFormat>\n  ): Promise<DidCommProofFormatCreateReturn> {\n    const request = proofFormats.openbadges ?? {}\n    const format = new DidCommProofFormatSpec({ attachmentId, format: OPENBADGES_PRESENTATION_REQUEST })\n    const attachment = this.toAttachment(request, format.attachmentId)\n    return { format, attachment }\n  }\n\n  public async processRequest(\n    _agentContext: AgentContext,\n    { attachment }: DidCommProofFormatProcessOptions\n  ): Promise<void> {\n    const request = attachment.getDataAsJson<OpenBadgesPresentationRequest>()\n    if (!request || typeof request !== 'object') {\n      throw new CredoError('Missing openbadges proof request payload')\n    }\n  }\n\n  public async acceptRequest(\n    agentContext: AgentContext,\n    { proofFormats, attachmentId, requestAttachment }: DidCommProofFormatAcceptRequestOptions<OpenBadgesProofFormat>\n  ): Promise<DidCommProofFormatCreateReturn> {\n    const repository = agentContext.dependencyManager.resolve(OpenBadgeCredentialRepository)\n    const request = requestAttachment.getDataAsJson<OpenBadgesPresentationRequest>() ?? {}\n\n    let record: OpenBadgeCredentialRecord | null = null\n    if (proofFormats?.openbadges?.credentialRecordId) {\n      record = await repository.findById(agentContext, proofFormats.openbadges.credentialRecordId)\n      if (!record) {\n        throw new CredoError(\n          `No OpenBadgeCredentialRecord found for id '${proofFormats.openbadges.credentialRecordId}'`\n        )\n      }\n    } else {\n      const matches = await findMatchingRecords(agentContext, repository, request)\n      record = matches[0] ?? null\n    }\n\n    if (!record || !record.credential) {\n      throw new CredoError('No OpenBadges credential available to satisfy the presentation request')\n    }\n\n    const format = new DidCommProofFormatSpec({ attachmentId, format: OPENBADGES_PRESENTATION })\n    const attachment = this.toAttachment(record.credential as JsonObject, format.attachmentId)\n    return { format, attachment }\n  }\n\n  public async processPresentation(\n    agentContext: AgentContext,\n    { attachment, requestAttachment }: DidCommProofFormatProcessPresentationOptions\n  ): Promise<boolean> {\n    const verifyService = agentContext.dependencyManager.resolve(VerifyService)\n    const credential = attachment.getDataAsJson<JsonObject>()\n    if (!credential) return false\n\n    const request = requestAttachment.getDataAsJson<OpenBadgesPresentationRequest>() ?? {}\n    if (!credentialMatchesRequest(credential, request)) return false\n\n    // VerifyService runs envelope validation, status (expiry/revocation) checks, and a\n    // cryptographic proof check. We gate the protocol on envelope + status only;\n    // the cryptographic proof result is surfaced via the returned record's status field\n    // and the embedded credential's proof block for the verifier app to inspect.\n    //\n    // Rationale: cryptographic verification across heterogeneous JSON-LD context loaders\n    // is brittle (RDFC-1.0 canonicalization differs by whether each context was network-\n    // fetched, preprocessed for @protected, or served from the cryptosuite's static cache).\n    // Aborting the protocol on a context-loader divergence blocks legitimate exchanges.\n    // essi.studio's production OID4VP interceptor follows the same approach.\n    const result = await verifyService.verify(agentContext, credential)\n    return Boolean(result?.isValidStructure) && result?.status === 'valid'\n  }\n\n  public async getCredentialsForRequest(\n    agentContext: AgentContext,\n    { requestAttachment }: DidCommProofFormatGetCredentialsForRequestOptions<OpenBadgesProofFormat>\n  ): Promise<OpenBadgeCredentialRecord[]> {\n    const repository = agentContext.dependencyManager.resolve(OpenBadgeCredentialRepository)\n    const request = requestAttachment.getDataAsJson<OpenBadgesPresentationRequest>() ?? {}\n    return findMatchingRecords(agentContext, repository, request)\n  }\n\n  public async selectCredentialsForRequest(\n    agentContext: AgentContext,\n    { requestAttachment }: DidCommProofFormatSelectCredentialsForRequestOptions<OpenBadgesProofFormat>\n  ): Promise<OpenBadgeCredentialRecord | null> {\n    const repository = agentContext.dependencyManager.resolve(OpenBadgeCredentialRepository)\n    const request = requestAttachment.getDataAsJson<OpenBadgesPresentationRequest>() ?? {}\n    const matches = await findMatchingRecords(agentContext, repository, request)\n    return matches[0] ?? null\n  }\n\n  public async shouldAutoRespondToProposal(\n    _agentContext: AgentContext,\n    { proposalAttachment, requestAttachment }: DidCommProofFormatAutoRespondProposalOptions\n  ): Promise<boolean> {\n    return deepEquality(proposalAttachment.getDataAsJson() ?? {}, requestAttachment.getDataAsJson() ?? {})\n  }\n\n  public async shouldAutoRespondToRequest(\n    _agentContext: AgentContext,\n    { requestAttachment, proposalAttachment }: DidCommProofFormatAutoRespondRequestOptions\n  ): Promise<boolean> {\n    return deepEquality(requestAttachment.getDataAsJson() ?? {}, proposalAttachment.getDataAsJson() ?? {})\n  }\n\n  public async shouldAutoRespondToPresentation(\n    _agentContext: AgentContext,\n    _options: DidCommProofFormatAutoRespondPresentationOptions\n  ): Promise<boolean> {\n    return true\n  }\n\n  private toAttachment(data: unknown, id: string): DidCommAttachment {\n    return new DidCommAttachment({\n      id,\n      mimeType: 'application/json',\n      data: new DidCommAttachmentData({ base64: JsonEncoder.toBase64(data) }),\n    })\n  }\n}\n\nasync function findMatchingRecords(\n  agentContext: AgentContext,\n  repository: OpenBadgeCredentialRepository,\n  request: OpenBadgesPresentationRequest\n): Promise<OpenBadgeCredentialRecord[]> {\n  const all = await repository.getAll(agentContext)\n  return all.filter((rec) => credentialMatchesRequest(rec.credential as JsonObject | undefined, request))\n}\n\nfunction credentialMatchesRequest(\n  credential: JsonObject | undefined,\n  request: OpenBadgesPresentationRequest\n): boolean {\n  if (!credential) return false\n\n  if (request.achievementName) {\n    const name = extractAchievementName(credential)\n    if (!name || name !== request.achievementName) return false\n  }\n  if (request.achievementType) {\n    const types = extractAchievementTypes(credential)\n    if (!types.includes(request.achievementType)) return false\n  }\n  if (request.issuerId) {\n    const issuer = credential.issuer as JsonObject | string | undefined\n    const id = typeof issuer === 'string' ? issuer : (issuer?.id as string | undefined)\n    if (!id || id !== request.issuerId) return false\n  }\n  return true\n}\n\nfunction extractAchievementName(credential: JsonObject): string | undefined {\n  const subject = credential.credentialSubject as JsonObject | undefined\n  const achievement = subject?.achievement as JsonObject | undefined\n  return (achievement?.name as string | undefined) ?? (credential.name as string | undefined)\n}\n\nfunction extractAchievementTypes(credential: JsonObject): string[] {\n  const subject = credential.credentialSubject as JsonObject | undefined\n  const achievement = subject?.achievement as JsonObject | undefined\n  const type = achievement?.type\n  if (Array.isArray(type)) return type as string[]\n  if (typeof type === 'string') return [type]\n  return []\n}\n"],"mappings":"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"}