Atmosphere 登录

本页内容

@emdash-cms/auth-atproto 包为 EmDash 添加了 Atmosphere 账户 登录选项。Atmosphere 账户是一种便携式、由用户拥有的身份,可在 Bluesky 及 AT Protocol 网络中的其他应用上使用。用户使用自己的 handle(例如 alice.bsky.social)登录,并在自己的提供者处进行身份验证 — EmDash 永远不会看到密码。

在以下情况下,这是一个很好的选择:

  • 你的贡献者已经拥有 Atmosphere 账户。
  • 你想限制对组织所控制域名(*.yourcompany.com)的访问,而无需管理 OAuth 应用或邀请。
  • 你正在构建属于更广泛 Atmosphere 生态的产品,并希望与技术栈的其余部分保持一致的身份。

安装

安装提供者包:

pnpm add @emdash-cms/auth-atproto

将提供者添加到 EmDash 集成中:

import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { atproto } from "@emdash-cms/auth-atproto";

export default defineConfig({
	server: {
		host: "127.0.0.1", // required for local development; see below
	},
	integrations: [
		emdash({
			authProviders: [atproto()],
		}),
	],
});

这样就足以在登录页面和设置向导上显示 Sign in with Atmosphere。未配置允许列表时,第一个用户成为 Admin,之后所有人的自助注册都会关闭 — 请参阅允许列表来开放注册。

该提供者是一个公共 OAuth 客户端,并在 /.well-known/atproto-client-metadata.json 提供自己的元数据文档,因此仅凭上面的配置即可工作 — 无需设置环境变量、客户端密钥或注册 OAuth 应用。

配置访问

atproto() 提供者接受一个允许列表和一个默认角色:

atproto({
	allowedDIDs: ["did:plc:abc123..."],
	allowedHandles: ["*.example.com", "alice.bsky.social"],
	defaultRole: 30, // Author
});
选项类型默认值说明
allowedDIDsstring[]无精确匹配的 DID 允许列表。
allowedHandlesstring[]无handle 允许列表。支持开头的通配符(*.example.com)。
defaultRolenumber10(Subscriber)分配给第一个用户之后被允许的用户的角色。第一个用户始终是 Admin。

完整的角色层级记录在主身份验证指南中。

允许列表

如果既没有设置 allowedDIDs 也没有设置 allowedHandles,则只有第一个用户可以注册。已关联到 EmDash 用户的账户可以继续登录,而新账户会被拒绝,并返回 signup_not_allowed。

当至少配置了一个允许列表时,每一次登录(包括现有用户的登录)都必须与之匹配。将现有用户的 DID 和 handle 从已配置的列表中移除,该账户就无法再登录。只要任意一个列表匹配,用户就会被允许:

  • DID 匹配。 用户稳定的账户标识符与 allowedDIDs 中的某个值完全匹配。
  • handle 匹配。 用户的 handle 与 allowedHandles 中的某个条目匹配,可以是精确匹配,也可以通过开头带通配符的模式匹配(*.example.com 匹配 alice.example.com 和 bob.team.example.com)。

尽管 handle 是可变的,handle 允许列表仍然是安全的。在通过 handle 匹配放行用户之前,EmDash 会独立解析该 handle 的 DNS/HTTP 记录,并验证它指向的 DID 与提供者声称的 DID 相同。行为异常的提供者不能仅仅声称自己拥有 you.yourcompany.com。

默认角色

被允许的用户会获得你在 defaultRole 中设置的角色。只有第一个用户 — 即完成设置的用户 — 会被强制设为 Admin。Atmosphere 账户没有组/角色映射;如果你需要更细粒度的角色,请在用户首次登录后,在管理后台侧边栏的 Users 页面中更改该用户的角色。

设置第一个用户

