@authlocker/better-auth

中文文档
0.0.2发布于 2026/09/02MITTypeScriptESM + CJS近 30 天下载 3348

AuthLocker 更好的 Auth 插件: 由 authlocker.dev 验证的电子邮件和短消息代码, 并为您创建会话 .

中文使用文档
文档目录18 节
  1. 它做什么,它故意没有
  2. 设置
  3. 1. 联合国. lib/auth.ts
  4. 2. 联合国. app/api/auth/[...all]/route.ts
  5. 3个. lib/auth-client.ts
  6. 4. 管理移徙
  7. 用这个
  8. 错误是 API
  9. 你发送的第一个短消息可能会失败
  10. 社会登录 : genericOAuth ,没有我们的插件
  11. 每个应用程序注册客户端一次,而不是每个功能一次
  12. 已经使用更好的 Auth emailOTP() ? 吗?
  13. 选项
  14. 限制费率
  15. 服务器端呼叫
  16. 什麽 verify-otp 返回时
  17. 没有记录的东西
  18. 许可证

@authlocker/ 更好 - auth

更好的认证插件 奥特洛克电子邮件和短信 由 AuthLocker 生成并检查的一次性代码, 并带有更好的 Auth 为您创建会话 .

pnpm add @authlocker/better-auth better-auth

要求数 better-auth 1.7.0或更新大量更好的认证材料 互联网上写着1.6或更早 下文在1.7中有所改变——它们被称作重要之处.

它做什么,它故意没有

三个端点,安装在您已有的更好的 Auth 处理器内:

终点客户端呼叫对
POST /api/auth/authlocker/send-otpauthClient.authlocker.sendOtp()请 AuthLocker 生成并发送代码
POST /api/auth/authlocker/verify-otpauthClient.authlocker.verifyOtp()检查、找到或创建用户、设置会话 cookie
POST /api/auth/authlocker/resend-otpauthClient.authlocker.resendOtp()再次发送相同的验证

AuthLocker 生成并检查每个代码. 这个插件从不薄荷, 永远不要存储一个, 从来没有计算尝试。 更好的奥特 verification 表格完全没有使用——重放保护,五通关卡 10分钟后, 三档预算和第六十二档 都住在AuthLocker,按键 verificationId。 。 。 .

如果你打开线人 期待检察官办公室的正常账簿记录, 因此,应当明确指出:这里没有任何人。 这也是重点所在。 满 授权就是让Twilio验证欺诈的分数 节流阀和短消息 花帽在玩。 仅限插件 已交付 消息会把一切都扔掉的.

唯一不住在奥特洛克的地方 是限制你的速度 应用程序将调用它, 因此插件宣布它自己的费率限制为防御 深度:每分5发,每分10次验证,每个客户端.

设置

1. 联合国. lib/auth.ts

import { betterAuth } from "better-auth";
import { nextCookies } from "better-auth/next-js";
import { authlocker } from "@authlocker/better-auth";

export const auth = betterAuth({
  database: /* your adapter */,
  plugins: [
    authlocker({
      clientId: process.env.AUTHLOCKER_CLIENT_ID!,
      clientSecret: process.env.AUTHLOCKER_CLIENT_SECRET!,
    }),
    // `nextCookies()` must be LAST. It works by hooking every response after
    // the other plugins have written their cookies; anything registered after
    // it sets cookies that never reach the browser.
    nextCookies(),
  ],
});

