本文面向为真实上线 Web 服务接入 Cloudflare Access Control 的 coding agent。它优先回答“生产服务该怎么接入、应用内权限怎么做、Dev Access 已打开时怎样验证”。Dev Access 的安装、配置和启动不在本文展开。
Cloudflare Access Control 提供的是域名级访问控制和可信身份输入;它不会规定业务应用必须有哪些接口、角色或权限模型。本文中的 /api/whoami、roles、permissions、reader/member/admin 等,是 Web 服务接入 Access 后推荐采用的应用层最佳实践,用于让前端获取当前用户状态、让后端统一做权限判断和审计。
如果现有系统已经有等价的 current user 接口、RBAC/ABAC 模型或审计体系,可以复用现有设计;关键要求是身份来自服务端可信入口,敏感操作由后端鉴权,前端不能自行声明用户或角色。
Cf-Access-Jwt-Assertion;只有在 origin 确认不能绕过 Cloudflare Access 边界时,才可以读取 Cf-Access-Authenticated-User-Email。/api/whoami 或等价 current user 接口获取后端返回的用户状态,再控制 UI 展示。Browser
-> Cloudflare Access 登录与策略校验
-> 受保护的生产域名 / staging 域名 / 可选临时测试域名
-> Web 应用 origin
-> 应用内 auth middleware
-> 业务路由权限判断
Access policy 适合表达“哪些邮箱、邮箱域名、IdP group 或 service token 能进站”。应用内 role / permission 适合表达 reader、member、admin 等产品权限,以及所有权判断。
origin 可以部署在正式服务器、容器平台、PaaS、内网服务,或测试期的 Dev Access 临时域名后面。部署形态不应该改变应用认证代码的核心契约:服务端从 Cloudflare Access 身份中解析当前用户,再进入自己的权限系统。
| 约定 | Agent 实现要求 |
|---|---|
| 邮箱规范化 | 读取 Cloudflare Access email 后统一 trim + lowercase。例如 Alice@Fstln.io 归一化为 alice@fstln.io。 |
| 真人用户默认域 | 默认真人来自 fstln.io。外部顾问或客户必须进入显式 allowlist,并单独分配应用角色。 |
| 业务主键 | 数据库使用自己的 users.id。email 用作唯一登录标识和审计展示字段,Cloudflare sub 仅作为辅助审计字段。 |
| 角色绑定 | 用完整邮箱绑定角色,例如 alice@fstln.io -> admin,不要只用邮箱前缀。 |
| 权限判断 | 后端优先判断 permission,role 只是权限集合名称。敏感 API 必须重复鉴权。 |
| 机器访问 | CI、Webhook、service token 不伪装成真人邮箱,单独设计机器身份和权限。 |
最小项目可以用两个邮箱列表,但新项目更推荐使用显式 roles -> permissions 配置,避免管理员是否继承成员权限这类隐式规则。
roles:
users:
alice@fstln.io:
- admin
bob@fstln.io:
- member
reader@fstln.io:
- reader
permissions:
reader:
- read
member:
- read
- upload
- delete_own
admin:
- read
- upload
- delete_own
- delete_any
- manage_users
- view_admin
只在 origin 无法绕过 Cloudflare Access 保护边界时,读取邮箱 header。这个边界可以由内网、防火墙、反向代理或平台网络策略提供;关键是外部用户不能直接伪造 header 打到 origin。
const raw = req.headers.get("cf-access-authenticated-user-email");
const email = raw?.trim().toLowerCase();
推荐验证 Cf-Access-Jwt-Assertion,至少校验签名、issuer、audience,以及时间类 claim。
CF_ACCESS_TEAM_DOMAIN=https://<team>.cloudflareaccess.com
CF_ACCESS_AUD=<access-application-aud-tag>
email、role、user_id。NODE_ENV=development 自动开启假登录;必须显式使用 AUTH_MODE=dev。| 目标 | 模式 | 命令或入口 | Agent 判断 |
|---|---|---|---|
| 日常功能开发、离线开发、快速切换角色 | AUTH_MODE=dev |
AUTH_MODE=dev DEV_AUTH_EMAIL=member@fstln.io npm run dev |
默认优先使用;它仍应走同一套后端 roles / permissions 解析逻辑。 |
| 正式上线或预发环境 | AUTH_MODE=cloudflare |
生产 / staging 域名绑定 Cloudflare Access application | 这是本文主路径。应用代码验证 Access 身份,然后使用自己的 roles / permissions 系统。 |
| 测试期通过 Dev Access 临时域名验收 | AUTH_MODE=cloudflare |
Dev Access 已 Running 后打开被测端口对应的 HTTPS URL | 只是部署和访问方式,方便在真实 Access 登录下测试尚未正式上线的代码;不是 Access Control 的必要组成部分。 |
| 自动化集成或 E2E 测试 | AUTH_MODE=test |
测试专用 X-Test-Auth-Email,仅 test mode 生效 |
测试身份只替代认证入口,不能绕过权限和所有权判断。 |
为真正上线的 Web 服务接入 Access Control 时,先定义正式域名和应用身份边界。origin 部署在哪里是独立问题;它可以是服务器、容器平台、PaaS、负载均衡后的服务,或其他受控网络入口。
| 配置项 | 约定 |
|---|---|
| Access application | 在 Cloudflare Zero Trust 中创建 Self-hosted application,绑定生产或预发域名,例如 app.example.com 或 staging.example.com。 |
| Access policy | 只表达谁可以进入这个域名,例如公司邮箱域名、指定邮箱、Google Workspace group、service token。 |
| Team domain | 用于后端 JWT 验签,例如 https://<your-team-name>.cloudflareaccess.com。 |
| AUD tag | 使用当前 Access application 的 Application Audience (AUD) Tag。每个 application 使用自己的 AUD。 |
| Origin 入口 | 按项目部署方案配置,可以是公开 origin、内网反向代理、平台入口或 Cloudflare Tunnel。这是部署路由问题,不是应用如何使用 Access Control 的逻辑。无论哪种方式,应用都应验证 JWT,或确保外部请求无法绕过 Access 直接到达 origin。 |
| 应用内权限 | 不要把业务权限都塞进 Access policy。后端根据 email / user id 映射 roles 和 permissions,并在敏感接口重复校验。 |
前提:Dev Access 已经按它自己的使用说明完成配置并处于 Running 状态。本文不说明如何安装、导入配置或启动 Dev Access,只说明 Web 服务接入 Access Control 后如何借它验收。
AUTH_MODE=cloudflare 启动被测 Web 服务,确保后端使用当前测试域名对应的 CF_ACCESS_TEAM_DOMAIN 和 CF_ACCESS_AUD。/api/whoami 或等价当前用户接口,确认返回的是 Cloudflare Access 身份解析出的 email、roles、permissions。Cf-Access-Jwt-Assertion 的签名、issuer、audience 和有效期。Cf-Access-Authenticated-User-Email header,必须先确认外部请求不能绕过 Access 直接访问 origin。AUTH_MODE=dev 和 AUTH_MODE=test 必须在生产环境失败或被禁用。/api/whoami 或等价 current user 接口返回当前用户状态,但不泄露 token、AUD、team secret 等配置。Alice@Fstln.io 与 alice@fstln.io 等价。AUTH_MODE=dev 和 AUTH_MODE=test 在生产环境启动失败。| 现象 | 优先检查 |
|---|---|
| Cloudflare Bad Gateway | 检查项目自己的 origin route、负载均衡、健康检查、防火墙或平台 ingress。Access 登录成功只说明用户通过了边缘策略,不代表 origin 一定可达。 |
直接访问 localhost 不跳 Google 登录 |
这是预期行为。Access 登录只在请求经过 Cloudflare 边缘和受保护 hostname 时发生。浏览器本地开发可用 AUTH_MODE=dev,真实登录验收要访问受 Access 保护的生产、预发或 Dev Access 测试域名。 |
| 登录成功但应用认为未登录 | 检查后端读取的 header 名、JWT 验签的 CF_ACCESS_TEAM_DOMAIN 和 CF_ACCESS_AUD,以及当前域名是否使用对应 Access application 的 AUD。 |
| 文档 | 只负责 | 不负责 |
|---|---|---|
install.html |
下载安装 macOS app、处理首次打开和系统安全提示。 | Access Control 代码接入、角色权限设计、业务应用测试。 |
dev-access.html |
导入 Dev Access 配置、启动 Dev Access、确认 Dev Access 自身 Running 和 test 页可用。 | 业务 Web 服务如何实现 Access middleware、roles、permissions、JWT 验签。 |
| 本文 | 真实 Web 服务如何接入 Cloudflare Access Control,以及 Dev Access 已打开时如何验证业务应用。 | 安装 Dev Access、导入配置、解释本机 proxy/tunnel 细节。 |
此文件描述 Web 服务接入 Cloudflare Access Control 的开发约定,不替代 Cloudflare 官方文档或管理员控制台配置。