JavaScript Integration

Add DID-based authentication to any website. Choose your approach based on your stack.

⚠️

Backend verification is required. Client-side wallet discovery and presentation requests are convenient for UX, but the Verifiable Presentation must be verified on the server where trusted issuer lists and resolver endpoints cannot be tampered with.

Vanilla JavaScript

HTML
 <script type="module">
  import { pickWallet, verifyPresentation }
    from 'https://cdn.attestto.com/id-wallet-adapter/0.4.1/index.mjs'

  document.getElementById('did-login').addEventListener('click', async () => {
    const wallet = await pickWallet({ render: 'modal' })
    const vp = await wallet.requestPresentation({
      challenge: crypto.randomUUID(),
    })

    // Send VP to backend for verification
    const res = await fetch('/api/auth/did-login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ vp }),
    })

    const session = await res.json()
    if (session.authenticated) {
      window.location.href = '/dashboard'
    }
  })
</script>

<button id="did-login">Sign in with DID</button> 

React

DIDLogin.tsx
 import { pickWallet } from '@attestto/id-wallet-adapter'

export function DIDLogin() {
  const handleLogin = async () => {
    const wallet = await pickWallet({ render: 'modal' })
    const vp = await wallet.requestPresentation({
      challenge: crypto.randomUUID(),
    })

    const res = await fetch('/api/auth/did-login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ vp }),
    })

    const session = await res.json()
    if (session.authenticated) window.location.href = '/dashboard'
  }

  return (
    <button onClick={handleLogin}>Sign in with DID</button>
  )
} 

Vue

DIDLogin.vue
 <script setup lang="ts">
import { pickWallet } from '@attestto/id-wallet-adapter'

async function handleLogin() {
  const wallet = await pickWallet({ render: 'modal' })
  const vp = await wallet.requestPresentation({
    challenge: crypto.randomUUID(),
  })

  const res = await fetch('/api/auth/did-login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ vp }),
  })

  const session = await res.json()
  if (session.authenticated) window.location.href = '/dashboard'
}
</script>

<template>
  <button @click="handleLogin">Sign in with DID</button>
</template> 

Web Component

HTML
 <!-- Drop-in: no framework needed -->
<script type="module"
  src="https://cdn.attestto.com/id-wallet-adapter/0.4.1/did-login.mjs"
></script>

<attestto-did-login
  verify-endpoint="/api/auth/did-login"
  redirect="/dashboard"
></attestto-did-login> 

Backend: Express.js Middleware

middleware/did-auth.ts
 import { verifyPresentation } from '@attestto/id-wallet-adapter'

const TRUSTED_ISSUERS = [
  'did:web:gov.example.com',
  'did:sns:attestto.sol',
]

export async function didAuth(req, res) {
  const { vp } = req.body
  if (!vp) return res.status(400).json({ error: 'Missing VP' })

  const result = await verifyPresentation(vp, {
    trustedIssuers: TRUSTED_ISSUERS,
    checkRevocation: true,
  })

  if (!result.valid) {
    return res.status(401).json({ errors: result.errors })
  }

  // Create session with the verified holder DID
  req.session.did = result.holderDid
  res.json({ authenticated: true, did: result.holderDid })
} 

CDN Approach

For sites that cannot use npm, the adapter is available via CDN as a single ESM module. Add a <script type="module"> tag pointing to the CDN URL. No build step required.

HTML
 <script type="module" src="https://cdn.attestto.com/id-wallet-adapter/0.4.1/index.mjs"></script>