Passwordless auth, built on your domain
Stop sending passwords over email. key-chains.life is a drop-in magic-link + passkey auth layer that runs on your infrastructure. You own the keys. You own the data. Forever.
# 1 โ Add to your project
npm install @key-chains/sdk
# 2 โ Point it at your domain
export KEY_CHAINS_DOMAIN=auth.myapp.com
# 3 โ Send a magic link
await keyChains.sendLink({ email: "user@example.com" })
# 4 โ Verify on callback
const session = await keyChains.verify(token)
// โ { userId, email, expiresAt }
# That's it. No cloud vendor. No shared secret DB.The problem with passwords (and most auth SaaS)
Every major breach starts with stolen credentials. Auth-as-a-service fixes the UX but trades one dependency for another.
Passwords get leaked
62% of breaches involve stolen credentials. Even bcrypt can't save you from reuse.
Auth SaaS locks you in
Auth0, Clerk, Supabase Auth โ great DX, until you hit the pricing wall or sunset notice.
You don't own your user data
When auth lives in a third-party cloud, your user table is a tenant row โ not your asset.
How it works
Three moving parts. All yours.
Deploy the auth server
One Docker image or a serverless function on your infra. Stores signing keys and session tokens in your own database.
Embed the SDK
A tiny JS/TS SDK handles magic-link generation, passkey registration, and session validation in your existing app.
Users authenticate
Email โ click โ done. Or tap a passkey. No passwords created, no secrets stored. Sessions signed with your keys.
Documentation
Everything you need to go from zero to passwordless.
Quick Start
# Prerequisites: Node 18+, any SQL or Mongo DB
# 1. Install
npm install @key-chains/sdk @key-chains/server
# 2. Configure
export KC_DOMAIN=auth.yourdomain.com
export KC_DB_URL=postgres://user:pass@host/db
export KC_FROM_EMAIL=auth@yourdomain.com
# 3. Run the auth server
npx key-chains-server
# 4. Protect a route (Next.js example)
import { withKeyChains } from '@key-chains/sdk/next'
export default withKeyChains(async (req, res) => {
// req.session.userId is available here
res.json({ user: req.session.userId })
})API Reference
Send a magic link to an email address. Returns a token ID for polling.
Verify a magic-link token. Returns a signed session JWT.
Begin WebAuthn registration ceremony. Returns PublicKeyCredentialCreationOptions.
Complete WebAuthn authentication. Returns signed session JWT.
Revoke the current session (logout).
Configuration Reference
| Variable | Required | Description |
|---|---|---|
| KC_DOMAIN | Yes | Your auth subdomain (e.g. auth.myapp.com) |
| KC_DB_URL | Yes | Postgres or MongoDB connection string |
| KC_FROM_EMAIL | Yes | Sender address for magic links |
| KC_SMTP_URL | Yes | SMTP connection string (or set KC_SENDGRID_KEY) |
| KC_JWT_SECRET | No | Custom signing secret (auto-generated if unset) |
| KC_LINK_TTL | No | Magic link TTL in seconds (default: 900) |
| KC_SESSION_TTL | No | Session TTL in seconds (default: 2592000) |
| KC_PASSKEYS | No | Enable passkey/WebAuthn support (default: true) |
| KC_ALLOW_LIST | No | Comma-separated domains that may sign in |
Framework Guides
Get early access
We're onboarding projects one by one. Drop your email and we'll reach out when we're ready for you.
No spam. Just one email when we're ready.