Form Validation

Add credential verification and signature checks to your existing form workflows. Verify identity, professional credentials, or document signatures before allowing a form submission.

Use Cases

  • KYC before submit — require a verified identity credential before accepting a registration form
  • Professional credential check — confirm the user holds a valid professional license (e.g., licensed lawyer, certified engineer)
  • Document signature validation — verify a signed document attached to the form
  • Age/residency verification — request selective disclosure of specific claims without revealing the full credential

Form Validation Hook

TypeScript
 import { pickWallet, verifyPresentation } from '@attestto/id-wallet-adapter'

async function validateWithCredential(form: HTMLFormElement) {
  // 1. Pick a wallet and request a presentation
  const wallet = await pickWallet({ render: 'modal' })
  const vp = await wallet.requestPresentation({
    challenge: crypto.randomUUID(),
    credentialType: 'ProfessionalLicense',
  })

  // 2. Attach VP to form data
  const vpField = document.createElement('input')
  vpField.type = 'hidden'
  vpField.name = 'verifiable_presentation'
  vpField.value = JSON.stringify(vp)
  form.appendChild(vpField)

  return true
}

// Attach to form submit
document.querySelector('form').addEventListener('submit', async (e) => {
  e.preventDefault()
  const valid = await validateWithCredential(e.target)
  if (valid) e.target.submit()
}) 

Server-side Middleware

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

export async function verifyFormCredential(req, res, next) {
  const vpJson = req.body.verifiable_presentation
  if (!vpJson) return res.status(400).json({ error: 'Credential required' })

  const vp = JSON.parse(vpJson)
  const result = await verifyPresentation(vp, {
    trustedIssuers: ['did:web:colegio-abogados.cr'],
    checkRevocation: true,
  })

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

  req.verifiedDid = result.holderDid
  next()
} 

Selective Disclosure Request

For age or residency verification, request only the claims you need. The wallet shows the user exactly which fields will be shared.

TypeScript
 const vp = await wallet.requestPresentation({
  challenge: crypto.randomUUID(),
  selectiveDisclosure: {
    credentialType: 'NationalID',
    requestedFields: ['ageOver18', 'countryOfResidence'],
  },
})

// Only ageOver18 and countryOfResidence are disclosed
// Full name, ID number, etc. remain private 

Error Handling

ErrorMeaningAction
CREDENTIAL_REVOKEDThe credential has been revoked by the issuerReject the form. Inform the user their credential is no longer valid.
ISSUER_UNTRUSTEDThe credential issuer is not in your trusted listReject or flag for manual review.
SIGNATURE_INVALIDThe presentation signature does not verifyReject. Possible tampering.

Embed Verify Widget

For a visual verification indicator inside your form, embed the <attestto-verify> Web Component. It shows a verification badge once the credential is confirmed.

HTML
 <form>
  <!-- Your form fields -->
  <attestto-verify
    credential-type="ProfessionalLicense"
    trusted-issuers="did:web:colegio-abogados.cr"
  ></attestto-verify>
  <button type="submit">Submit</button>
</form>