为什么我们构建了一个自托管的 Clerk 替代方案(并开源了它)

发布: (2026年3月20日 GMT+8 02:35)
16 分钟阅读
原文: Dev.to

Source: Dev.to

为什么我们构建了一个自托管的 Clerk 替代方案并将其开源

背景

在过去的几年里,Clerk 已经成为我们在多个项目中实现用户身份验证和授权的首选 SaaS 解决方案。它提供了开箱即用的 UI 组件、强大的会话管理以及对多因素认证(MFA)的原生支持。尽管如此,随着我们的业务规模扩大,以下几个痛点开始显现:

  1. 成本:随着活跃用户数的增长,Clerk 的订阅费用呈指数级上升。对预算有限的初创公司和个人项目来说,这是一笔不小的开支。
  2. 数据主权:我们希望对用户数据拥有完全的控制权,尤其是在处理敏感信息(如健康记录或金融数据)时。将这些数据托管在第三方平台会带来合规风险。
  3. 自定义需求:虽然 Clerk 提供了丰富的配置选项,但某些业务场景仍需要深度定制,例如特定的登录流程、品牌化的 UI 以及与内部系统的同步。
  4. 供应商锁定:迁移到其他身份提供商(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 的对比

功能Clerkauthless (当前实现)
注册/登录
邮箱验证
多因素认证 (TOTP)
社交登录 (OAuth)✅(可扩展)
会话持久化✅(HttpOnly Cookie)
UI 组件库✅(React)✅(React + Tailwind)
自定义登录流程✅(受限)✅(完全开放)
数据所有权SaaS(第三方)完全自托管
价格按 MAU 计费免费(自行托管)

可以看到,在核心身份验证功能上两者已经相当接近。唯一的差距在于 高级企业功能(如 SSO、细粒度权限、审计日志)仍在路线上。

贡献指南

我们非常欢迎社区的参与!如果你想贡献代码、改进文档或报告 bug,请遵循以下步骤:

  1. Fork 本仓库并创建一个新的分支
  2. 编写 单元测试(使用 Jest)并确保 npm test 全部通过
  3. 提交 Pull Request,在 PR 描述中注明所解决的问题或新增的特性
  4. 我们的 CI 会自动运行 lint、测试以及类型检查,所有检查通过后会进行代码审查

代码风格:遵循 eslint-config-prettier,保持 2 空格缩进。

未来计划

  • 角色与权限系统:基于 RBAC 的细粒度访问控制
  • 审计日志:记录关键操作,满足合规需求
  • 多租户支持:为 SaaS 场景提供隔离的用户空间
  • 更丰富的 UI 主题:支持暗黑模式、品牌配色等
  • 官方 Docker 镜像:简化部署流程

如果你对上述任意功能感兴趣,欢迎在 Issues 区域提出讨论或直接提交实现。

结语

构建并开源 authless 的过程让我们重新审视了身份验证系统的本质:它不应该是一个只能消费的黑盒,而应该是可以被自由定制、部署并完全掌控的数据层。我们希望通过这个项目,帮助更多开发者摆脱供应商锁定,降低运营成本,同时提升对用户数据的安全感。

如果你正在寻找一个可自托管、开源且易于集成的身份验证解决方案,欢迎尝试 authless 并加入我们的社区!


本文由 Alan West 撰写,原文发布于 Dev.to。

Hosted Auth 的问题

1. 价格增长快于收入

UsersClerkAuth0Authon
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
Pythonauthon (PyPI)
Goauthon-go
Dart / Flutterauthon (pub.dev)
Swift (iOS/macOS)Authon (SPM)
Kotlin (Android)authon-kotlin (Maven)
CLI scaffoldingcreate-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

FeatureAuthonClerkAuth0Auth.jsFirebase AuthSupabase Auth
自托管N/A (library)
免费无限用户否 (10K)否 (25K)部分部分
Web3
Passkeys实验性有限
MFA付费
组织是 (付费)
ShadowDOM UI
SDK 数量15820+510+6
预构建 UIUniversal LoginFirebaseUI基本

开源

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。我们期待您的反馈。

0 浏览
Back to Blog

相关文章

阅读更多 »