为什么我们构建了一个自托管的 Clerk 替代方案(并开源了它)
Source: Dev.to
为什么我们构建了一个自托管的 Clerk 替代方案并将其开源
背景
在过去的几年里,Clerk 已经成为我们在多个项目中实现用户身份验证和授权的首选 SaaS 解决方案。它提供了开箱即用的 UI 组件、强大的会话管理以及对多因素认证(MFA)的原生支持。尽管如此,随着我们的业务规模扩大,以下几个痛点开始显现:
- 成本:随着活跃用户数的增长,Clerk 的订阅费用呈指数级上升。对预算有限的初创公司和个人项目来说,这是一笔不小的开支。
- 数据主权:我们希望对用户数据拥有完全的控制权,尤其是在处理敏感信息(如健康记录或金融数据)时。将这些数据托管在第三方平台会带来合规风险。
- 自定义需求:虽然 Clerk 提供了丰富的配置选项,但某些业务场景仍需要深度定制,例如特定的登录流程、品牌化的 UI 以及与内部系统的同步。
- 供应商锁定:迁移到其他身份提供商(IdP)往往需要大量的重构工作,这限制了我们在技术选型上的灵活性。
基于上述原因,我们决定自行实现一个轻量级、可自托管的身份验证系统,并在 GitHub 上开源,以帮助社区中的其他开发者解决类似的问题。
项目概览
- 名称:
authless(暂定) - 语言/框架:Node.js(TypeScript) + Prisma + Fastify
- 数据库:PostgreSQL(支持 SQLite 本地开发)
- 主要特性
- 注册、登录、密码重置
- 邮箱验证(基于 JWT 的一次性链接)
- 多因素认证(TOTP)
- 会话管理(基于 HttpOnly Cookie)
- 可插拔的 OAuth2 提供商(Google、GitHub 等)
- 完全可自定义的 UI(React 组件库 + Tailwind CSS)
注意:项目仍在早期阶段,当前仅实现了核心的身份验证功能,后续计划逐步加入用户角色、权限系统以及审计日志等企业级特性。
为什么选择这些技术?
| 技术 | 选型理由 |
|---|---|
| TypeScript | 静态类型帮助我们在大型代码库中保持可维护性,且社区生态成熟。 |
| Fastify | 轻量且性能出色,适合作为微服务的入口。 |
| Prisma | 类型安全的 ORM,能够快速生成迁移脚本并支持多种数据库。 |
| Tailwind CSS | 原子化的样式系统让 UI 定制更加灵活,且体积小。 |
| React | 与我们现有前端项目保持一致,便于复用组件。 |
快速上手
下面展示了如何在本地运行 authless:
# 克隆仓库
git clone https://github.com/alanwest/authless.git
cd authless
# 安装依赖
npm install
# 设置环境变量(.env 示例)
cp .env.example .env
# 编辑 .env,填入 DATABASE_URL、SMTP 配置等
# 运行数据库迁移
npx prisma migrate dev --name init
# 启动开发服务器
npm run dev
提示:如果你没有本地的 PostgreSQL 实例,可以使用 Docker 快速启动一个:
docker run --name authless-db -e POSTGRES_PASSWORD=secret -p 5432:5432 -d postgres
启动后,访问 http://localhost:3000 即可看到默认的登录/注册页面。
与 Clerk 的对比
| 功能 | Clerk | authless (当前实现) |
|---|---|---|
| 注册/登录 | ✅ | ✅ |
| 邮箱验证 | ✅ | ✅ |
| 多因素认证 (TOTP) | ✅ | ✅ |
| 社交登录 (OAuth) | ✅ | ✅(可扩展) |
| 会话持久化 | ✅ | ✅(HttpOnly Cookie) |
| UI 组件库 | ✅(React) | ✅(React + Tailwind) |
| 自定义登录流程 | ✅(受限) | ✅(完全开放) |
| 数据所有权 | SaaS(第三方) | 完全自托管 |
| 价格 | 按 MAU 计费 | 免费(自行托管) |
可以看到,在核心身份验证功能上两者已经相当接近。唯一的差距在于 高级企业功能(如 SSO、细粒度权限、审计日志)仍在路线上。
贡献指南
我们非常欢迎社区的参与!如果你想贡献代码、改进文档或报告 bug,请遵循以下步骤:
- Fork 本仓库并创建一个新的分支
- 编写 单元测试(使用 Jest)并确保
npm test全部通过 - 提交 Pull Request,在 PR 描述中注明所解决的问题或新增的特性
- 我们的 CI 会自动运行 lint、测试以及类型检查,所有检查通过后会进行代码审查
代码风格:遵循
eslint-config-prettier,保持 2 空格缩进。
未来计划
- 角色与权限系统:基于 RBAC 的细粒度访问控制
- 审计日志:记录关键操作,满足合规需求
- 多租户支持:为 SaaS 场景提供隔离的用户空间
- 更丰富的 UI 主题:支持暗黑模式、品牌配色等
- 官方 Docker 镜像:简化部署流程
如果你对上述任意功能感兴趣,欢迎在 Issues 区域提出讨论或直接提交实现。
结语
构建并开源 authless 的过程让我们重新审视了身份验证系统的本质:它不应该是一个只能消费的黑盒,而应该是可以被自由定制、部署并完全掌控的数据层。我们希望通过这个项目,帮助更多开发者摆脱供应商锁定,降低运营成本,同时提升对用户数据的安全感。
如果你正在寻找一个可自托管、开源且易于集成的身份验证解决方案,欢迎尝试 authless 并加入我们的社区!
本文由 Alan West 撰写,原文发布于 Dev.to。
Hosted Auth 的问题
1. 价格增长快于收入
| Users | Clerk | Auth0 | Authon |
|---|---|---|---|
| 10 000 | 免费 | ~ $700/月 | 免费 |
| 50 000 | ~ $825/月 | ~ $3 500/月 | 免费 |
| 100 000 | ~ $1 825/月 | 定制 | 免费 |
| 500 000 | 定制 | 定制 | 免费 |
Clerk 对超过 10 k 的 MAU 收取 $0.02;Auth0 Essentials 收取 $0.07/MAU。对自筹资金的初创公司来说,这笔费用很痛苦。认证费用应随计算成本而非用户数量增长。
2. 供应商锁定是真实存在的
导入 @clerk/nextjs 并调用 clerkClient.users.getUser() 会把代码绑定到 Clerk。切换提供商需要大幅重写。
Firebase Auth 更加紧耦合:Firestore 安全规则引用 request.auth,云函数使用 auth 触发器,用户 ID 也是 Firebase 专有的。迁移离开会变成一个多周的项目。
3. 数据主权
托管认证服务将凭证、会话和个人数据存储在第三方服务器上。对于受监管行业(医疗、金融)或有严格数据隐私法律的地区(欧盟),这通常是不可接受的。
4. 我们需要的缺失功能
| 功能 | 现有平台为何未能满足需求 |
|---|---|
| Web3 钱包认证 | 需要原生的 EVM 与 Solana 登录,而不是第三方插件 |
| ShadowDOM 隔离 UI | 需要一个登录模态框永不与宿主 CSS 冲突 |
| 15+ SDKs | 想要对 React、Vue、Svelte、Angular、React Native、Flutter、Python、Go、Swift、Kotlin 等提供一方支持 |
解决方案:Authon
Authon 是一个独立的认证平台,围绕四大原则构建:
- 自行托管,始终免费 – 在自己的服务器上运行;每位用户无需付费。
- 即插即用 SDK – 一行代码即可为任何框架添加认证。
- ShadowDOM 隔离 – 登录模态框被封装,消除 CSS 冲突。
- 原生 Web3 – 钱包认证是首要功能,而非事后考虑。
什么是 Authon
认证方式
- 邮箱 / 密码(注册、登录、密码重置)
- OAuth(Google、Apple、GitHub、Discord、Facebook、Microsoft、Kakao、Naver、LINE、X)
- 无密码(魔法链接、邮件 OTP)
- Passkeys(WebAuthn)— 完整生命周期:注册、认证、列出、重命名、撤销
- Web3 钱包 – EVM(MetaMask、WalletConnect、Coinbase Wallet、Trust Wallet)和 Solana(Phantom)
安全
- MFA(TOTP)— 兼容 Google Authenticator / Authy,提供备份码
- 会话管理 – 列出活跃会话,逐个撤销
- 审计日志 – 每个认证事件都会被记录
平台功能
- 组织 – 多租户支持,角色(所有者、管理员、成员)
- JWT 模板 – 自定义声明映射
- Webhooks – 10 种事件类型,带签名负载
- 品牌化 – 完全主题定制(颜色、徽标、自定义 CSS、语言)
- 管理后台 – 用户管理、分析、配置
6 种语言的 15 个 SDK
| 平台 | 软件包 |
|---|---|
| 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 |
我们不想只为 React 发布 SDK,却让其他人自行摸索。无论你使用 Django、FastAPI、Gin、Flutter 还是 SwiftUI——都有官方提供的 SDK。
工作原理
React 示例
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() 会渲染一个 ShadowDOM 模态框,包含您在仪表盘中配置的所有提供商。
Next.js 中间件
import { authMiddleware } from '@authon/nextjs';
export default authMiddleware({
publicRoutes: ['/', '/sign-in', '/pricing'],
});
export const config = {
matcher: ['/((?!_next|.*\\..*).*)'],
};
三行配置即可保护所有非公开路由。
Express 后端
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 });
});
中间件会验证 JWT,解码用户信息,并将其附加到 req.auth。
Web3 登录
const authon = new Authon('pk_live_...');
// 1️⃣ 获取钱包需要签名的 nonce
const { message } = await authon.web3GetNonce(address, 'evm', 'metamask');
// 2️⃣ 用户在 MetaMask 中签名消息
const signature = await ethereum.request({
method: 'personal_sign',
params: [message, address],
});
// 3️⃣ 验证签名并获取会话
const user = await authon.web3Verify(
message,
signature,
address,
'evm',
'metamask'
);
无需第三方插件,也不需要额外服务——仅使用原生钱包认证。
Authon 让您全面掌控认证成本、数据主权和功能集,同时在所有主流平台上保持集成简便。 🚀
Plex 集成
Web3 认证已内置于核心 SDK。
ShadowDOM:为何重要
大多数认证方案要么直接将 UI 注入到 DOM 中,要么使用 iframe。两者都有问题:
- 直接 DOM 注入 – 你的应用 CSS 可能会影响认证模态框。
* { box-sizing: border-box; }规则、全局字体覆盖或高 z-index 元素都可能导致登录表单出错。 - iframe – 能工作,但会产生用户体验摩擦(不同的滚动上下文、部分浏览器会阻止、主题定制更困难)。
Authon 使用 ShadowDOM。模态框渲染在一个完全与应用样式隔离的 shadow root 中。你的 CSS 无法泄漏进去,模态框的样式也不会泄漏出来。它始终如一地工作。
The Comparison
| Feature | Authon | Clerk | Auth0 | Auth.js | Firebase Auth | Supabase Auth |
|---|---|---|---|---|---|---|
| 自托管 | 是 | 否 | 否 | N/A (library) | 否 | 是 |
| 免费无限用户 | 是 | 否 (10K) | 否 (25K) | 是 | 部分 | 部分 |
| Web3 | 是 | 否 | 否 | 否 | 否 | 否 |
| Passkeys | 是 | 是 | 是 | 实验性 | 有限 | 否 |
| MFA | 是 | 是 | 是 | 否 | 付费 | 是 |
| 组织 | 是 | 是 | 是 (付费) | 否 | 否 | 否 |
| ShadowDOM UI | 是 | 否 | 否 | 否 | 否 | 否 |
| SDK 数量 | 15 | 8 | 20+ | 5 | 10+ | 6 |
| 预构建 UI | 是 | 是 | Universal Login | 否 | FirebaseUI | 基本 |
开源
Authon SDK 使用 MIT 许可证,可在 GitHub 上获取:
git clone https://github.com/mikusnuz/authon-sdk.git
cd authon-sdk
pnpm install
pnpm build
入门指南
尝试 Authon 的最快方式
npx create-authon-app my-app --framework nextjs
这将搭建一个已预配置 Authon 的 Next.js 应用——包括提供程序、中间件、登录页面和仪表盘。
或手动安装
npm install @authon/nextjs @authon/js
- 文档: (link pending)
- GitHub: (link pending)
- 网站: (link pending)
接下来
我们正在积极构建:
- 短信/电话认证
- SAML 和 OIDC 联合身份认证
- 匿名认证
- 限流和机器人检测
- 更多 OAuth 提供商
如果您厌倦了为每位用户的认证付费,或者需要在自托管的方案中使用 Web3/Passkeys/MFA,请尝试 Authon。我们期待您的反馈。