Skip to content

授权界面 apps/auth

NATLAN DN42 身份服务的 OAuth / OIDC 授权界面,生产入口 https://auth.natlan.io/authorize

四个站里的两个例外:不用 SvelteKit不部署成 Cloudflare Worker。两条例外都 有具体理由,见第 5 节与 internals/auth-hosting.md

1. 它做什么

它是 OAuth 授权码流里那一屏「请确认授权」,前面挂着一整套 DN42 身份验证:

/authorize?client_id=…&redirect_uri=…&…
  ↓ 后端校验并归一化请求参数(前端从不信任原始 query)
① ASN        输入 ASN,查 registry 得到 mntner 与可用验证方式
② 验证方式    email / PGP / SSH —— 由服务端下发,含「这一项是否需要人机验证」
③ 应答       按方式完成挑战(收码 / 签名)
④ 授权       展示 client 与 scope,同意或拒绝 → 后端返回 redirect_to

passkey 快路径贯穿全程:上次验证成功的 ASN 记在 dn42.auth.lastAsn,下次进入 页面时会无提示地发起一次 passkey 登录尝试;任何失败(未注册、用户关闭对话框、 Safari 拒绝无用户激活的凭据请求)都是静默的——常规的 ASN 步骤已经在下面就位并预填好, 方式步骤里也仍然提供手动 passkey 按钮。验证通过后可在同意页注册 / 管理凭据。

2. 契约要点

要点说明
重定向目标只来自后端响应绝不从原始 query 里取。ContextRedirectapproveredirect_to 是唯一来源
人机验证的必要性由服务端决定VerificationOption.captcha_required不要kind 反推——策略在服务端,可以不发前端就改
验证码票一次性解一次可换一个 captcha_pass(绑定该 ASN、约 30 分钟),重发挑战凭它跳过验证码
params 原样回传authorize-context 返回的已归一化参数,在 approve 时逐字节回传
会话令牌只进 sessionStorage安全红线:身份凭据必须随标签页消亡,不进 localStorage、不进 cookie、不进 URL

dn42.auth.lastAsn 是唯一进 localStorage 的东西——ASN 是半公开的 registry 数据、 不是凭据,那条红线对它不适用。

端点全表见 api/auth.md

3. 组件

文件职责
App.svelte流程编排:boot → 校验请求 → 有无会话 → 验证身份 → 同意
Stepper.svelte四步指示器
StepAsn / StepMethod / StepAnswer / StepConsent四个步骤屏
PasskeyEnrollCard / PasskeyManagerpasskey 注册与凭据管理
CodeInput / CopyBlock验证码输入、可复制块(PGP / SSH 挑战文本)
ErrorScreen致命错误终屏
lib/errors.ts错误分类(classify)与文案
lib/webauthn.ts / lib/passkeys.tsWebAuthn 编解码与登录 / 注册流程

人机验证控件来自共享库 $uiCapCheck,解题器是 ui/vendor/cap/

4. 主题与语言

  • 语言:英 / 简中 / 繁中 / 日,页内切换,存 dn42.auth.lang
  • 主题:不盖 data-theme 标记,直接跟随系统。这是一次性流程,没有导航也没有 设置入口。共享样式表为此备了第二条暗色投递路径,见 internals/i18n-and-theming.md

5. 为什么不用 SvelteKit

它只有一个入口(/authorize)、没有路由也没有 SSR,一个 index.html 加一棵组件树 就够了;套上 SvelteKit 只会多一层 adapter 和 .svelte-kit 产物。

因此 $ui 别名在它自己的 vite.config.ts 里声明(另外两站由 sveltekit({ alias }) 注入)。

6. 部署

源站 nginx 同源静态托管,不是 Worker。产物 dist/ 与认证服务在同一个来源下, 前端始终用相对路径请求 /api/v1/*,因此零 CORS。

两条最容易踩的:

⚠️ 改了 ui/ 之后这个站不会自动重建。 另外三站被 Cloudflare 的 Build watch paths 覆盖,这个站不在 CF 上——必须手工 npm run build:auth 并重新投放。

⚠️ index.html 里的两处 __CSP_NONCE__ 占位符不能删。 nginx 靠 sub_filter 逐请求把它换成 $request_id,同一个值也进 CSP 响应头。删了验证码会静默超时。

发布顺序、自检与验收见 guides/deploy-auth.md。 托管形态的完整理由见 internals/auth-hosting.md

7. 本地开发

bash
npm run dev:auth      # http://localhost:5173

vite.config.ts/api 反代到 https://auth.natlan.io,与线上的同源布局一致—— 前端代码始终用相对路径,本地也没有 CORS,不需要为开发切基址。指向另一套部署时用 VITE_AUTH_API

vite dev 下没有东西替换 __CSP_NONCE__,也不发 CSP,所以那个占位符在开发时是惰性的。