身份验证

本页内容

EmDash 使用通行密钥(Passkey)作为主要登录方式。通行密钥能抵御网络钓鱼、无需记忆密码,并可通过浏览器或密码管理器跨设备使用。

对于 Cloudflare 部署,可选择使用 Cloudflare Access 作为替代身份验证方式。

工作原理

通行密钥基于 WebAuthn 标准,创建存储在设备上或通过密码管理器同步的公钥凭据。登录时,设备证明持有凭据,而无需通过网络发送密码。

通行密钥的优势:

  • 无需记忆或泄露密码
  • 防钓鱼 — 凭据绑定到站点域名
  • 跨设备同步 — 支持 iCloud 钥匙串、Google 密码管理器、1Password 等
  • 快速登录 — 生物识别或 PIN 一键完成

首位用户设置

首次访问后台时,设置向导会引导你创建管理员账号。

  1. 打开 http://localhost:4321/_emdash/admin

  2. 你会被重定向到设置向导,填写:

    • Site Title — 站点名称
    • Tagline — 简短描述
    • Admin Email — 你的邮箱地址
  3. 点击 Create Site 注册通行密钥

  4. 浏览器会提示你创建通行密钥:

    • macOS:Touch ID、设备密码或安全密钥
    • Windows:Windows Hello 或安全密钥
    • 移动端:Face ID、指纹或 PIN
  5. 通行密钥注册成功后,你会登录并跳转到管理仪表盘。

登录

设置完成后,再次访问后台会触发通行密钥认证:

  1. 访问 /_emdash/admin

  2. 未登录时会看到登录页面

  3. 点击 Sign in 进行认证

  4. 浏览器提示使用通行密钥(生物识别、PIN 或安全密钥)

  5. 验证通过后跳转到管理仪表盘

魔法链接备选

无法使用通行密钥时(如设备丢失),可用魔法链接作为替代。需要先配置邮件。

  1. 在登录页点击 Sign in with email

  2. 输入邮箱地址

  3. 查收收件箱中的登录链接

  4. 点击链接完成认证(15 分钟内有效)

OAuth 登录

配置后 EmDash 支持 GitHub 和 Google 的 OAuth 登录。用户可在通行密钥设置完成后关联账号。

详见配置指南

用户角色

EmDash 使用五级角色的访问控制:

角色等级说明
Subscriber10仅查看权限
Contributor20创建内容(需审核)
Author30创建/编辑/发布自己的内容
Editor40管理所有内容
Admin50完全访问权限,含设置

每个角色继承所有更低级别的权限。首位用户始终创建为 Admin。

邀请用户

管理员可通过后台邀请新用户:

  1. 前往 Settings > Users

  2. 点击 Invite User

  3. 输入用户邮箱并选择角色

  4. 点击 Send Invite

  5. 用户会收到包含邀请链接的邮件

  6. 点击链接注册通行密钥

邀请 7 天内有效。管理员可在用户页面重新发送或撤销邀请。

管理通行密钥

用户可在账号设置中管理通行密钥:

  • 添加通行密钥 — 注册额外的通行密钥用于备份或其他设备
  • 删除通行密钥 — 删除不再使用的通行密钥
  • 重命名通行密钥 — 为通行密钥设置描述性名称

每位用户最多可注册 10 个通行密钥。

自助注册

对于团队站点,可为特定邮箱域名启用自助注册:

import { defineConfig } from "astro/config";
import emdash from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			auth: {
				selfSignup: {
					domains: ["example.com"],
					defaultRole: "contributor",
				},
			},
		}),
	],
});

匹配邮箱域名的用户无需邀请即可注册。他们会收到验证邮件,并注册通行密钥完成注册。

会话配置

会话使用安全的 HttpOnly Cookie,默认配置合理:

emdash({
	auth: {
		session: {
			maxAge: 30 * 24 * 60 * 60, // 30 天(默认)
			sliding: true, // 活动时重置过期时间
		},
	},
});

安全说明

  • 通行密钥存储为公钥 — 私钥永远不会离开设备
  • 挑战验证防止重放攻击
  • 速率限制防止暴力破解(每 IP 每分钟 5 次尝试)
  • 会话 Cookie 为 HttpOnly、Secure、SameSite=Lax
  • 魔法链接令牌经 SHA-256 哈希 — 原始令牌不会被存储

故障排查

”No passkeys registered”

登录时看到此错误,通行密钥可能已从密码管理器中删除。请联系管理员发送魔法链接或新的邀请。

