@attestto/login
v0.1.1
Published
One-line DID login for any website. Web Component with OAuth bridge — Google, Microsoft, or any DID wallet. No framework required.
Maintainers
Readme
@attestto/login
One-line DID login for any website. Drop a Web Component, get passwordless authentication backed by cryptographic identity. Falls back to Google/Microsoft OAuth and converts tokens into portable Verifiable Credentials.
@attestto/login gives your users decentralized identity login with zero friction. No passwords, no OAuth vendor lock-in, no monthly subscription. One HTML element, works in any page. Part of the Attestto identity infrastructure.
Documentation · Playground · Full API
Quick start
Option 1: Copy-paste (any website, WordPress, Joomla, static HTML)
<script type="module" src="https://unpkg.com/@attestto/login"></script>
<attestto-login></attestto-login>That's it. Users see a "Login with DID" button. If they have a DID wallet extension installed, they authenticate with cryptographic proof. No passwords.
Option 2: npm (React, Vue, Svelte, Angular)
npm install @attestto/loginimport '@attestto/login'
// Now use <attestto-login> in your templatesWith OAuth fallback
Users without a DID wallet can still log in. Their OAuth token gets converted into a Verifiable Credential — next time, they use the credential directly.
<attestto-login
providers="google,microsoft"
issuer-endpoint="/api/auth/issue-vc"
trusted-issuers="did:web:yoursite.com"
></attestto-login>First login uses Google. Every login after uses their DID wallet. Google is cut out of the loop.
How it works
User clicks "Login with DID"
→ pickWallet() shows wallet selector
→ Wallet presents Verifiable Credential (CHAPI)
→ verifyPresentation() validates the trust chain
→ login-success event fires with user's DID
User clicks "Continue with Google" (fallback)
→ OAuth redirect to your backend
→ Backend verifies Google token
→ Backend issues a Verifiable Credential
→ VC stored in user's wallet
→ Next login uses DID path directlyAttributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| providers | string | "" | Comma-separated OAuth providers: "google,microsoft" |
| issuer-endpoint | string | "" | Backend URL for OAuth → VC exchange |
| trusted-issuers | string | "" | Comma-separated trusted issuer DIDs |
| resolver-url | string | "" | DID resolver URL for VP verification |
| did-label | string | "Login with DID" | Primary button label |
| theme | "light" | "dark" | "light" | Color scheme |
| compact | boolean | false | Button-only mode (no card wrapper) |
Events
All events are composed — they cross Shadow DOM boundaries.
| Event | Detail | When |
|---|---|---|
| login-success | LoginResult | Authentication succeeded |
| login-error | { error, method } | Authentication failed or cancelled |
| oauth-start | { provider } | User clicked an OAuth button |
| wallets-discovered | { count, wallets } | Wallet discovery completed |
const login = document.querySelector('attestto-login')
login.addEventListener('login-success', (e) => {
console.log('Authenticated:', e.detail.did)
console.log('Method:', e.detail.method) // 'did' or 'google'
// Create your session
fetch('/api/session', {
method: 'POST',
body: JSON.stringify({ did: e.detail.did })
})
})CSS theming
Override CSS custom properties to match your brand:
attestto-login {
--attestto-primary: #594FD3;
--attestto-primary-hover: #7B72ED;
--attestto-text: #1a1a2e;
--attestto-bg: #ffffff;
--attestto-bg-card: #f8fafc;
--attestto-border: #e2e8f0;
--attestto-font: system-ui, sans-serif;
}Style internal elements via CSS parts:
attestto-login::part(did-button) { border-radius: 20px; }
attestto-login::part(oauth-button) { font-size: 0.85rem; }
attestto-login::part(divider) { margin: 1.5rem 0; }Advanced: use the wallet adapter directly
@attestto/login re-exports the full @attestto/id-wallet-adapter API for advanced use cases:
import {
discoverWallets,
pickWallet,
verifyPresentation,
requestSignature,
} from '@attestto/login'
// Build your own login UI with full control
const wallets = await discoverWallets()
const wallet = await pickWallet()
const result = await verifyPresentation(vp, wallet, { resolverUrl, trustedIssuers })Backend: OAuth → VC exchange
Your backend handles the OAuth callback and issues a Verifiable Credential. Example with Express:
app.get('/api/auth/issue-vc', async (req, res) => {
const { provider, redirect } = req.query
// 1. Run OAuth flow with the provider
const oauthResult = await runOAuth(provider)
// 2. Issue a VC proving the user authenticated
const vc = await issuer.issue({
type: 'AuthenticationCredential',
subjectDid: oauthResult.did || generateDid(),
claims: {
provider,
email: oauthResult.email,
verifiedAt: new Date().toISOString(),
},
})
// 3. Redirect back with the VC for wallet storage
res.redirect(`${redirect}#vc=${encodeCredential(vc)}`)
})Ecosystem
| Package | Role |
|---|---|
| @attestto/id-wallet-adapter | Wallet discovery protocol (re-exported) |
| @attestto-com/vc-sdk | Issue VCs server-side (OAuth bridge) |
| @attestto/verify | Document verification + signing |
Privacy
No tracking, no analytics, no telemetry. The component runs entirely client-side. OAuth tokens are exchanged via your own backend — Attestto never sees them. DID login never touches any server. See the Attestto privacy model.
License
Apache 2.0 — see LICENSE
One line of HTML. Passwordless login. No vendor lock-in.