客户端的秘密永远留在服务器上. POST /api/v1/verify/* 带有 HTTP Basic 和 AuthLocker 的认证 发送任何 CORS 头, 所以 a 浏览器无法调用它, 即使你愿意将这个秘密传送给一个. 这个限制是这个插件存在的原因 .

从 POST /oauth/register,或 npx botparty authlocker provision。 。 。 .

2. 联合国. app/api/auth/[...all]/route.ts

import { toNextJsHandler } from "better-auth/next-js";
import { auth } from "@/lib/auth";

export const { GET, POST } = toNextJsHandler(auth.handler);

3个. lib/auth-client.ts

import { createAuthClient } from "better-auth/react";
import { authlockerClient } from "@authlocker/better-auth/client";

export const authClient = createAuthClient({
  plugins: [authlockerClient()],
});

方法名称来源于端点路径 kebab- case 到 caham- case, 所以 /authlocker/send-otp 变成 authClient.authlocker.sendOtp()您没有 宣布他们.

4. 管理移徙

该插件添加 phoneNumber 财务报告和审定财务报表 phoneNumberVerified 页:1 user 页:1 短消息频道需要放置一个已核实的号码和更好的 Auth 用户 模型没有一栏。 名字和Auth的内置一致 phoneNumber() 插件是故意的,所以运行两个应用程序都会有一个 页:1.

npx @better-auth/cli migrate

用这个

"use client";
import { useState } from "react";
import { getAttemptsRemaining } from "@authlocker/better-auth/client";
import { authClient } from "@/lib/auth-client";

export function SignIn() {
  const [verificationId, setVerificationId] = useState<string | null>(null);
  const [attemptsLeft, setAttemptsLeft] = useState(5);
  const [message, setMessage] = useState("");

  async function send(to: string) {
    const { data, error } = await authClient.authlocker.sendOtp({
      channel: "email",
      to,
    });
    if (error) return setMessage(error.message ?? "Could not send a code");
    setVerificationId(data.verificationId);
    // `data.to` is masked — "a**@example.com". Safe to show back.
    setMessage(`Code sent to ${data.to}`);
  }

  async function verify(code: string) {
    const { error } = await authClient.authlocker.verifyOtp({
      verificationId: verificationId!,
      code,
    });
    if (!error) return; // Session cookie is set. You are signed in.

    switch (error.code) {
      case "INVALID_CODE": {
        const left = getAttemptsRemaining(error) ?? 0;
        setAttemptsLeft(left);
        return setMessage(`Wrong code — ${left} left`);
      }
      case "VERIFICATION_LOCKED":
        return setMessage("Too many tries. Start again.");
      case "VERIFICATION_EXPIRED":
        return setMessage("That code expired. Send a new one.");
      default:
        setMessage(error.message ?? "Something went wrong");
    }
  }
}

短信,通过 channel: "sms" 和E.164的电话号码(+15551234567) (中文(简体) ).

错误是 API

该插件通过AuthLocker的出错代码而不是平分它们, 因为一个签到的形式 真正不同的事情 来谈论每一个. 把它们全部折叠到一个赤裸的401——这就是手卷的代理端点 通常是的——使正确的UI无法构建.

error.code状态这是什么意思
INVALID_CODE400个错误的代码。 还带着 attemptsRemaining。 。 。 .
VERIFICATION_LOCKED409个5次尝试。 终端——发送新代码.
VERIFICATION_EXPIRED400个过了10分钟TTL。 责无旁贷;提供再版.
VERIFICATION_ALREADY_VERIFIED409个单用. 通常为双提交表.
RESEND_TOO_SOON429个60秒窗内.
RESEND_LIMIT_REACHED429个3次重新进行核查.
NOT_FOUND404 (韩语)未知或已收获 verificationId。 。 。 .
SMS_REQUIRES_CLAIMED_CLIENT403 (韩语)见下文.
SMS_COUNTRY_BLOCKED403 (韩语)此客户端没有启用拨号代码 .
SMS_DAILY_CAP_REACHED429个客户端自有"每日短信"封顶.
USER_NOT_FOUND400个disableSignUp 没有人拥有那个地址。 密码是 对;不要告诉当事人这是错的.
AUTHLOCKER_UNREACHABLE第503条腾讯网. 故意在AuthLocker的词汇之外,所以一个DNS的失败永远不会被误认为是一个错误的代码.

attemptsRemaining 以兄妹身份抵达 error 在电线上,不是里面. 插件将它提升到错误的正文上 getAttemptsRemaining(error) 从 @authlocker/better-auth/client 读回 — 更好的 Auth 客户端错误类型 固定在 { code, message, status, statusText },所以额外的字段到达 浏览器,但不包括类型,而这个帮助者是替代铸件的替代.

你发送的第一个短消息可能会失败

SMS_REQUIRES_CLAIMED_CLIENT 是最可能的首发失败. AuthLocker只为归属于BotParty命名空间的客户发送短信, 因为每条消息都要花很多钱 电子邮件和 OAuth 的未声明工作 客户端,所以没有 警告你直到第一个 channel: "sms"。 。 。 .

向客户索赔 registration_access_token,或 npx botparty authlocker provision,设置 verify.dailySmsCap 当你 在那边.

插件还记录了像这样的配置类失败 ctx.context.logger 在服务器上, 不仅仅是呼叫者 第一次在浏览器内获取,其中半构件形式极有可能 吞下尸体,展示一个通用的红色土司.

社会登录 : genericOAuth,没有我们的插件

AuthLocker是符合标准的OIDC供应商,所以Better Auth的内置 genericOAuth 用发现处理Google和GitHub。 没有 这个包裹已经涉及到了.

import { betterAuth } from "better-auth";
import { genericOAuth } from "better-auth/plugins";
import { nextCookies } from "better-auth/next-js";
import { authlocker } from "@authlocker/better-auth";

export const auth = betterAuth({
  database: /* your adapter */,
  plugins: [
    authlocker({
      clientId: process.env.AUTHLOCKER_CLIENT_ID!,
      clientSecret: process.env.AUTHLOCKER_CLIENT_SECRET!,
    }),
    genericOAuth({
      // One entry per upstream provider, sharing a single client_id.
      config: (["google", "github"] as const).map((provider) => ({
        providerId: provider,
        clientId: process.env.AUTHLOCKER_CLIENT_ID!,
        clientSecret: process.env.AUTHLOCKER_CLIENT_SECRET!,
        discoveryUrl:
          "https://authlocker.dev/.well-known/openid-configuration",
        // The discovered authorization_endpoint is /oauth/authorize, which
        // answers 400 unless the request names a provider.
        authorizationUrlParams: { provider },
        scopes: ["openid", "email", "profile"],
        // Defaults to true in 1.7. Written out because AuthLocker *requires*
        // PKCE — leaving it implicit hides a hard requirement.
        pkce: true,
      })),
    }),
    nextCookies(),
  ],
});
await authClient.signIn.social({ provider: "google" });