“Passkey authentication failed”

通常是因为通行密钥是为不同域名创建的。通行密钥与域名绑定 — 为 localhost:4321 创建的通行密钥无法在 example.com 使用。请为每个域名注册新的通行密钥。

“Session expired”

默认会话有效期为 30 天,支持滑动过期。如果意外退出,请清除 Cookie 后重新登录。

丢失所有通行密钥

如果你丢失了所有已注册通行密钥的访问权限:

  1. 请另一位管理员发送魔法链接(需配置邮件)
  2. 使用魔法链接登录
  3. 在账号设置中注册新的通行密钥

如果你是唯一管理员且未配置邮件,需要通过数据库重置站点的身份验证。

Cloudflare Access

部署到 Cloudflare 时,可使用 Cloudflare Access 替代通行密钥。Access 利用你现有的身份提供商在边缘处理认证。

为何使用 Cloudflare Access?

  • 单点登录 — 用户通过公司身份提供商认证
  • 集中访问控制 — 在 Cloudflare 面板管理后台访问权限
  • 无需通行密钥管理 — 无需注册或管理通行密钥
  • 基于组的角色 — 自动将身份提供商组映射到 EmDash 角色

设置

  1. 为 EmDash 站点创建 Cloudflare Access 应用
  2. 在应用设置中记录 Application Audience (AUD) Tag
  3. 配置 EmDash 使用 Access:
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import emdash from "emdash/astro";
import { d1, access } from "@emdash-cms/cloudflare";

export default defineConfig({
	output: "server",
	adapter: cloudflare(),
	integrations: [
		emdash({
			database: d1({ binding: "DB" }),
			auth: access({
				teamDomain: "myteam.cloudflareaccess.com",
				audience: "abc123def456...", // 来自 Access 应用设置
			}),
		}),
	],
});

配置选项

选项类型默认值说明
teamDomainstring必填Access 团队域名(如 myteam.cloudflareaccess.com
audiencestring必填Access 设置中的 Application Audience (AUD) Tag
autoProvisionbooleantrue首次 Access 登录时自动创建 EmDash 用户
defaultRolenumber30未匹配任何组的用户角色(30 = Author)
syncRolesbooleanfalse每次登录时根据身份提供商组更新角色
roleMappingobject将身份提供商组名映射到角色等级
audienceEnvVarstring"CF_ACCESS_AUDIENCE"audience tag 的环境变量名(替代硬编码)

角色映射

将身份提供商组映射到 EmDash 角色:

emdash({
	auth: access({
		teamDomain: "myteam.cloudflareaccess.com",
		audience: "abc123...",
		roleMapping: {
			Admins: 50, // Admin
			"Content Editors": 40, // Editor
			Writers: 30, // Author
		},
		defaultRole: 20, // 未在任何组中的用户为 Contributor
	}),
});

用户属于多个组时,第一个匹配的组生效。首位访问站点的用户始终成为 Admin,不受组设置影响。

角色同步行为

默认情况下(syncRoles: false),用户角色在首次登录时设定,之后不再变更。这允许管理员在 EmDash 中手动调整角色。

设置 syncRoles: true 可让身份提供商组具有权威性 — 用户角色将在每次登录时根据当前所属组更新。

工作流程

  1. 用户访问 /_emdash/admin
  2. Cloudflare Access 拦截并重定向到身份提供商
  3. 用户完成认证(SSO、MFA 等)
  4. Access 在请求中设置签名的 JWT
  5. EmDash 验证 JWT 并创建/认证用户

禁用的功能

启用 Access 后,以下功能不可用:

  • 登录页面(/_emdash/admin/login
  • 通行密钥注册和管理
  • OAuth 登录
  • 魔法链接登录
  • 自助注册
  • 用户邀请

用户管理完全通过 Cloudflare Access 策略进行。

故障排查

”No Access JWT present”

请求到达 EmDash 时没有 Access JWT,说明:

  • Access 未配置为保护你的应用
  • Access 策略未匹配后台路由

请验证 Access 应用覆盖了 /_emdash/admin/*

“JWT audience mismatch”

配置中的 audience 与 JWT 不匹配。请仔细检查 Access 应用设置中的 Application Audience Tag。

“User not authorized”

用户通过 Access 认证但 autoProvisionfalse 且在 EmDash 中不存在。可以:

  • 设置 autoProvision: true,或
  • 在用户登录前手动创建用户