Why We Built a Self-Hosted Clerk Alternative (and Open-Sourced It)
Source: Dev.to
The Problem with Hosted Auth
1. Pricing Scales Faster Than Revenue
| Users | Clerk | Auth0 | Authon |
|---|---|---|---|
| 10 000 | Free | ~ $700/mo | Free |
| 50 000 | ~ $825/mo | ~ $3 500/mo | Free |
| 100 000 | ~ $1 825/mo | Custom | Free |
| 500 000 | Custom | Custom | Free |
Clerk charges $0.02 per MAU beyond 10 k; Auth0 Essentials $0.07/MAU. For a bootstrapped startup, that cost is painful. Authentication should scale with compute cost, not user count.
2. Vendor Lock‑in Is Real
Importing @clerk/nextjs and calling clerkClient.users.getUser() ties your code to Clerk. Switching providers forces a massive rewrite.
Firebase Auth is even tighter: Firestore security rules reference request.auth, Cloud Functions use auth triggers, and user IDs are Firebase‑specific. Migrating away becomes a multi‑week project.
3. Data Sovereignty
Hosted auth services store credentials, sessions, and personal data on third‑party servers. For regulated industries (healthcare, finance) or regions with strict data‑privacy laws (EU), this is often unacceptable.
4. Missing Features We Needed
| Feature | Why Existing Platforms Fell Short |
|---|---|
| Web3 wallet authentication | Needed native EVM & Solana sign‑in, not a third‑party plugin |
| ShadowDOM‑isolated UI | Required a login modal that never conflicts with host CSS |
| 15+ SDKs | Wanted first‑party support for React, Vue, Svelte, Angular, React Native, Flutter, Python, Go, Swift, Kotlin, etc. |
The Solution: Authon
Authon is a standalone auth platform built around four principles:
- Self‑hosted, always free – Run it on your own server; pay nothing per user.
- Drop‑in SDKs – One line adds auth to any framework.
- ShadowDOM isolation – The login modal is encapsulated, eliminating CSS conflicts.
- Web3 native – Wallet auth is a first‑class citizen, not an afterthought.
What Authon Does
Authentication Methods
- Email / password (sign‑up, sign‑in, password reset)
- OAuth (Google, Apple, GitHub, Discord, Facebook, Microsoft, Kakao, Naver, LINE, X)
- Passwordless (magic link, email OTP)
- Passkeys (WebAuthn) – full lifecycle: register, authenticate, list, rename, revoke
- Web3 wallets – EVM (MetaMask, WalletConnect, Coinbase Wallet, Trust Wallet) and Solana (Phantom)
Security
- MFA (TOTP) – Google Authenticator / Authy compatible, with backup codes
- Session management – list active sessions, revoke individually
- Audit logs – every auth event is tracked
Platform Features
- Organizations – multi‑tenant support with roles (owner, admin, member)
- JWT templates – custom claim mapping
- Webhooks – 10 event types with signed payloads
- Branding – full theming (colors, logos, custom CSS, locale)
- Admin dashboard – user management, analytics, configuration
15 SDKs Across 6 Languages
| Platform | Package |
|---|---|
| Vanilla JS | @authon/js |
| React | @authon/react |
| Next.js | @authon/nextjs |
| Vue 3 | @authon/vue |
| Nuxt 3 | @authon/nuxt |
| Svelte | @authon/svelte |
| Angular | @authon/angular |
| React Native | @authon/react-native |
| Node.js | @authon/node |
| Python | authon (PyPI) |
| Go | authon-go |
| Dart / Flutter | authon (pub.dev) |
| Swift (iOS/macOS) | Authon (SPM) |
| Kotlin (Android) | authon-kotlin (Maven) |
| CLI scaffolding | create-authon-app |
We didn’t want to ship an SDK for React and leave everyone else to figure it out. Whether you’re building with Django, FastAPI, Gin, Flutter, or SwiftUI — there’s a first‑party SDK.
How It Works
React Example
import {
AuthonProvider,
SignedIn,
SignedOut,
UserButton,
useAuthon,
} from '@authon/react';
function App() {
return (
<AuthonProvider>
<SignedIn>
<UserButton />
</SignedIn>
<SignedOut>
<SignInButton />
</SignedOut>
</AuthonProvider>
);
}
function SignInButton() {
const { openSignIn } = useAuthon();
return <button onClick={openSignIn}>Sign in</button>;
}
openSignIn() renders a ShadowDOM modal with every provider you’ve configured in the dashboard.
Next.js Middleware
import { authMiddleware } from '@authon/nextjs';
export default authMiddleware({
publicRoutes: ['/', '/sign-in', '/pricing'],
});
export const config = {
matcher: ['/((?!_next|.*\\..*).*)'],
};
Three lines of config protect every non‑public route.
Express Backend
import { expressMiddleware } from '@authon/node';
app.use(
'/api',
expressMiddleware({
secretKey: process.env.AUTHON_SECRET_KEY!,
})
);
app.get('/api/profile', (req, res) => {
res.json({ user: req.auth });
});
The middleware verifies the JWT, decodes the user, and attaches it to req.auth.
Web3 Sign‑In
const authon = new Authon('pk_live_...');
// 1️⃣ Get a nonce for the wallet to sign
const { message } = await authon.web3GetNonce(address, 'evm', 'metamask');
// 2️⃣ User signs the message in MetaMask
const signature = await ethereum.request({
method: 'personal_sign',
params: [message, address],
});
// 3️⃣ Verify the signature and obtain a session
const user = await authon.web3Verify(
message,
signature,
address,
'evm',
'metamask'
);
No third‑party plugins, no extra services—just native wallet authentication.
Authon gives you full control over authentication costs, data sovereignty, and feature set while keeping integration simple across every major platform. 🚀
Plex Integrations
Web3 auth is built into the core SDK.
ShadowDOM: Why It Matters
Most auth solutions inject UI directly into the DOM or use iframes. Both have problems:
- Direct DOM injection – your app’s CSS can affect the auth modal. A
* { box-sizing: border-box; }rule, a global font override, or a high‑z‑index element can break the login form. - Iframes – work but create UX friction (different scroll contexts, blocked by some browsers, harder to theme).
Authon uses ShadowDOM. The modal is rendered inside a shadow root that is completely isolated from your app’s styles. Your CSS can’t leak in, and the modal’s styles can’t leak out. It just works, every time.
The Comparison
| Feature | Authon | Clerk | Auth0 | Auth.js | Firebase Auth | Supabase Auth |
|---|---|---|---|---|---|---|
| Self‑hosted | Yes | No | No | N/A (library) | No | Yes |
| Free unlimited users | Yes | No (10K) | No (25K) | Yes | Partial | Partial |
| Web3 | Yes | No | No | No | No | No |
| Passkeys | Yes | Yes | Yes | Experimental | Limited | No |
| MFA | Yes | Yes | Yes | No | Paid | Yes |
| Organizations | Yes | Yes | Yes (paid) | No | No | No |
| ShadowDOM UI | Yes | No | No | No | No | No |
| SDKs (count) | 15 | 8 | 20+ | 5 | 10+ | 6 |
| Pre‑built UI | Yes | Yes | Universal Login | No | FirebaseUI | Basic |
Open Source
Authon SDK is MIT‑licensed and available on GitHub:
git clone https://github.com/mikusnuz/authon-sdk.git
cd authon-sdk
pnpm install
pnpm build
Getting Started
The fastest way to try Authon
npx create-authon-app my-app --framework nextjs
This scaffolds a Next.js app with Authon pre‑configured – provider, middleware, sign‑in page, and dashboard.
Or install manually
npm install @authon/nextjs @authon/js
- Documentation: (link pending)
- GitHub: (link pending)
- Website: (link pending)
What’s Next
We’re actively building:
- SMS/phone authentication
- SAML and OIDC federation
- Anonymous auth
- Rate limiting and bot detection
- More OAuth providers
If you’re tired of paying per user for authentication, or you need Web3/Passkeys/MFA in a self‑hosted package, give Authon a try. We’d love your feedback.