6个容易出错的东西,都会产生混乱 失败,而不是明显的失败:

  • 每个上游供应商一个配置,一个不叫 "authlocker"。 。 。 . 这个 发现 authorization_endpoint 实值 /oauth/authorize,和 AuthLocker 键 拒绝一个不指定提供者的请求——所以每个条目都需要 authorizationUrlParams: { provider: "google" }编辑 providerId 同时是 您所传递的字符串 signIn.social() 还有 <SocialButtons> 从 登记发送,所以命名他们 "google" 财务报告和审定财务报表 "github"。 。 。 .
  • 回电是 {baseURL}/api/auth/callback/{providerId} ——每笔 输入,所以 /api/auth/callback/google 财务报告和审定财务报表 /api/auth/callback/github选择 /api/auth/oauth2/callback/...,这是前1.7,也是大多数博客贴出的内容 仍然显示。 用 AuthLocker 注册确切的字符串 : redirect_uri 实值 准确匹配,登记后不能更改,不登记 再来一次.
  • 奥米 redirectURI 让更好的Auth从中得来 baseURL编辑 上游的文献对此自相矛盾 匹配上面的路径.
  • authClient.signIn.social({ provider })。 。 。 . signIn.oauth2() 不再继续 已存在.
  • 没有 genericOAuthClient() 再来一次. 客户端没有要添加的内容 用于社交登录.
  • nextCookies() 最后一个,如上. (中文(简体) ).

每个应用程序注册客户端一次,而不是每个功能一次

AuthLocker 问题 双向 主题标识符,取自 client_id :: provider :: upstream user id。注册客户两次和 同一个Google账户在两个不同的下方到达 subs, 哪个更好的 Auth 存储为两个账户,没有投诉。 每个应用程序一个注册——两个 genericOAuth 上面的条目是罚款 完全因为他们共享 a client_id;只有上游供应商不同,谷歌的身份和 GitHub的身份总是两个身份.

已经使用更好的 Auth emailOTP()? 吗?

你无法做到 emailOTP() 我是奥特洛克的代表 它已经 没有 verifyOTP 钩子 它生成代码,储存在 verification 表格中, 盘点尝试,自己检查, 让你的回话来完成交付 只有这个 连接 AuthLocker 进入 sendVerificationOTP 会降级到邮件中 中继 : 消息中的代码将是更好的 Auth , 而 AuthLocker 的欺诈 计分,尝试关闭 和每个目标油门永远不会运行.

