关于 restricted-access
这是用于配置 Val Town val HTTP 端点受限访问的技能,说明组织授权、机器绕过令牌和访问者身份,并区分代码隐私与端点访问设置。
主要功能
- 01区分代码与端点隐私
- 02配置端点访问权限
- 03授予或撤销组织访问权
- 04管理自动化绕过令牌
- 05在服务端解析访问者身份
适用场景
- 01将内部工具限制为组织访问
- 02用绕过令牌允许 Webhook 请求
- 03排查受限端点的重定向
- 04识别已登录的人类访问者
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ val-town/ plugins --skill restricted-access安装后在 Claude Code 中输入/restricted-access确认已加载。
详细介绍
受限应用访问
一个 val 有两项相互独立的访问设置。更改其中一项不会影响另一项:
- 代码(
privacy:public/unlisted/private)——谁可以在 val.town 上查看源代码。 - 应用访问(
httpPrivacy:public/restricted)——谁可以调用 val 的 HTTP 端点。
一个 val 可以有私有代码,但端点完全开放;也可以代码公开,但端点受到限制。update_val 的 privacy 字段只会更改第一项;应用访问则通过 set_http_privacy 更改。
已启用此功能的组织可以使用受限应用访问。在此类组织中创建的 val 可能默认设为 restricted——请始终从 get_val_detail、list_vals、create_val 或 remix_val 的响应中读取 httpPrivacy,不要假设新 val 的 URL 是开放的。
这是不是合适的工具?
有两种不同的做法,听起来都像是“让我的应用要求登录”:
- 受限应用访问(本技能)在平台边缘拦截端点,在你的代码运行前完成。没有访问权限的请求永远不会到达 val。你无需编写任何身份验证代码。访问权限授予整个组织,而非个人。
std/oauth(参见oauth技能)在你的 val 内部运行:你包装处理程序,任何拥有 Val Town 账户的人都可以登录。你可以控制会话,并构建面向每个用户的功能。
如果是只有团队成员才能访问的内部工具,请选择受限访问。如果任何 Val Town 用户都可以登录,且应用需要定义自己的登录用户概念,请选择 std/oauth。
不要误将两者叠加使用。在已受限的 val 中添加 oauthMiddleware,意味着访客要进行两次身份验证——先通过访问关卡,再在你的代码中验证。如果受限 val 需要知道谁在查看,请使用下方的身份标头,而不是添加 OAuth。
谁可以通过
访问权限授予组织,而非个人。只有当 val 已获准某个组织访问,且访客是该组织的成员时,访客才能进入。移除其中任一项授权,访问权限会在下一次请求时立即撤销——不会在会话期间缓存。
授权来源包括:
- 直接授权——
add_allowed_user为组织授予访问权限,list_allowed_users显示当前授权,remove_allowed_user撤销授权。 - 域名规则——val 可以允许所有使用指定域名电子邮件地址的人访问。这些规则会与直接授权一起显示在
list_allowed_users中。 - 邀请——可以通过电子邮件邀请获授权组织以外的人;对方接受邀请后即可访问。
- 绕过令牌——供机器使用、限定于特定项目的密钥;见下文。
其他人会看到什么
未经身份验证的请求不会到达 val。平台会返回 302,将请求重定向到 Val Town 登录或授权页面。这是调试受限 val 时最常见的困惑来源:
fetch_val_endpoint会报告重定向,但不会跟随它。curl显示的是指向val.town的302,而不是你的响应。- API 客户端收到的是登录页面的 HTML,而不是预期的 JSON。
- 无访问权限但已登录的访客会收到
403,说明他们需要访问其所在的组织。
这些都不意味着 val 的代码有问题。请先检查 httpPrivacy——如果它是 restricted,说明访问关卡正在正常工作。你可以通过 set_http_privacy 将 val 设为公开、授权调用方所在的组织,或使用绕过令牌。
自动化和 Webhook
机器无法完成登录重定向,因此接收 Webhook(Stripe、GitHub、另一个 val 中的定时任务)的受限 val 需要一个绕过令牌——其密钥仅限该 val 使用。
使用 create_bypass_token 创建令牌;密钥只会显示一次,之后无法再次获取。使用 list_bypass_tokens 管理令牌,使用 revoke_bypass_token 撤销令牌。
任选以下一种方式传递:
// Header (preferred — keeps the secret out of logs and referrers)
await fetch(url, { headers: { "X-Val-Town-Access": Deno.env.get("MY_BYPASS_TOKEN")! } });
// Query param (for services that only accept a URL, e.g. some webhook configs)
await fetch(`${url}?val_town_access=${Deno.env.get("MY_BYPASS_TOKEN")}`);
平台会在处理程序运行前移除标头和查询参数,因此你的代码不会看到它们。使用绕过令牌的请求不携带任何访客身份——它是匿名的机器调用者。
识别访问者
对于通过入口访问的用户,平台会转发一个短期有效的签名 X-Val-Town-User 标头。它本身并非用户身份信息——请使用 val 自己的 API 令牌,通过 Val Town 的 API 查询访问者的个人资料;Val Town 会将该令牌注入 valtown 环境变量:
const IDENTITY_HEADER = "X-Val-Town-User";
/** Returns the viewer's public profile, or null when there isn't one. */
async function getViewer(req: Request) {
const signed = req.headers.get(IDENTITY_HEADER);
if (!signed) return null;
const res = await fetch("https://api.val.town/v3/val/viewer", {
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
[IDENTITY_HEADER]: signed,
},
});
if (!res.ok) return null;
// { id, username, type, bio, profileImageUrl, url, links }
return await res.json();
}
需要注意的规则:
- 始终将访问者视为可选项。 公开 val 或使用旁路令牌的请求都不会提供访问者。切勿使用
!断言其存在,也不要对空结果进行索引。 - 每次请求都在服务端解析访问者身份。 凭据有效期很短,且与您的 val 绑定——不要持久化、传递给浏览器,也不要尝试在客户端代码中查询。
- 返回的只有公开个人资料字段——用户名、简介、头像、个人资料 URL。不包括电子邮件地址和计费等级。不要构建依赖这些信息的 val。
上述传输方式(X-Val-Town-User 加上 /v3/val/viewer 查询)是目前的实现方式,之后可能会变;但这三条规则始终适用。
管理应用访问权限
| 任务 | 工具 |
|---|---|
| 检查当前设置 | get_val_detail(httpPrivacy 字段) |
| 将端点设为公开或受限 | set_http_privacy |
| 查看有权访问的用户 | list_allowed_users |
| 授予 / 撤销组织权限 | add_allowed_user / remove_allowed_user |
| 创建 / 列出 / 撤销自动化密钥 | create_bypass_token / list_bypass_tokens / revoke_bypass_token |
受限 val 只能由 val.town 在 iframe 中嵌入,因此无论登录用户是谁,将其嵌入外部网站都会被浏览器拦截。