Add DID Login
Replace passwords with cryptographic identity. Users prove who they are with a DID wallet — no passwords, no OAuth, no third-party tokens.
Install
npm install @attestto/id-wallet-adapter Step 1 — Discover wallets
Find credential wallet extensions installed in the user's browser:
import { discoverWallets } from '@attestto/id-wallet-adapter' const wallets = await discoverWallets() console.log('Found', wallets.length, 'wallets')
Step 2 — Show the wallet picker
Or skip discovery and use the built-in modal — it handles everything:
import { pickWallet } from '@attestto/id-wallet-adapter' // Shows a modal with all detected wallets const wallet = await pickWallet() if (!wallet) { console.log('User cancelled') return } console.log('Selected:', wallet.name, wallet.did)
discoverWallets() and build your own UI.
Tier 2 — call pickWallet() for the built-in modal.
Tier 3 — pass a custom render function to pickWallet().
Step 3 — Request a Verifiable Presentation
Ask the user to present credentials via the CHAPI protocol:
// Request identity proof via W3C CHAPI const credential = await navigator.credentials.get({ web: { VerifiablePresentation: { query: { type: 'DIDAuthentication' }, challenge: crypto.randomUUID(), domain: window.location.origin, }, }, })
Step 4 — Verify the presentation
Cryptographically validate the trust chain — DID resolution, signature check, issuer trust, revocation:
import { verifyPresentation } from '@attestto/id-wallet-adapter' const result = await verifyPresentation(credential, wallet, { resolverUrl: 'https://your-backend.com/api/resolver', trustedIssuers: ['did:web:attestto.com'], }) if (result.valid) { console.log('Authenticated:', result.holderDid) // Create session, set cookie, etc. } else { console.error('Failed:', result.errors) }
Complete example
Here's the full login flow in one file:
<button id="login">Login with DID</button> <div id="status"></div> <script type="module"> import { pickWallet, verifyPresentation } from 'https://unpkg.com/@attestto/id-wallet-adapter' document.getElementById('login').addEventListener('click', async () => { const wallet = await pickWallet() if (!wallet) return const vp = await navigator.credentials.get({ web: { VerifiablePresentation: { query: { type: 'DIDAuthentication' }, challenge: crypto.randomUUID(), domain: location.origin } } }) const result = await verifyPresentation(vp, wallet, { resolverUrl: '/api/resolver', trustedIssuers: ['did:web:attestto.com'] }) document.getElementById('status').textContent = result.valid ? 'Authenticated: ' + result.holderDid : 'Failed: ' + result.errors.map(e => e.code).join(', ') }) </script>
Verification checks
The verifyPresentation() function runs a 6-step trust chain:
Wallet trust — optional allowlist check
Extract holder DID from the Verifiable Presentation
Resolve DID Document — fetch the public key
Verify VP signature — cryptographic proof
Check VC issuers — all credentials from trusted issuers
Revocation check — Bitstring Status List query
Next steps
- Customize the picker — themes, custom renderers, protocol filtering
- Request document signatures — sign PDFs with DID wallets
- Full JS integration guide — React, Vue, vanilla JS patterns
- Form validation — add credential checks to existing forms