Quickstart · 10 minutes

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

Terminal
 npm install @attestto/id-wallet-adapter 

Step 1 — Discover wallets

Find credential wallet extensions installed in the user's browser:

TypeScript
 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:

TypeScript
 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) 
💡
Three tiers of control: Tier 1 — call 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:

TypeScript
 // 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:

TypeScript
 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:

login.html
 <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:

1

Wallet trust — optional allowlist check

2

Extract holder DID from the Verifiable Presentation

3

Resolve DID Document — fetch the public key

4

Verify VP signature — cryptographic proof

5

Check VC issuers — all credentials from trusted issuers

6

Revocation check — Bitstring Status List query

Next steps