{"version":3,"file":"JwtService.mjs","names":["JwtService","keys: KeyService","jws: JwsService","privateKey: any","header: JWTHeaderParameters","keyLike: any | undefined","publicJwk: any","e: any"],"sources":["../../src/services/JwtService.ts"],"sourcesContent":["import type { AgentContext } from '@credo-ts/core'\nimport { injectable, inject } from '@credo-ts/core'\nimport type { JWTHeaderParameters, JWK } from 'jose'\nimport { KeyService } from './KeyService'\nimport { JwsService, JwtPayload } from '@credo-ts/core'\n// no direct key math here; wallet/JwsService handles signing\n\ntype KeyHint = { id: string; controller: string }\n\n\n\n/**\n * Get the DID method from a kid string\n */\nfunction getDidMethod(kid: string): 'did:web' | 'did:key' | 'did:jwk' | null {\n  if (kid?.startsWith('did:web:')) return 'did:web'\n  if (kid?.startsWith('did:key:')) return 'did:key'\n  if (kid?.startsWith('did:jwk:')) return 'did:jwk'\n  return null\n}\n\n/**\n * Derive JWKS URL from did:web kid\n */\nfunction jwksUrlFromDidWebKid(kid: string): string | null {\n  if (!kid || !kid.startsWith('did:web:')) return null\n  let rest = kid.slice('did:web:'.length)\n  const hashIdx = rest.indexOf('#')\n  if (hashIdx !== -1) rest = rest.slice(0, hashIdx)\n  const segs = rest.split(':')\n  const hostEnc = segs.shift() || ''\n  const host = decodeURIComponent(hostEnc)\n  // For localhost or host with port, default to http, else https\n  const protocol = host.includes('localhost') || host.includes(':') ? 'http' : 'https'\n  return `${protocol}://${host}/.well-known/jwks.json`\n}\n\n/**\n * Get algorithm string for key type\n */\nfunction getAlgForKeyType(jwk: any): string {\n  if (jwk?.kty === 'OKP' && jwk?.crv === 'Ed25519') return 'EdDSA'\n  if (jwk?.kty === 'EC' && jwk?.crv === 'P-256') return 'ES256'\n  if (jwk?.kty === 'EC' && jwk?.crv === 'P-384') return 'ES384'\n  return 'EdDSA' // Default\n}\n\n@injectable()\nexport class JwtService {\n  public constructor(@inject(KeyService) private readonly keys: KeyService, @inject(JwsService) private readonly jws: JwsService) {}\n\n  // Signs a VC as a JWT (VC-JWT). For simplicity in dev, embeds public JWK in header when no known key is provided.\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  public async signVcJwt(agentContext: AgentContext, document: any, key?: KeyHint): Promise<string> {\n    const { SignJWT, generateKeyPair, exportJWK, importJWK, calculateJwkThumbprint } = await import('jose')\n\n    // Claims\n    const issuer = typeof document?.issuer === 'string' ? document.issuer : document?.issuer?.id\n    const subject = document?.credentialSubject?.id\n    const nbf = document?.validFrom ? Math.floor(Date.parse(document.validFrom) / 1000) : undefined\n\n    // For production, you’d import your Ed25519 key. For dev simplicity, generate ephemeral if no key provided.\n    let privateKey: any\n    let header: JWTHeaderParameters = { alg: 'EdDSA', typ: 'JWT' }\n\n    const logger = agentContext.config.logger\n    if (key?.id) {\n      // Use KMS-backed key via JwsService; header will contain kid and jku; JwsService signs with KMS key id mapping\n      const vmId = key.id\n      logger.debug('[OB][JwtService] sign', { vmId })\n      // Find binding to get KMS key id\n      const binding = await this.keys.getBindingByVm(agentContext, vmId)\n      if (!binding) throw new Error('vm_not_bound_to_kms_key')\n      const payload = new JwtPayload({ iss: issuer, sub: subject, nbf, additionalClaims: { vc: document } })\n      const walletKey = await this.keys.getKeyForVm(agentContext as any, vmId)\n      logger.trace('[OB][JwtService] walletKey lookup', { found: !!walletKey })\n      if (!walletKey) throw new Error('wallet_key_not_found')\n      if (!walletKey.kmsKeyId) throw new Error('wallet_key_missing_kms_key_id')\n      const jku = jwksUrlFromDidWebKid(vmId) || undefined\n      const compact = await this.jws.createJwsCompact(agentContext as any, {\n        payload,\n        keyId: walletKey.kmsKeyId,\n        protectedHeaderOptions: { alg: 'EdDSA', kid: vmId, ...(jku ? { jku } : {}) },\n      })\n      // jose SignJWT also can be used, but we leverage KMS-backed signing via JwsService\n      return compact\n    } else {\n      // Dev mode: ephemeral JWK embedded\n      const { privateKey: pk, publicKey } = await generateKeyPair('EdDSA')\n      privateKey = pk\n      const jwk = (await exportJWK(publicKey)) as JWK\n      ;(jwk as any).kty = 'OKP'\n      ;(jwk as any).crv = 'Ed25519'\n      const kid = await calculateJwkThumbprint(jwk)\n      header = { ...header, jwk, kid }\n    }\n\n    const signer = new SignJWT({ vc: document })\n      .setProtectedHeader(header)\n      .setIssuedAt()\n\n    if (issuer) signer.setIssuer(issuer)\n    if (subject) signer.setSubject(subject)\n    if (nbf != null && !Number.isNaN(nbf)) signer.setNotBefore(nbf)\n\n    return signer.sign(privateKey)\n  }\n\n  public async verifyVcJwt(agentContext: AgentContext, jwt: string): Promise<{ verified: boolean; payload?: any; error?: string }>\n  {\n    const logger = agentContext.config.logger\n    try {\n      const { decodeProtectedHeader, importJWK, jwtVerify } = await import('jose')\n      const header = decodeProtectedHeader(jwt)\n      let alg = header.alg || 'EdDSA'\n\n      // Resolve verification key\n      let keyLike: any | undefined\n\n      // 1. Try embedded JWK in header\n      if (header.jwk && typeof header.jwk === 'object') {\n        alg = getAlgForKeyType(header.jwk)\n        keyLike = await importJWK(header.jwk as any, alg)\n      }\n\n      // 2. Try to resolve from kid\n      if (!keyLike && typeof header.kid === 'string') {\n        const kid = header.kid\n        const didMethod = getDidMethod(kid)\n        logger.trace('[OB][JwtService] verify header.kid', { kid, didMethod })\n\n        // 2a. Try did:key or did:jwk - key material is embedded in the DID\n        if (didMethod === 'did:key' || didMethod === 'did:jwk') {\n          const did = kid.split('#')[0]\n          const jwk = this.keys.getJwkForDid(did)\n          logger.trace('[OB][JwtService] verify resolved JWK from DID', { resolved: !!jwk })\n          if (jwk) {\n            alg = getAlgForKeyType(jwk)\n            keyLike = await importJWK(jwk, alg)\n          }\n        }\n\n        // 2b. Try local KeyService by VM id\n        if (!keyLike) {\n          try {\n            const publicJwk: any = await this.keys.getPublicJwkByVm(agentContext, kid)\n            logger.trace('[OB][JwtService] verify local publicJwk lookup', { found: !!publicJwk })\n            if (publicJwk) {\n              alg = getAlgForKeyType(publicJwk)\n              keyLike = await importJWK(publicJwk, alg)\n            }\n          } catch { /* ignore */ }\n        }\n      }\n\n      // 3. If we still don't have a key, try jku (JWKS URL) if present or derive from did:web kid\n      if (!keyLike && typeof header.kid === 'string') {\n        const derivedJku = jwksUrlFromDidWebKid(header.kid)\n        const jkuHeader = (header as any).jku as string | undefined\n        const candidateJku = jkuHeader || derivedJku || undefined\n        logger.trace('[OB][JwtService] verify candidateJku', { candidateJku })\n\n        if (candidateJku) {\n          try {\n            const res = await fetch(candidateJku)\n            if (res.ok) {\n              const jwks = (await res.json()) as { keys?: Array<{ kid?: string; [key: string]: unknown }> }\n              const kid = header.kid\n              const match = Array.isArray(jwks?.keys) ? jwks.keys.find((k) => k.kid === kid) : undefined\n              logger.trace('[OB][JwtService] verify matched jwk from jku', { matched: !!match })\n              if (match) {\n                alg = getAlgForKeyType(match)\n                keyLike = await importJWK(match as any, alg)\n              }\n            }\n          } catch {}\n        }\n      }\n\n      if (!keyLike) return { verified: false, error: 'no_verification_key' }\n\n      // Determine allowed algorithms based on resolved key\n      const allowedAlgs = ['EdDSA', 'ES256', 'ES384']\n      const { payload } = await jwtVerify(jwt, keyLike, { algorithms: allowedAlgs, clockTolerance: 60 })\n      return { verified: true, payload }\n    } catch (e: any) {\n      const msg = e?.message ? String(e.message).split('\\n')[0]?.slice(0, 200) : String(e)\n      logger.warn('[OB][JwtService] jwtVerify failed', { error: msg })\n      return { verified: false, error: msg }\n    }\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAS,aAAa,KAAuD;AAC3E,KAAI,KAAK,WAAW,WAAW,CAAE,QAAO;AACxC,KAAI,KAAK,WAAW,WAAW,CAAE,QAAO;AACxC,KAAI,KAAK,WAAW,WAAW,CAAE,QAAO;AACxC,QAAO;;;;;AAMT,SAAS,qBAAqB,KAA4B;AACxD,KAAI,CAAC,OAAO,CAAC,IAAI,WAAW,WAAW,CAAE,QAAO;CAChD,IAAI,OAAO,IAAI,MAAM,EAAkB;CACvC,MAAM,UAAU,KAAK,QAAQ,IAAI;AACjC,KAAI,YAAY,GAAI,QAAO,KAAK,MAAM,GAAG,QAAQ;CAEjD,MAAM,UADO,KAAK,MAAM,IAAI,CACP,OAAO,IAAI;CAChC,MAAM,OAAO,mBAAmB,QAAQ;AAGxC,QAAO,GADU,KAAK,SAAS,YAAY,IAAI,KAAK,SAAS,IAAI,GAAG,SAAS,QAC1D,KAAK,KAAK;;;;;AAM/B,SAAS,iBAAiB,KAAkB;AAC1C,KAAI,KAAK,QAAQ,SAAS,KAAK,QAAQ,UAAW,QAAO;AACzD,KAAI,KAAK,QAAQ,QAAQ,KAAK,QAAQ,QAAS,QAAO;AACtD,KAAI,KAAK,QAAQ,QAAQ,KAAK,QAAQ,QAAS,QAAO;AACtD,QAAO;;AAIF,uBAAMA,aAAW;CACtB,AAAO,YAAY,AAAqCC,MAAkB,AAAqCC,KAAiB;EAAxE;EAAuD;;CAI/G,MAAa,UAAU,cAA4B,UAAe,KAAgC;EAChG,MAAM,EAAE,SAAS,iBAAiB,WAAW,WAAW,2BAA2B,MAAM,OAAO;EAGhG,MAAM,SAAS,OAAO,UAAU,WAAW,WAAW,SAAS,SAAS,UAAU,QAAQ;EAC1F,MAAM,UAAU,UAAU,mBAAmB;EAC7C,MAAM,MAAM,UAAU,YAAY,KAAK,MAAM,KAAK,MAAM,SAAS,UAAU,GAAG,IAAK,GAAG;EAGtF,IAAIC;EACJ,IAAIC,SAA8B;GAAE,KAAK;GAAS,KAAK;GAAO;EAE9D,MAAM,SAAS,aAAa,OAAO;AACnC,MAAI,KAAK,IAAI;GAEX,MAAM,OAAO,IAAI;AACjB,UAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC;AAG/C,OAAI,CADY,MAAM,KAAK,KAAK,eAAe,cAAc,KAAK,CACpD,OAAM,IAAI,MAAM,0BAA0B;GACxD,MAAM,UAAU,IAAI,WAAW;IAAE,KAAK;IAAQ,KAAK;IAAS;IAAK,kBAAkB,EAAE,IAAI,UAAU;IAAE,CAAC;GACtG,MAAM,YAAY,MAAM,KAAK,KAAK,YAAY,cAAqB,KAAK;AACxE,UAAO,MAAM,qCAAqC,EAAE,OAAO,CAAC,CAAC,WAAW,CAAC;AACzE,OAAI,CAAC,UAAW,OAAM,IAAI,MAAM,uBAAuB;AACvD,OAAI,CAAC,UAAU,SAAU,OAAM,IAAI,MAAM,gCAAgC;GACzE,MAAM,MAAM,qBAAqB,KAAK,IAAI;AAO1C,UANgB,MAAM,KAAK,IAAI,iBAAiB,cAAqB;IACnE;IACA,OAAO,UAAU;IACjB,wBAAwB;KAAE,KAAK;KAAS,KAAK;KAAM,GAAI,MAAM,EAAE,KAAK,GAAG,EAAE;KAAG;IAC7E,CAAC;SAGG;GAEL,MAAM,EAAE,YAAY,IAAI,cAAc,MAAM,gBAAgB,QAAQ;AACpE,gBAAa;GACb,MAAM,MAAO,MAAM,UAAU,UAAU;AACtC,GAAC,IAAY,MAAM;AACnB,GAAC,IAAY,MAAM;GACpB,MAAM,MAAM,MAAM,uBAAuB,IAAI;AAC7C,YAAS;IAAE,GAAG;IAAQ;IAAK;IAAK;;EAGlC,MAAM,SAAS,IAAI,QAAQ,EAAE,IAAI,UAAU,CAAC,CACzC,mBAAmB,OAAO,CAC1B,aAAa;AAEhB,MAAI,OAAQ,QAAO,UAAU,OAAO;AACpC,MAAI,QAAS,QAAO,WAAW,QAAQ;AACvC,MAAI,OAAO,QAAQ,CAAC,OAAO,MAAM,IAAI,CAAE,QAAO,aAAa,IAAI;AAE/D,SAAO,OAAO,KAAK,WAAW;;CAGhC,MAAa,YAAY,cAA4B,KACrD;EACE,MAAM,SAAS,aAAa,OAAO;AACnC,MAAI;GACF,MAAM,EAAE,uBAAuB,WAAW,cAAc,MAAM,OAAO;GACrE,MAAM,SAAS,sBAAsB,IAAI;GACzC,IAAI,MAAM,OAAO,OAAO;GAGxB,IAAIC;AAGJ,OAAI,OAAO,OAAO,OAAO,OAAO,QAAQ,UAAU;AAChD,UAAM,iBAAiB,OAAO,IAAI;AAClC,cAAU,MAAM,UAAU,OAAO,KAAY,IAAI;;AAInD,OAAI,CAAC,WAAW,OAAO,OAAO,QAAQ,UAAU;IAC9C,MAAM,MAAM,OAAO;IACnB,MAAM,YAAY,aAAa,IAAI;AACnC,WAAO,MAAM,sCAAsC;KAAE;KAAK;KAAW,CAAC;AAGtE,QAAI,cAAc,aAAa,cAAc,WAAW;KACtD,MAAM,MAAM,IAAI,MAAM,IAAI,CAAC;KAC3B,MAAM,MAAM,KAAK,KAAK,aAAa,IAAI;AACvC,YAAO,MAAM,iDAAiD,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC;AAClF,SAAI,KAAK;AACP,YAAM,iBAAiB,IAAI;AAC3B,gBAAU,MAAM,UAAU,KAAK,IAAI;;;AAKvC,QAAI,CAAC,QACH,KAAI;KACF,MAAMC,YAAiB,MAAM,KAAK,KAAK,iBAAiB,cAAc,IAAI;AAC1E,YAAO,MAAM,kDAAkD,EAAE,OAAO,CAAC,CAAC,WAAW,CAAC;AACtF,SAAI,WAAW;AACb,YAAM,iBAAiB,UAAU;AACjC,gBAAU,MAAM,UAAU,WAAW,IAAI;;YAErC;;AAKZ,OAAI,CAAC,WAAW,OAAO,OAAO,QAAQ,UAAU;IAC9C,MAAM,aAAa,qBAAqB,OAAO,IAAI;IAEnD,MAAM,eADa,OAAe,OACA,cAAc;AAChD,WAAO,MAAM,wCAAwC,EAAE,cAAc,CAAC;AAEtE,QAAI,aACF,KAAI;KACF,MAAM,MAAM,MAAM,MAAM,aAAa;AACrC,SAAI,IAAI,IAAI;MACV,MAAM,OAAQ,MAAM,IAAI,MAAM;MAC9B,MAAM,MAAM,OAAO;MACnB,MAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,MAAM,MAAM,EAAE,QAAQ,IAAI,GAAG;AACjF,aAAO,MAAM,gDAAgD,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC;AAClF,UAAI,OAAO;AACT,aAAM,iBAAiB,MAAM;AAC7B,iBAAU,MAAM,UAAU,OAAc,IAAI;;;YAG1C;;AAIZ,OAAI,CAAC,QAAS,QAAO;IAAE,UAAU;IAAO,OAAO;IAAuB;GAItE,MAAM,EAAE,YAAY,MAAM,UAAU,KAAK,SAAS;IAAE,YADhC;KAAC;KAAS;KAAS;KAAQ;IAC8B,gBAAgB;IAAI,CAAC;AAClG,UAAO;IAAE,UAAU;IAAM;IAAS;WAC3BC,GAAQ;GACf,MAAM,MAAM,GAAG,UAAU,OAAO,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,OAAO,EAAE;AACpF,UAAO,KAAK,qCAAqC,EAAE,OAAO,KAAK,CAAC;AAChE,UAAO;IAAE,UAAU;IAAO,OAAO;IAAK;;;;;CA7I3C,YAAY;oBAES,OAAO,WAAW;oBAAqC,OAAO,WAAW"}