当你使用已配置 Atmosphere 提供者的全新站点启动时,设置向导会将其作为创建初始管理员账户的选项提供。

  1. 访问 /_emdash/admin。在 Set up your site 中,输入站点标题和可选的副标题,然后继续。

  2. 在 Create your account 中,输入要存储在 EmDash 用户上的电子邮件地址和可选的姓名。

  3. 在 Secure your account 中,选择 Atmosphere,输入你的 handle(例如 alice.bsky.social),然后继续。

  4. 你的账户提供者会打开其授权页面。使用该提供者支持的方式登录,并批准请求。

  5. 提供者会将你重定向回 EmDash。EmDash 会将第一个用户创建为 Admin,保存第 2 步中的电子邮件,建立 EmDash 会话,并打开仪表板。

之后的登录从 handle 开始,在账户提供者处继续,并带着 EmDash 会话返回。提供者的 OAuth 状态和令牌与该 EmDash 会话分开存储,因此 OAuth 回调可以完成,提供者也可以刷新自己的会话。

本地开发

AT Protocol 的 OAuth 配置要求环回重定向 URI 使用 IP 字面量(127.0.0.1 或 [::1]),而不是 localhost。EmDash 在生成重定向 URI 时会透明地将 ://localhost 重写为 ://127.0.0.1,但这意味着你的开发会话也需要从 127.0.0.1 开始 — 否则,在 localhost 上设置的会话 cookie,在重定向将你带到 127.0.0.1 之后将不可见。

Astro 的开发服务器使用 Vite,默认绑定到 localhost。请将 Astro 顶层的 server.host 选项设置为环回 IP:

export default defineConfig({
	server: {
		host: "127.0.0.1",
	},
	// ...
});

然后在 http://127.0.0.1:4321/_emdash/admin 打开,完成整个流程。

生产环境

相同的配置在生产环境中同样适用。提供者会在以下地址提供自己的客户端元数据:

https://your-site.example.com/.well-known/atproto-client-metadata.json

授权服务器在登录期间会获取此 URL,以验证客户端的重定向 URI。请确保你部署的站点 URL 可通过 HTTPS 从公共互联网访问 — 位于 VPN 之后、仅限内部访问的部署无法完成登录,因为用户的授权服务器无法获取元数据文档。

如果你在终止 TLS 的反向代理后面运行 EmDash,请设置 siteUrl,以便 EmDash 构建正确的重定向 URI。没有它,请求看起来像 http://internal-host:4321,元数据将与授权服务器看到的不匹配。

故障排除

”Account is not in the allowlist”

你用来登录的 handle 或 DID 不在 allowedDIDs / allowedHandles 中。请检查通配符模式(必须以 *. 开头),并记住 handle 匹配是针对 DNS/HTTP 验证的 — 如果该 handle 的 DID 记录当前没有解析到提供者返回的同一个 DID,匹配就会被拒绝。

“Self-signup is not allowed”

你已成功到达回调,但没有配置允许列表,并且你不是第一个用户。请将该账户的 DID 添加到 allowedDIDs,或将其已验证的 handle 添加到 allowedHandles。电子邮件邀请不会将 Atmosphere DID 关联到 EmDash 用户。

登录无错误地重定向到登录页面

这几乎总是本地开发中描述的环回 cookie 问题。在 http://127.0.0.1:4321(设置 server.host: "127.0.0.1" 后)打开管理面板并重试。

自托管 handle 的 handle 解析失败

提供者通过让 DNS-over-HTTPS(Cloudflare 的 DoH 端点)与 HTTP /.well-known/atproto-did 查询相互竞速来验证 handle。自托管的 handle 至少需要以下之一:

  • 包含 did=<your-did> 的 _atproto.<handle> DNS TXT 记录,或
  • 包含该 DID 的 https://<handle>/.well-known/atproto-did 文件。

如果两种方法都失败,即使底层账户有效,handle 匹配也会被拒绝。allowedDIDs 中的 DID 不受影响 — 它们是直接匹配的。