内置 phoneNumber() 插件 已经 有个 verifyOTP 钩子和可以 代表适当的短信。 如果你已经是了,这才是真正的选择 投资了它,但它覆盖一个频道, 所以你最后会有两个 不同的客户端 API 为相同的流量, 它仍然留下电子邮件未授权 . 此插件以一个形状覆盖两个频道 .

如果你坚持 phoneNumber() 与此插件并列, 共享 phoneNumber 栏是有意的,两个定义合并.

选项

authlocker({
  clientId: string,
  clientSecret: string,

  // Base URL of the AuthLocker deployment. Default "https://authlocker.dev".
  issuer?: string,

  // Refuse to provision a user for an address nobody owns yet, so a correct
  // code signs in an existing account or fails with USER_NOT_FOUND.
  // Off by default: passwordless OTP sign-in is normally also sign-up.
  disableSignUp?: boolean,

  // User column holding a verified E.164 number. Default "phoneNumber".
  phoneNumberField?: string,

  // Placeholder address for a user who signed up by SMS — Better Auth's user
  // model requires a unique email and an SMS sign-up has none. The default
  // builds one under `.invalid`, which RFC 2606 reserves so it can never
  // resolve. It does copy the number into a second column; override this if
  // that matters to you.
  getTempEmail?: (phoneNumber: string) => string,

  // Display name for a newly created user. Empty by default — prompt for a
  // real one afterwards rather than guessing from an address.
  getName?: (to: string, channel: "email" | "sms") => string,
})

限制费率

插件声明 :

路径窗口马克斯
/authlocker/send-otp, (中文(简体) ), /authlocker/resend-otp60岁页:1
/authlocker/verify-otp60岁10个

二事须知. 更好的 Auth 应用利率限制 仅生产 以 默认( e)rateLimit.enabled),这些是惰性的 next dev和一个 应用程序级 rateLimit.customRules 其中一条路径的条目将覆盖 插件集,这是支持的收紧或松动它们的方法 :

betterAuth({
  rateLimit: {
    customRules: {
      "/authlocker/send-otp": { window: 3600, max: 10 },
    },
  },
});

服务器端呼叫

每个终点都在 auth.api 此外,对于服务器动作和路由处理器:

const verification = await auth.api.sendOtp({
  body: { channel: "email", to: "[email protected]" },
});

什麽 verify-otp 返回时

{ verification, token, user }。 。 。 . token 财务报告和审定财务报表 user 是其他更好的东西 认证登录返回; verification 是因为 @authlocker/react 类型 AuthLockerAdapter.checkVerification 以 Verification 财务报告和审定财务报表 把它开走 如果没有正确的代码, 将失败的解析 并显示为一个错误 对于已经获得一个会话饼干的人.

没有记录的东西

地址和代码,永远. verify/check 是一个 AuthLocker 响应 - 代码刚刚证明收件人 收到它——这个插件将这个值视为查找或创建输入:永远 已登录, 并且除属于用户行外, 无处写入 。 它确实会回来 打开 verification.to键入一个浏览器 与a并列 user 已经带有同一地址;这就是允许 onVerified 显示它自己的文件承诺。 到处都是 to 实值 已蒙上面罩( n)a**@example.com, (中文(简体) ), +*******6789) (中文(简体) ).

许可证

麻省理工学院

版本与发布记录

@authlocker/better-auth 共发布 2 个版本,首次发布于 2026/09/01;过去 12 个月发布了 2 个版本。

版本发布日期距今
0.0.2latest2026/09/022 个月前
0.0.12026/09/012 个月前

关于 @authlocker/better-auth 的常见问题

@authlocker/better-auth 是什么?

AuthLocker 更好的 Auth 插件: 由 authlocker.dev 验证的电子邮件和短消息代码, 并为您创建会话 .

如何安装 @authlocker/better-auth?

在项目目录运行 npm install @authlocker/better-auth,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:better-auth(>=1.7.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@authlocker/better-auth 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@authlocker/better-auth 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@authlocker/better-auth 可以商用吗?

@authlocker/better-auth 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@authlocker/better-auth 的下载量有多少?

根据 npm 官方下载统计,@authlocker/better-auth 在 2026/10/07 – 2026/11/05 这 30 天内被下载 3,348 次,日均约 112 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@authlocker/better-auth 还在维护吗?

最新版本 0.0.2 发布于 2026/09/02(2 个月前);至今共发布 2 个版本,其中过去 12 个月发布了 2 个;首次发布于 2026/09/01。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。