{"_id":"@attestto/identity-bridge","name":"@attestto/identity-bridge","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.1":{"name":"@attestto/identity-bridge","version":"0.1.1","description":"Identity middleware for credential wallet discovery, VP exchange, and cryptographic verification — the identity layer crypto wallets are missing","type":"module","main":"dist/index.cjs","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts --clean","lint":"tsc --noEmit","prepublishOnly":"npm run build"},"keywords":["did","credential","wallet","discovery","chapi","verifiable-credentials","verifiable-presentation","w3c","ssi","identity","didcomm"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/Attestto-com/identity-bridge.git"},"author":{"name":"Eduardo Chongkan"},"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0"},"gitHead":"50080e76414a5e2686d6bd108865596cea155613","_id":"@attestto/identity-bridge@0.1.1","bugs":{"url":"https://github.com/Attestto-com/identity-bridge/issues"},"homepage":"https://github.com/Attestto-com/identity-bridge#readme","_nodeVersion":"25.6.1","_npmVersion":"11.9.0","dist":{"integrity":"sha512-ZZD5bEQ2hLQITsc3fYpmOFuE1L/lbspRdyF2o5/3rjDyP1HSmkyq4BxuLZgu1ofXEprj6kvGUvt6N0nyIH4jZA==","shasum":"8313c8cbe4f35ed39743f6fc453c8cf89224c986","tarball":"https://registry.npmjs.org/@attestto/identity-bridge/-/identity-bridge-0.1.1.tgz","fileCount":6,"unpackedSize":37466,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEjaolnKE2xRIR5GJlpzalefSCYTmPEmeVC60Bw1IX91AiEAz6wRqyZAkU2aEQ7V8Hgpnq/q0UUXOlhft5YldtqKvOY="}]},"_npmUser":{"name":"chongkan","email":"e.chongkan@gmail.com"},"directories":{},"maintainers":[{"name":"chongkan","email":"e.chongkan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/identity-bridge_0.1.1_1773927710548_0.4714219518060043"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-19T13:41:50.451Z","0.1.1":"2026-03-19T13:41:50.710Z","modified":"2026-03-19T13:41:50.965Z"},"maintainers":[{"name":"chongkan","email":"e.chongkan@gmail.com"}],"description":"Identity middleware for credential wallet discovery, VP exchange, and cryptographic verification — the identity layer crypto wallets are missing","homepage":"https://github.com/Attestto-com/identity-bridge#readme","keywords":["did","credential","wallet","discovery","chapi","verifiable-credentials","verifiable-presentation","w3c","ssi","identity","didcomm"],"repository":{"type":"git","url":"git+https://github.com/Attestto-com/identity-bridge.git"},"author":{"name":"Eduardo Chongkan"},"bugs":{"url":"https://github.com/Attestto-com/identity-bridge/issues"},"license":"MIT","readme":"# identity-bridge\n\nUniversal discovery protocol for credential wallet browser extensions — like [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) but for W3C identity wallets.\n\nSites broadcast a discovery event, installed wallet extensions announce themselves with their DID identity and metadata. Multiple wallets can coexist — the user always chooses.\n\n```mermaid\nsequenceDiagram\n    participant Site as Website\n    participant A as Extension A<br>(Attestto Creds)\n    participant B as Extension B<br>(Credible)\n\n    Site->>Site: dispatchEvent('credential-wallet:discover')\n    A-->>Site: announce → did:web:attestto.com:wallets:attestto-creds\n    B-->>Site: announce → did:web:credible.dev:wallets:credible\n    Note over Site: Collect announcements (800ms window)\n    Site->>Site: Show wallet picker → user chooses\n    Site->>A: navigator.credentials.get({ VerifiablePresentation })\n    A-->>Site: Returns signed VP\n    Site->>Site: Verify VP → resolve DID → check signature → issuer trust → revocation\n```\n\n## Identity Middleware — not a wallet connector\n\nWalletConnect, Dynamic, and Wagmi are **crypto wallet connectors**. They connect MetaMask, Phantom, and Ledger to dApps for **transaction signing** — send ETH, swap tokens, call contracts. They prove \"this person controls this private key.\" That's where they stop.\n\nThis package is the **credential exchange layer that comes after**. It connects **credential wallets** — Attestto Creds, Credible, Trinsic — to sites for **Verifiable Presentations**. It proves \"a trusted issuer attested this about you\" with field-level selective disclosure and cryptographic verification.\n\n### Why this matters\n\nA crypto wallet connector tells you someone owns address `0xabc...`. It cannot tell you that person passed KYC, holds a vLEI credential from GLEIF, or has a verified institutional identity. For any regulated use case — FATF Travel Rule, eIDAS 2.0, AML compliance — you need the identity layer, not just the key.\n\n<table>\n<tr>\n<td width=\"60\" align=\"center\"><strong>Step</strong></td>\n<td width=\"280\"><strong>Layer</strong></td>\n<td width=\"60\" align=\"center\"><strong>Role</strong></td>\n<td><strong>Output</strong></td>\n</tr>\n<tr>\n<td align=\"center\">1</td>\n<td>WalletConnect / Phantom</td>\n<td align=\"center\">🔌</td>\n<td>Address + signer</td>\n</tr>\n<tr>\n<td align=\"center\">2</td>\n<td><a href=\"https://github.com/Attestto-com/identity-resolver\">identity-resolver</a></td>\n<td align=\"center\">🔍</td>\n<td>DIDs, KYC status, vLEI, SBTs, domains</td>\n</tr>\n<tr>\n<td align=\"center\">3</td>\n<td><strong>identity-bridge</strong></td>\n<td align=\"center\">🛡️</td>\n<td>VP request + cryptographic verification</td>\n</tr>\n<tr>\n<td colspan=\"4\" align=\"center\"><em>Existing connectors handle step 1. Steps 2–3 are the identity middleware that crypto wallets are missing.</em></td>\n</tr>\n</table>\n\n### What you get vs. what exists\n\n<table>\n<tr>\n<th width=\"160\"></th>\n<th width=\"320\">WalletConnect / Dynamic / Wagmi</th>\n<th width=\"320\">identity-bridge</th>\n</tr>\n<tr>\n<td><strong>Connects</strong></td>\n<td>Crypto wallets (MetaMask, Phantom)</td>\n<td>Credential wallets (Attestto Creds, Credible)</td>\n</tr>\n<tr>\n<td><strong>Protocol</strong></td>\n<td>JSON-RPC (<code>eth_sign</code>, <code>sol_signTransaction</code>)</td>\n<td>W3C CHAPI (<code>VerifiablePresentation</code>)</td>\n</tr>\n<tr>\n<td><strong>What flows</strong></td>\n<td>Transactions, message signatures</td>\n<td>VCs, VPs, selective disclosure</td>\n</tr>\n<tr>\n<td><strong>Identity model</strong></td>\n<td>Address = identity</td>\n<td>DID = identity (method-agnostic)</td>\n</tr>\n<tr>\n<td><strong>Trust model</strong></td>\n<td>\"You hold the key\"</td>\n<td>\"A trusted issuer attested this about you\"</td>\n</tr>\n<tr>\n<td><strong>Discovery</strong></td>\n<td>EIP-6963 (Ethereum-specific)</td>\n<td><code>credential-wallet:discover</code> (chain-agnostic)</td>\n</tr>\n<tr>\n<td><strong>Compliance</strong></td>\n<td>None</td>\n<td>CHAPI + DIDComm v2 (eIDAS 2.0, FATF ready)</td>\n</tr>\n</table>\n\n### The full stack\n\n1. **WalletConnect** → connect Solana/Ethereum wallet → get address\n2. **[identity-resolver](https://github.com/Attestto-com/identity-resolver)** → resolve that address → find SNS domain, Attestto credentials, Civic pass, vLEI attestation\n3. **identity-bridge** → discover credential wallet extensions → request VP → verify cryptographically\n\n### How this relates to existing standards and tools\n\nSeveral projects touch parts of this problem. None cover the same surface.\n\n<table>\n<tr>\n<th width=\"200\">Project</th>\n<th width=\"280\">What it does</th>\n<th>What it doesn't do</th>\n</tr>\n<tr>\n<td><a href=\"https://w3c-fedid.github.io/digital-credentials/\">W3C Digital Credentials API</a><br><em>Chrome 141 + Safari 26</em></td>\n<td>Native <code>navigator.credentials.get({ digital })</code> — routes credential requests to the <strong>OS wallet</strong> (Apple Wallet, Google Wallet)</td>\n<td>Does not discover <strong>browser extension</strong> wallets. Only mediates between the page and the OS credential store.</td>\n</tr>\n<tr>\n<td><a href=\"https://chapi.io/\">W3C CHAPI polyfill</a><br><code>credential-handler-polyfill</code></td>\n<td>Polyfills <code>navigator.credentials</code> for VC exchange via a centralized mediator (<code>credential.mediator.org</code>)</td>\n<td>No direct extension-to-page discovery. Relies on a third-party mediator service. No VP verification.</td>\n</tr>\n<tr>\n<td><a href=\"https://github.com/walt-id/waltid-identity\">walt.id</a></td>\n<td>Full-stack identity platform — issuer, verifier, wallet services with OID4VP v1</td>\n<td>Enterprise platform, not a lightweight npm package. You adopt their full stack or nothing.</td>\n</tr>\n<tr>\n<td><a href=\"https://github.com/openwallet-foundation/credo-ts\">Credo-ts</a><br><em>(OpenWallet Foundation)</em></td>\n<td>DIDComm v2 + OID4VP framework for Node.js and React Native agents</td>\n<td>No browser extension discovery. Designed for server agents and mobile wallets.</td>\n</tr>\n<tr>\n<td><a href=\"https://spruceid.com/products/sprucekit\">SpruceKit</a></td>\n<td>Sign-In with Ethereum (SIWE) + credential issuance + off-chain data vaults</td>\n<td>Ethereum-first. Authentication-centric, not a general credential wallet discovery protocol.</td>\n</tr>\n</table>\n\n**Where identity-bridge fits:** The W3C Digital Credentials API routes to OS-level wallets. identity-bridge discovers **browser extension** wallets — the same gap [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) filled for Ethereum wallets when `window.ethereum` only supported one provider at a time. As DC-API matures for OS wallets and CHAPI standardizes the browser API, identity-bridge provides the missing extension discovery layer with built-in VP verification that neither standard includes.\n\n## Install\n\n```bash\nnpm install identity-bridge\n```\n\n## Quick Start\n\n### Site-side (your web app)\n\n```ts\nimport { discoverWallets, verifyPresentation } from 'identity-bridge'\n\n// 1. Discover installed credential wallets\nconst wallets = await discoverWallets()\n\nif (wallets.length === 0) {\n  // No wallet found — show install prompts\n} else if (wallets.length === 1) {\n  // One wallet — auto-select\n  console.log('Using', wallets[0].name, wallets[0].did)\n} else {\n  // Multiple wallets — show picker\n  wallets.forEach(w => console.log(w.name, w.did, w.protocols))\n}\n\n// 2. After user picks a wallet, request a credential via standard CHAPI\nconst credential = await navigator.credentials.get({\n  web: {\n    VerifiablePresentation: {\n      query: { type: 'DIDAuthentication' },\n      challenge: crypto.randomUUID(),\n      domain: window.location.origin,\n    },\n  },\n})\n\n// 3. Verify the returned VP cryptographically\nconst result = await verifyPresentation(credential, wallets[0], {\n  resolverUrl: 'https://your-backend.com/api/resolver',\n  trustedIssuers: ['did:web:attestto.com'],\n})\n\nif (result.valid) {\n  console.log('Verified holder:', result.holderDid)\n  console.log('DID Document:', result.didDocument)\n} else {\n  console.error('Verification failed:', result.errors)\n}\n```\n\n### Wallet-side (your browser extension)\n\n```ts\nimport { registerWallet } from 'identity-bridge'\n\n// Call once in your content script (MAIN world)\nregisterWallet({\n  did: 'did:web:yourorg.com:wallets:your-wallet',\n  name: 'Your Wallet',\n  icon: 'https://yourorg.com/icon-64.svg',\n  version: '1.0.0',\n  protocols: ['chapi', 'didcomm-v2'],\n  maintainer: {\n    name: 'Your Org',\n    did: 'did:web:yourorg.com',\n    url: 'https://yourorg.com',\n  },\n})\n```\n\n## API\n\n### `discoverWallets(timeoutMs?: number): Promise<WalletAnnouncement[]>`\n\nDiscover all credential wallet extensions. Dispatches a discover event and collects announcements within the timeout window (default 800ms).\n\n### `registerWallet(wallet: WalletAnnouncement): void`\n\nRegister your wallet extension to respond to discovery events. Call once in your content script's MAIN world.\n\n### `verifyPresentation(vp, wallet, options): Promise<VerifyResult>`\n\nVerify a Verifiable Presentation returned by a credential wallet. Performs the full trust chain:\n\n1. **Wallet trust check** — is this wallet in your trusted wallets list?\n2. **Holder extraction** — extract the holder DID from the VP\n3. **DID resolution** — resolve the holder's DID Document from your resolver\n4. **Signature verification** — verify the VP signature against the DID Document\n5. **Issuer trust check** — are all VC issuers in your trusted issuers list?\n6. **Revocation check** — query each VC's Bitstring Status List for revocation\n\n```ts\nconst result = await verifyPresentation(vp, wallet, {\n  resolverUrl: 'https://your-backend.com/api/resolver',  // DID resolver endpoint (required)\n  trustedIssuers: ['did:web:attestto.com'],               // Trusted VC issuers (required)\n  trustedWallets: ['did:web:attestto.com:wallets:attestto-creds'], // Optional wallet allowlist\n  checkRevocation: true,                                   // Check Bitstring Status List (default true)\n  signal: abortController.signal,                          // Optional AbortSignal\n})\n```\n\n**Returns:**\n\n```ts\ninterface VerifyResult {\n  valid: boolean                         // true if zero errors\n  holderDid: string | null               // The holder's DID extracted from the VP\n  errors: VerifyError[]                  // All verification failures\n  didDocument: Record<string, unknown> | null  // Resolved DID Document\n}\n\ninterface VerifyError {\n  code: VerifyErrorCode                  // Machine-readable error code\n  message: string                        // Human-readable description\n}\n\ntype VerifyErrorCode =\n  | 'NO_HOLDER'           // VP has no holder DID\n  | 'RESOLUTION_FAILED'   // Could not resolve the holder's DID\n  | 'SIGNATURE_INVALID'   // VP signature verification failed\n  | 'ISSUER_UNTRUSTED'    // VC issuer not in trustedIssuers list\n  | 'CREDENTIAL_REVOKED'  // VC revoked via Bitstring Status List\n  | 'WALLET_UNTRUSTED'    // Wallet DID not in trustedWallets list\n```\n\n### `WalletAnnouncement`\n\n```ts\ninterface WalletAnnouncement {\n  did: string              // Wallet's own DID\n  name: string             // Human-readable name\n  icon: string             // Icon URL (SVG or PNG, 64x64)\n  version: string          // Semantic version\n  protocols: WalletProtocol[]  // Supported protocols\n  maintainer: WalletMaintainer\n  url?: string             // Homepage / docs\n}\n\ntype WalletProtocol = 'chapi' | 'didcomm-v2' | 'oid4vp' | 'waci-didcomm'\n\ninterface WalletMaintainer {\n  name: string\n  did?: string\n  url?: string\n}\n```\n\n### Event Constants\n\n```ts\nimport { DISCOVER_EVENT, ANNOUNCE_EVENT } from 'identity-bridge'\n// 'credential-wallet:discover'\n// 'credential-wallet:announce'\n```\n\n## Writing a Custom Wallet Integration\n\nFollow these steps to make your browser extension discoverable via this protocol.\n\n### Step 1 — Install and register\n\nAdd the package to your extension and call `registerWallet()` in a content script that runs in the page's **MAIN** world (not the isolated extension world).\n\n```ts\n// content-script.ts (MAIN world)\nimport { registerWallet } from 'identity-bridge'\n\nregisterWallet({\n  did: 'did:web:yourorg.com:wallets:your-wallet',\n  name: 'Your Wallet',\n  icon: 'https://yourorg.com/icon-64.svg',\n  version: '1.0.0',\n  protocols: ['chapi'],\n  maintainer: { name: 'Your Org', did: 'did:web:yourorg.com' },\n})\n```\n\nYour `did` field must be a real, resolvable DID. The protocol eats its own dog food — wallets identify themselves the same way users do.\n\n### Step 2 — Handle CHAPI requests\n\nOverride `navigator.credentials.get()` in the MAIN world to intercept credential requests:\n\n```ts\nconst originalGet = navigator.credentials.get.bind(navigator.credentials)\n\nnavigator.credentials.get = async function (options) {\n  // Check if this is a VP request\n  const vpRequest = (options as any)?.web?.VerifiablePresentation\n  if (!vpRequest) return originalGet(options)\n\n  // Show your consent UI to the user\n  const userConsented = await showConsentDialog(vpRequest)\n  if (!userConsented) throw new DOMException('User denied', 'NotAllowedError')\n\n  // Build and return the VP\n  return buildVerifiablePresentation(vpRequest)\n}\n```\n\n### Step 3 — Return a Verifiable Presentation\n\nThe VP you return must include:\n\n- **`holder`** — the user's DID (string or `{ id: 'did:...' }`)\n- **`verifiableCredential`** — array of VCs\n- **`proof`** — cryptographic signature over the VP\n\n```ts\nfunction buildVerifiablePresentation(request) {\n  return {\n    '@context': ['https://www.w3.org/2018/credentials/v1'],\n    type: ['VerifiablePresentation'],\n    holder: 'did:web:user.example.com',\n    verifiableCredential: [/* user's selected VCs */],\n    proof: {\n      type: 'Ed25519Signature2020',\n      created: new Date().toISOString(),\n      challenge: request.challenge,\n      domain: request.domain,\n      verificationMethod: 'did:web:user.example.com#key-1',\n      proofValue: '...',  // Sign with the user's private key\n    },\n  }\n}\n```\n\n### Step 4 — Verify your DID is resolvable\n\nThe site will verify your VP by resolving the holder's DID and checking the signature against the public key in the DID Document. Make sure:\n\n- The holder's DID resolves to a valid DID Document\n- The DID Document contains the public key referenced in `proof.verificationMethod`\n- The `proof.challenge` and `proof.domain` match what the site sent\n\n## Supported Protocols\n\n<table>\n<tr>\n<th width=\"160\">Protocol</th>\n<th>Description</th>\n</tr>\n<tr>\n<td><code>chapi</code></td>\n<td>W3C Credential Handler API — <code>navigator.credentials.get()</code></td>\n</tr>\n<tr>\n<td><code>didcomm-v2</code></td>\n<td>DIDComm v2 Present Proof 3.0</td>\n</tr>\n<tr>\n<td><code>oid4vp</code></td>\n<td>OpenID for Verifiable Presentations</td>\n</tr>\n<tr>\n<td><code>waci-didcomm</code></td>\n<td>Wallet And Credential Interaction via DIDComm</td>\n</tr>\n</table>\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for:\n\n- **Wallet discovery spoofing** — discovery is untrusted metadata; trust is established via VP verification\n- **Trusted wallet allowlist** — restrict which wallet DIDs your app accepts\n- **Cross-origin considerations** — CORS for DID resolution and revocation checks\n- **API key exposure** — always use a backend proxy for resolver calls\n- **Trust chain** — the DID method spec defines where to resolve, not the VC\n\n## See It In Action\n\nThe [DID Landscape Explorer](https://github.com/chongkan/did-landscape-explorer) uses this package in its self-assessment wizard. The Identity step discovers installed wallets and lets users present their DID.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-6a00ce9514f7b4d9153b7429566928e5"}