EmDash 使用通行密鑰驗證作為其主要登入方式。通行密鑰具有抗網路釣魚能力,無需密碼,並可透過瀏覽器或密碼管理器在裝置間工作。
除了通行密鑰之外,您還可以新增可插拔的登入提供商 — GitHub、Google 和 Atmosphere(AT 協定)開箱即用,且相同的提供商介面對第三方套件開放。任何設定的提供商都可用於建立第一個管理員帳戶、登入或連結到現有使用者。
對於 Cloudflare 部署,Cloudflare Access 也可作為接管整個登入流程的獨占驗證方法。
運作原理
通行密鑰使用 WebAuthn,這是一個建立公鑰憑證的 Web 標準,憑證儲存在您的裝置上或透過密碼管理器同步。當您登入時,您的裝置證明擁有憑證,而無需透過網路傳送密碼。
通行密鑰驗證的優勢:
- 無需記住或洩露密碼
- 抗網路釣魚 — 憑證繫結到您網站的網域
- 跨裝置同步 — 可與 iCloud 鑰匙圈、Google 密碼管理器、1Password 等搭配使用
- 快速登入 — 使用生物辨識或 PIN 一鍵登入
首次使用者設定
首次存取管理面板時,設定精靈將引導您建立管理員帳戶。
-
導覽至
http://localhost:4321/_emdash/admin -
您將被重新導向到設定精靈。輸入:
- 網站標題 — 您網站的名稱
- 標語 — 簡短描述
- 管理員信箱 — 您的信箱地址
-
點擊建立網站以註冊您的通行密鑰
-
您的瀏覽器將提示您建立通行密鑰:
- macOS:Touch ID、裝置密碼或安全金鑰
- Windows:Windows Hello 或安全金鑰
- 行動裝置:Face ID、指紋或 PIN
-
註冊通行密鑰後,您將登入並重新導向到管理儀表板。
登入
設定完成後,返回管理面板將觸發通行密鑰驗證:
-
造訪
/_emdash/admin -
如果未登入,您將看到登入頁面
-
點擊登入進行驗證
-
您的瀏覽器會提示您提供通行密鑰(生物辨識、PIN 或安全金鑰)
-
驗證後,您將被重新導向到管理儀表板
魔法連結備用方案
如果您無法使用通行密鑰(例如,裝置遺失),魔法連結提供了替代方案。這需要設定信箱。
-
在登入頁面上,點擊使用信箱登入
-
輸入您的信箱地址
-
檢查收件匣中的登入連結
-
點擊連結進行驗證(有效期 15 分鐘)
登入提供商
除了通行密鑰之外,EmDash 還支援可插拔的登入提供商,它們會出現在登入頁面和設定精靈中。GitHub、Google 和 Atmosphere 提供商開箱即用;第三方套件可以使用相同的介面註冊自己的提供商。
提供商是累加的 — 啟用提供商時通行密鑰繼續工作,使用者可以將提供商連結到現有的僅通行密鑰帳戶。第一個使用者也可以透過任何設定的提供商建立,因此如果您願意,全新安裝可以完全跳過通行密鑰。
設定提供商
將提供商傳遞給 EmDash 整合上的 authProviders 陣列。以下範例啟用 GitHub、Google 和 Atmosphere:
import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";
export default defineConfig({
integrations: [
emdash({
authProviders: [github(), google(), atproto()],
}),
],
});
對於登入頁面,順序很重要:提供商按您列出的順序渲染,緊湊的純按鈕提供商首先顯示,需要自訂表單的提供商(如要求提供使用者名稱的 Atmosphere)隨後顯示。
GitHub
以下範例啟用 GitHub 提供商:
import { github } from "emdash/auth/providers/github";
emdash({ authProviders: [github()] });
透過環境變數設定憑證。EmDash 首先檢查帶前綴的名稱,然後回退到不帶前綴的名稱:
| 變數 | 用途 |
|---|---|
EMDASH_OAUTH_GITHUB_CLIENT_ID / GITHUB_CLIENT_ID | OAuth 應用程式用戶端 ID |
EMDASH_OAUTH_GITHUB_CLIENT_SECRET / GITHUB_CLIENT_SECRET | OAuth 應用程式密鑰 |
將您的 GitHub OAuth 應用程式的回呼 URL 設定為 https://your-site.example.com/_emdash/api/auth/oauth/github/callback。
以下範例啟用 Google 提供商:
import { google } from "emdash/auth/providers/google";
emdash({ authProviders: [google()] });
透過環境變數設定憑證。EmDash 首先檢查帶前綴的名稱,然後回退到不帶前綴的名稱:
| 變數 | 用途 |
|---|---|
EMDASH_OAUTH_GOOGLE_CLIENT_ID / GOOGLE_CLIENT_ID | OAuth 應用程式用戶端 ID |
EMDASH_OAUTH_GOOGLE_CLIENT_SECRET / GOOGLE_CLIENT_SECRET | OAuth 應用程式密鑰 |
將您的 Google OAuth 用戶端的重新導向 URI 設定為 https://your-site.example.com/_emdash/api/auth/oauth/google/callback。
Atmosphere(AT 協定)
對於貢獻者已經擁有 Atmosphere 帳戶的網站 — Bluesky 和更廣泛的 AT 協定網路背後的使用者擁有的身分 — 安裝 Atmosphere 提供商:
pnpm add @emdash-cms/auth-atproto
以下範例啟用帶有使用者名稱白名單的 Atmosphere 提供商:
import { atproto } from "@emdash-cms/auth-atproto";
emdash({
authProviders: [
atproto({
allowedHandles: ["*.example.com"],
}),
],
});
無需用戶端密鑰或環境變數。有關使用者名稱/DID 白名單、角色對映以及 AT 協定 OAuth 設定檔所需的本地開發設定,請參閱 Atmosphere 登入指南。
建構您自己的提供商
提供商只是一個 AuthProviderDescriptor — 一個 id、一個人類可讀的標籤,以及管理端 React 元件、路由處理程序、公共路由前綴和儲存集合的任意組合。該形狀從 emdash 匯出:
import type { AuthProviderDescriptor } from "emdash";
export function myProvider(): AuthProviderDescriptor {
return {
id: "my-provider",
label: "My Provider",
adminEntry: "my-provider/admin", // exports LoginButton / LoginForm / SetupStep
routes: [
{ pattern: "/_emdash/api/auth/my-provider/login", entrypoint: "my-provider/routes/login.ts" },
{ pattern: "/_emdash/api/auth/my-provider/callback", entrypoint: "my-provider/routes/callback.ts" },
],
publicRoutes: ["/_emdash/api/auth/my-provider/"],
storage: {
sessions: {},
},
};
}
Atmosphere 套件(@emdash-cms/auth-atproto)是需要自訂登入表單、OAuth 路由處理程序和持久儲存的提供商的最完整的實際參考。
使用者角色
EmDash 使用基於角色的存取控制,共有五個級別:
| 角色 | 級別 | 描述 |
|---|---|---|
| Subscriber | 10 | 閱讀已發布內容(無草稿存取權限) |
| Contributor | 20 | 建立內容(需要核准才能發布) |
| Author | 30 | 建立/編輯/發布自己的內容 |
| Editor | 40 | 管理所有內容 |
| Admin | 50 | 完全存取權限,包括設定 |
每個角色都繼承所有較低級別的權限。第一個使用者始終建立為管理員。
訂閱者和草稿內容
訂閱者擁有 content:read 權限,因此可以向已驗證的讀者提供僅限會員的已發布內容。他們無法看到草稿、已排程項目、已刪除項目、修訂版本或預覽 URL — 這些由 content:read_drafts 保護,授予給貢獻者及以上角色。清單和取得端點對訂閱者透明地過濾為 status=published;僅編輯者檢視(/compare、/revisions、/trash、/preview-url)直接拒絕訂閱者請求。
邀請使用者
管理員可以透過管理面板邀請新使用者:
-
前往設定 > 使用者
-
點擊邀請使用者
-
輸入使用者的信箱並選擇角色
-
點擊傳送邀請
-
使用者會收到帶有邀請連結的郵件
-
他們點擊連結並註冊通行密鑰
邀請有效期為 7 天。管理員可以從使用者頁面重新傳送或撤銷邀請。
管理通行密鑰
使用者可以從帳戶設定中管理他們的通行密鑰:
- 新增通行密鑰 — 為備份或其他裝置註冊額外的通行密鑰
- 刪除通行密鑰 — 刪除您不再使用的通行密鑰
- 重新命名通行密鑰 — 為通行密鑰提供描述性名稱
每個使用者最多可以註冊 10 個通行密鑰。
允許群組無需邀請即可登入
要允許群組無需邀請每個使用者即可登入,請設定帶有白名單的登入提供商。Atmosphere 提供商接受 allowedHandles 和 allowedDIDs(參見 Atmosphere 登入);Cloudflare Access 配接器透過 autoProvision 和 roleMapping 從您的身分提供商佈建使用者。任何設定的提供商也可以建立初始管理員帳戶。
工作階段
工作階段使用安全的 HttpOnly、SameSite=Lax cookie,並具有滑動過期時間,持續 30 天 — 過期時間在活動時重設。
安全性說明
- 通行密鑰儲存為公鑰 — 私鑰永遠不會離開您的裝置
- 挑戰驗證可防止重播攻擊
- 速率限制可防止暴力破解(5 次嘗試/分鐘/IP)
- 工作階段是 HttpOnly、Secure、SameSite=Lax 以確保 cookie 安全
- 魔法連結權杖採用 SHA-256 雜湊 — 原始權杖永遠不會儲存
疑難排解
”未註冊通行密鑰”
如果您在登入時看到此錯誤,您的通行密鑰可能已從密碼管理器中刪除。請管理員向您傳送魔法連結或新邀請。
“通行密鑰驗證失敗”
這通常意味著通行密鑰是為不同的網域建立的。通行密鑰與網域繫結 — 為 localhost:4321 建立的通行密鑰在 example.com 上無法使用。為每個網域註冊新的通行密鑰。
“工作階段已過期”
工作階段預設持續 30 天,具有滑動過期時間。如果您意外登出,請清除 cookie 並重新登入。
遺失所有通行密鑰
如果您失去了對所有已註冊通行密鑰的存取權限:
- 請另一位管理員向您傳送魔法連結(需要信箱設定)
- 使用魔法連結登入
- 在帳戶設定中註冊新的通行密鑰
如果您是唯一的管理員且未設定信箱,則需要透過資料庫重設網站的驗證。
Cloudflare Access
部署到 Cloudflare 時,您可以使用 Cloudflare Access 作為您的驗證提供商,而不是通行密鑰。Access 使用您現有的身分提供商在邊緣處理驗證。
為什麼使用 Cloudflare Access
- 單一登入 — 使用者使用您公司的 IdP 進行驗證
- 集中存取控制 — 在 Cloudflare 儀表板中管理誰可以存取管理
- 無需管理通行密鑰 — 無需註冊或管理通行密鑰
- 基於群組的角色 — 自動將 IdP 群組對映到 EmDash 角色
設定
- 為您的 EmDash 網站建立 Cloudflare Access 應用程式
- 從應用程式設定中記下應用程式受眾(AUD)標籤
- 設定 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...", // From Access app settings
}),
}),
],
});
設定選項
| 選項 | 型別 | 預設值 | 描述 |
|---|---|---|---|
teamDomain | string | 必需 | 您的 Access 團隊網域(例如 myteam.cloudflareaccess.com) |
audience | string | 必需 | Access 設定中的應用程式受眾(AUD)標籤 |
autoProvision | boolean | true | 在首次 Access 登入時建立 EmDash 使用者 |
defaultRole | number | 30 | 不符合任何群組的使用者的角色(30 = Author) |
syncRoles | boolean | false | 根據 IdP 群組在每次登入時更新角色 |
roleMapping | object | — | 將 IdP 群組名稱對映到角色級別 |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | 受眾標籤的環境變數名稱(硬編碼的替代方案) |
角色對映
將您的 IdP 群組對映到 EmDash 角色:
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "abc123...",
roleMapping: {
Admins: 50, // Admin
"Content Editors": 40, // Editor
Writers: 30, // Author
},
defaultRole: 20, // 不屬於任何群組的使用者為 Contributor
}),
});
如果使用者屬於多個群組,則第一個符合的群組獲勝。存取網站的第一個使用者始終成為管理員,無論群組如何。
角色同步行為
預設情況下(syncRoles: false),使用者的角色在首次登入時設定,之後不會變更。這允許管理員在 EmDash 中手動調整角色。
如果您希望 IdP 群組具有權威性,請設定 syncRoles: true — 使用者的角色將根據其當前群組在每次登入時更新。
運作原理
- 使用者造訪
/_emdash/admin - Cloudflare Access 攔截並重新導向到您的 IdP
- 使用者進行驗證(SSO、MFA 等)
- Access 在請求中設定簽署的 JWT
- EmDash 驗證 JWT 並建立/驗證使用者
已停用的功能
啟用 Access 後,以下功能不可用:
- 登入頁面(
/_emdash/admin/login) - 通行密鑰註冊和管理
- OAuth 登入
- 魔法連結登入
- 自助註冊
- 使用者邀請
使用者管理完全透過您的 Cloudflare Access 原則完成。
疑難排解
”無 Access JWT”
請求在沒有 Access JWT 的情況下到達 EmDash。這意味著:
- Access 未設定為保護您的應用程式
- Access 原則與管理路由不符合
驗證您的 Access 應用程式涵蓋 /_emdash/admin/*。
“JWT 受眾不符合”
您設定中的 audience 與 JWT 不符合。仔細檢查 Access 應用程式設定中的應用程式受眾標籤。
“使用者未授權”
使用者透過 Access 驗證,但 autoProvision 為 false,並且他們在 EmDash 中不存在。要麼:
- 設定
autoProvision: true,要麼 - 在他們登入之前手動建立使用者