better-auth-oauth-passkey

Better Auth 集成:OAuth 提供方与通行密钥

认证服务通过一个工厂函数来构建它的 Better Auth 实例。这个工厂从环境读取配置,接入基于 Prisma 的存储,挂上通行密钥(passkey)插件,并只注册那些确实配置好的社交登录提供方。在这个库实例之外,还有一层手写的 OAuth 服务,承载着应用真正在意的账号关联与强因子(strong factor)逻辑。这个节点之所以存在,正是因为这两者:库提供原语,服务决定一次登录意味着什么

工厂

createAuth 接收一个 Prisma client,返回 Better Auth 实例以及规范化后的 base path。在构造任何东西之前它先做快速失败:环境里必须存在签名密钥,否则抛错。base URL 和 base path 都被手动规范化——强制给 path 补上前导斜杠、给两者去掉尾部斜杠——这样无论环境怎么写,重定向 URI 都能可预测地拼出来。

实例的配置包括:

  • 存储——基于 PostgreSQL 的 Prisma adapter,会话存进数据库,而不是只留在 token 里。
  • 禁用邮箱+密码——这套部署不通过 Better Auth 做密码登录;强因子是 OAuth 和通行密钥。
  • 通行密钥插件——用一个 relying-party id、一个 relying-party name,以及一个或多个允许的 origin 来配置。给出多个 origin 时传入列表,只有一个时传入单值。
  • 社交登录提供方——按条件加入(见下)。
  • 限流——默认开启,可通过配置关掉。

按条件注册社交登录

只有当某个提供方的 client id client secret 都在环境里存在时,它才会被注册。Google 和 LinkedIn 各自都受这道闸把守。对每一个配置好的提供方,工厂用规范化后的 base URL、base path 再加上每个提供方各自的回调段,推导出重定向 URI——回调地址从不手写。如果最终没有任何提供方被配置,工厂只是记一条 warning,而不是失败——服务仍可只靠通行密钥运行。

这条"只配置存在的东西"的规则意味着:一套部署要打开某个提供方,只需提供它的凭据;要关掉,只需省略它。这里没有单独的 feature flag。

薄薄的插件包装

Prisma adapter 和通行密钥插件各自都通过一个只有一行的包装模块引入,把 Better Auth 的构建物以本地名字重新导出。这些包装存在的意义,是给代码库其余部分一个稳定的 import 路径和一个带类型的接口,而不是增加行为——它们是接缝(seam),不是逻辑。

应用层的 OAuth 服务

在库之外,一个 OAuthService 承载着产品自己定义的流程。它在构造时把同样的提供方(Google、LinkedIn)从配置读进一个 map,所以它对那些从未配置过的提供方同样保持沉默。它的职责:

  • 换 token——拿授权码去提供方的 token 端点换取 token,失败时把提供方自己的错误描述透出来。
  • 取用户信息——用 access token 去提供方的 userinfo 端点拉取资料。
  • 登录——按 email(转小写;email 必填)解析账号。如果用户已存在而该提供方尚未关联,就关联上;如果用户不存在,就创建一个——OAuth 的 email 被当作已验证,因为提供方已经验证过——然后关联提供方账号并开启会话。
  • 关联——当调用方已经持有一个会话时,同一个登录入口会转向关联路径:它要求会话存在、拒绝重复关联同一个已关联的提供方、记录新账号,并让任何缓存的资料失效。

另有一份并行的提供方清单,列出受支持的提供方(Google、LinkedIn)及其显示名和 connect 路径,让周边的应用能渲染"关联"入口,而不必重复一份提供方知识。

供给强因子生命周期

一次成功的 OAuth 登录会给用户记录一条类型为 oauth 的强因子断言,返回的用户对象被标记为强因子已完成,并带上方法和完成时间。同一个对象还会报告该用户是否注册了任何通行密钥。这就是这些认证路径与账号安全态势之间的接点:OAuth 与通行密钥是用户达成强因子完成的两条路,每一次登录或关联都会更新系统其余部分据以判断"这个账号被允许做什么"的那些标志位。

为什么是这个形状

Better Auth 负责那些手写既繁琐又危险的机制——WebAuthn 仪式、OAuth 往返、会话存储。应用层的 OAuth 服务负责那些专属于本产品的决策——什么算强因子、账号何时是新建何时是关联、注册时可选的"按邮箱域名判定管理员"规则。把这两者分开,产品规则才保持可读,库也能在其下被独立升级。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →