EmDash 使用通行密钥(Passkey)作为主要登录方式。通行密钥能抵御网络钓鱼、无需记忆密码,并可通过浏览器或密码管理器跨设备使用。
对于 Cloudflare 部署,可选择使用 Cloudflare Access 作为替代身份验证方式。
工作原理
通行密钥基于 WebAuthn 标准,创建存储在设备上或通过密码管理器同步的公钥凭据。登录时,设备证明持有凭据,而无需通过网络发送密码。
通行密钥的优势:
- 无需记忆或泄露密码
- 防钓鱼 — 凭据绑定到站点域名
- 跨设备同步 — 支持 iCloud 钥匙串、Google 密码管理器、1Password 等
- 快速登录 — 生物识别或 PIN 一键完成
首位用户设置
首次访问后台时,设置向导会引导你创建管理员账号。
-
打开
http://localhost:4321/_emdash/admin -
你会被重定向到设置向导,填写:
- Site Title — 站点名称
- Tagline — 简短描述
- Admin Email — 你的邮箱地址
-
点击 Create Site 注册通行密钥
-
浏览器会提示你创建通行密钥:
- macOS:Touch ID、设备密码或安全密钥
- Windows:Windows Hello 或安全密钥
- 移动端:Face ID、指纹或 PIN
-
通行密钥注册成功后,你会登录并跳转到管理仪表盘。
登录
设置完成后,再次访问后台会触发通行密钥认证:
-
访问
/_emdash/admin -
未登录时会看到登录页面
-
点击 Sign in 进行认证
-
浏览器提示使用通行密钥(生物识别、PIN 或安全密钥)
-
验证通过后跳转到管理仪表盘
魔法链接备选
无法使用通行密钥时(如设备丢失),可用魔法链接作为替代。需要先配置邮件。
-
在登录页点击 Sign in with email
-
输入邮箱地址
-
查收收件箱中的登录链接
-
点击链接完成认证(15 分钟内有效)
OAuth 登录
配置后 EmDash 支持 GitHub 和 Google 的 OAuth 登录。用户可在通行密钥设置完成后关联账号。
详见配置指南。
用户角色
EmDash 使用五级角色的访问控制:
| 角色 | 等级 | 说明 |
|---|---|---|
| Subscriber | 10 | 仅查看权限 |
| Contributor | 20 | 创建内容(需审核) |
| Author | 30 | 创建/编辑/发布自己的内容 |
| Editor | 40 | 管理所有内容 |
| Admin | 50 | 完全访问权限,含设置 |
每个角色继承所有更低级别的权限。首位用户始终创建为 Admin。
邀请用户
管理员可通过后台邀请新用户:
-
前往 Settings > Users
-
点击 Invite User
-
输入用户邮箱并选择角色
-
点击 Send Invite
-
用户会收到包含邀请链接的邮件
-
点击链接注册通行密钥
邀请 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 后重新登录。
丢失所有通行密钥
如果你丢失了所有已注册通行密钥的访问权限:
- 请另一位管理员发送魔法链接(需配置邮件)
- 使用魔法链接登录
- 在账号设置中注册新的通行密钥
如果你是唯一管理员且未配置邮件,需要通过数据库重置站点的身份验证。
Cloudflare Access
部署到 Cloudflare 时,可使用 Cloudflare Access 替代通行密钥。Access 利用你现有的身份提供商在边缘处理认证。
为何使用 Cloudflare Access?
- 单点登录 — 用户通过公司身份提供商认证
- 集中访问控制 — 在 Cloudflare 面板管理后台访问权限
- 无需通行密钥管理 — 无需注册或管理通行密钥
- 基于组的角色 — 自动将身份提供商组映射到 EmDash 角色
设置
- 为 EmDash 站点创建 Cloudflare Access 应用
- 在应用设置中记录 Application Audience (AUD) Tag
- 配置 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 应用设置
}),
}),
],
});
配置选项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
teamDomain | string | 必填 | Access 团队域名(如 myteam.cloudflareaccess.com) |
audience | string | 必填 | Access 设置中的 Application Audience (AUD) Tag |
autoProvision | boolean | true | 首次 Access 登录时自动创建 EmDash 用户 |
defaultRole | number | 30 | 未匹配任何组的用户角色(30 = Author) |
syncRoles | boolean | false | 每次登录时根据身份提供商组更新角色 |
roleMapping | object | — | 将身份提供商组名映射到角色等级 |
audienceEnvVar | string | "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 可让身份提供商组具有权威性 — 用户角色将在每次登录时根据当前所属组更新。
工作流程
- 用户访问
/_emdash/admin - Cloudflare Access 拦截并重定向到身份提供商
- 用户完成认证(SSO、MFA 等)
- Access 在请求中设置签名的 JWT
- 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 认证但 autoProvision 为 false 且在 EmDash 中不存在。可以:
- 设置
autoProvision: true,或 - 在用户登录前手动创建用户