A drop-in web component that verifies Auths decentralized identities — the open-source equivalent of GitHub's green "Verified" badge. Point it at any repository that uses Auths, and it cryptographically verifies the identity chain in the browser via WASM.
CDN (no build step):
Pin an exact version and add a Subresource Integrity (SRI) hash so the browser refuses a tampered bundle:
<script
type="module"
src="https://unpkg.com/@auths-dev/verify@0.4.0/dist/auths-verify.mjs"
integrity="sha384-C6a5GCsPgw/o9tmtnaUXZ0qJgY/q8LdNxvuP/10EwOK39ZdiAcYz6CP8lT6m1V5R"
crossorigin="anonymous"
></script>Pin a version with SRI — never
@latest. Theintegrityhash is byte-exact, so it only validates against an immutable, version-pinned URL (@0.4.0). A moving@latestURL changes bytes on every release and would break the hash — and the badge — without warning.crossorigin="anonymous"is required: without it the browser fetches the cross-origin script opaquely, can't read it to verify, and the load fails. The hash above is for@0.4.0; regenerate it for any other version withnpm run sri, or copy it from the file's page on jsDelivr, which displays the SRI hash for you. The WASM verifier is base64-inlined into this single.mjs, so oneintegrityhash covers the entire runtime — there is no separate.wasmfetch to protect.
jsDelivr serves the same published file, so the same pin + integrity works there too:
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@auths-dev/verify@0.4.0/dist/auths-verify.mjs"
integrity="sha384-C6a5GCsPgw/o9tmtnaUXZ0qJgY/q8LdNxvuP/10EwOK39ZdiAcYz6CP8lT6m1V5R"
crossorigin="anonymous"
></script>npm (for bundlers):
npm install @auths-dev/verifyimport '@auths-dev/verify';Add the widget to any page and point it at a repository:
<auths-verify repo="https://github.com/user/repo"></auths-verify>That's it. The widget will:
- Fetch the repository's latest GitHub Release and locate the
*.auths.jsonattestation asset - Read the verification key (Ed25519 or P-256) from the attestation
- Load the WASM verification engine
- Cryptographically verify the attestation chain
- Display a badge showing the result (Verified, Invalid, Expired, etc.)
Prerequisite: The repository owner must have published an Auths attestation as a *.auths.json asset on a GitHub Release (Gitea repos expose it via refs/auths/ instead). If the repo has no Auths attestation, the widget will show an error.
Supported forges: GitHub (via Release assets) and Gitea (via Git refs, including self-hosted). GitLab is not supported for auto-resolve because its API does not expose custom Git refs — use manual mode instead.
Build your embed in the browser: the Embed Builder lets you paste a repo URL, preview the live badge, and copy a version-pinned, SRI-protected snippet.
Compact inline pill showing verification status.
<auths-verify repo="https://github.com/user/repo"></auths-verify>Badge with an expandable panel showing the full attestation chain. Click the badge to expand.
<auths-verify repo="https://github.com/user/repo" mode="detail"></auths-verify>Badge with a hover tooltip summarizing verification status.
<auths-verify repo="https://github.com/user/repo" mode="tooltip"></auths-verify><auths-verify repo="..." size="sm"></auths-verify>
<auths-verify repo="..." size="md"></auths-verify> <!-- default -->
<auths-verify repo="..." size="lg"></auths-verify>| Attribute | Type | Default | Description |
|---|---|---|---|
repo |
URL string | — | Repository URL to verify (recommended) |
forge |
github | gitea | gitlab |
auto-detected | Override forge detection (useful for self-hosted Gitea) |
identity |
DID string | — | Filter to a specific identity when a repo has multiple |
mode |
badge | detail | tooltip |
badge |
Display mode |
size |
sm | md | lg |
md |
Badge size |
auto-verify |
boolean | true |
Verify automatically on page load |
wasm-url |
URL string | — | Override the WASM binary URL |
If you already have the attestation data (e.g., from a CI pipeline, from a GitLab repo, or for offline verification), you can supply it directly instead of using repo:
<auths-verify
attestation='{"version":1, ...}'
public-key="aabbccdd..."
></auths-verify>Or for a full chain:
<auths-verify
attestations='[{"version":1, ...}, {"version":1, ...}]'
public-key="aabbccdd..."
></auths-verify>| Attribute | Type | Description |
|---|---|---|
attestation |
JSON string | Single attestation to verify |
attestations |
JSON array string | Chain of attestations to verify |
public-key |
hex string | Root/issuer public key — Ed25519 (64 hex chars) or P-256 (66 hex chars, compressed) |
const el = document.querySelector('auths-verify');
// Trigger verification manually
await el.verify();
// Get the last verification report
const report = el.getReport();
// report.status.type → 'Valid' | 'InvalidSignature' | 'Expired' | 'Revoked' | 'BrokenChain'
// report.chain → [{ issuer, subject, valid, error? }, ...]
// report.warnings → string[]
// Listen for events
el.addEventListener('auths-verified', (e) => {
console.log('Status:', e.detail.status.type);
console.log('Chain:', e.detail.chain);
});
el.addEventListener('auths-error', (e) => {
console.error('Error:', e.detail.error);
});The <auths-verify> component needs a browser DOM. For everything else — Node,
Deno, Bun, SSR/RSC, edge functions, CI/supply-chain gates, and tests — import
the DOM-free @auths-dev/verify/core entry, which exposes the verifier
functions directly. It loads with no HTMLElement/customElements/document
references, so it works headless with no DOM shim.
It runs the exact same compiled WASM verifier the widget uses and returns the
same verdict — so a server pre-check, a CI gate, and the in-browser badge all
agree. (It does not apply the Rust CLI's additional supply-chain commit-trust
check; the verdict matches the widget's, not the CLI's.) The WASM is inlined, so
there is no separate .wasm to fetch and no init step for the caller.
// Node 20+ (ESM), Deno, Bun, edge, SSR — no DOM required.
import { verifyAttestation } from '@auths-dev/verify/core';
const { valid, error } = await verifyAttestation(attestationJson, issuerKeyHex);
if (valid) {
console.log('Verified ✓');
} else {
console.error('Rejected:', error);
}Strict (throwing) form — resolves on a Valid verdict, throws the status
otherwise:
import { verifyAttestationJson } from '@auths-dev/verify/core';
try {
await verifyAttestationJson(attestationJson, issuerKeyHex); // throws if not Valid
console.log('Verified ✓');
} catch (err) {
console.error('Rejected:', err.message);
}| Export | Signature | Returns |
|---|---|---|
verifyAttestation |
(attestationJson, issuerKeyHex) |
Promise<{ valid, error? }> — the widget's verdict |
verifyChain |
(attestations[], rootKeyHex) |
Promise<VerificationReport> |
verifyAttestationJson |
(attestationJson, issuerKeyHex) |
Promise<void> — resolves on Valid, throws otherwise |
verifyChainJson |
(attestationsJsonArray, rootKeyHex) |
Promise<string> — raw VerificationReport JSON |
verifySignature |
(fileHashHex, signatureHex, publicKeyHex, curve?) |
Promise<boolean> |
init / ensureInit |
() |
Promise<void> — optional; verify calls initialize on first use |
verifySignatureis an alias ofverifyArtifactSignature— it verifies a detached signature over a file/artifact hash, not an arbitrary message. Passcurveas"ed25519"(default) or"p256". The hash and signature are hex strings.
All colors are overridable via CSS custom properties:
auths-verify {
--auths-verified-bg: #eef2ff;
--auths-verified-fg: #3730a3;
--auths-verified-border: #a5b4fc;
--auths-font-family: 'Inter', sans-serif;
--auths-border-radius: 6px;
}Available properties: --auths-{state}-bg, --auths-{state}-fg, --auths-{state}-border for each state (verified, invalid, expired, revoked, error, loading, idle), plus --auths-font-family, --auths-font-size, --auths-border-radius, --auths-detail-border-radius.
When you set repo="https://github.com/user/repo":
- The widget parses the URL and detects the forge (GitHub, Gitea, or GitLab).
- It resolves the attestation data — the mechanism depends on the forge:
- GitHub: fetches the repository's latest Release (
/repos/{owner}/{repo}/releases/latest), finds the*.auths.jsonasset, and downloads it (via the Contents API, falling back to the Release asset API). - Gitea: reads the auths data from Git refs under
refs/auths/via the Gitea REST API. - GitLab: not supported for auto-resolve (its REST API does not expose the required data) — use manual mode.
- GitHub: fetches the repository's latest Release (
- It derives the verification key (Ed25519 or P-256): for GitHub, the device public key carried in the attestation; for Gitea, extracted from the controller's
did:key/CESR identifier (pure TypeScript, no WASM needed). - It loads the WASM verification engine and cryptographically verifies the attestation chain.
- It renders the result as a badge.
The resolver layer uses dynamic imports — if you only use manual attestation/public-key attributes, the resolver code is never loaded (zero bundle size impact).
- Node.js >= 18
- Rust 1.93+ with
wasm32-unknown-unknowntarget (for WASM builds) - wasm-pack
- The auths repo cloned alongside this one:
auths-base/ ├── auths/ # main auths repo └── auths-verify-widget/ # this repo
npm installnpm run build:wasm# Unit tests (no WASM required — mocked)
npm test
# Type check
npm run typechecknpm run devOpens the examples directory with hot reload via Vite.
npm run build:wasm
npm run buildOutputs:
dist/auths-verify.mjs— single self-contained file with the WASM base64-inlined. Recommended for CDN + SRI: one file, oneintegrityhash covers the whole runtime.dist/slim/auths-verify.mjs— the./slimexport. Note: it currently also inlines the WASM (viavite-plugin-wasm), so it is the same size as the full bundle and shares the same single-hash SRI story; there is no separate.wasmto fetch today.
Generate the SRI hash for the built bundle(s) with npm run sri.
MIT