关于 oauth
oauth 技能介绍 Val Town 的 std/oauth 中间件,用于为 Hono 应用添加 Val Town 账户登录。它说明如何自动管理登录、登出和会话,无需在应用中配置提供方或会话存储。
主要功能
- 01用 OAuth 中间件封装 Hono fetch 处理器
- 02添加托管的登录、回调和登出路由
- 03读取已认证用户的会话数据
- 04在加密 Cookie 中保存会话
适用场景
- 01为 Hono 应用添加 Val Town 账户登录
- 02根据当前会话限制路由访问
- 03验证未认证请求的重定向或 401 响应
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ val-town/ plugins --skill oauth安装后在 Claude Code 中输入/oauth确认已加载。
详细介绍
OAuth (std/oauth)
Val Town 通过 std/oauth 提供零配置的“使用 Val Town 登录”。无需设置数据库或配置提供方,只需封装 Hono fetch 处理函数,即可免费获得登录、注销和会话管理功能。会话存储在加密 Cookie 中,有效期为 30 天。
这仅适用于 Val Town 账户登录。如需使用 Google / GitHub / Slack / 等服务的 OAuth,请参阅 third-party-integrations 技能,其中按服务记录了相应流程。
如果目标是让团队内部使用应用,而不是让应用拥有自己的登录用户,那么限制该 val 的应用访问权限是更简单的方案——平台会在代码运行前拦截端点,无需编写任何身份验证代码。请参阅 restricted-access 技能。不要在同一个 val 上同时使用两者:同时受限且运行 oauthMiddleware 的 val 会让访客进行两次身份验证。
导入
import {
getOAuthUserData,
oauthMiddleware,
} from "https://esm.town/v/std/oauth/middleware.ts";
封装应用
oauthMiddleware(handler) 接收 Hono fetch 处理函数,并返回一个封装后的处理函数,该函数会注入三个自动管理的路由:
GET /auth/login— 启动登录流程GET /auth/callback— 完成登录流程POST /auth/logout— 清除会话
将封装后的处理函数导出为 val 的默认导出:
import { Hono } from "npm:hono";
import { oauthMiddleware } from "https://esm.town/v/std/oauth/middleware.ts";
const app = new Hono();
app.onError((err) => Promise.reject(err));
app.get("/", (c) => c.text("hello"));
export default oauthMiddleware(app.fetch);
无需自行编写 /auth/* 路由——中间件会添加这些路由。不要在自己的应用中覆盖它们。
读取当前用户
可在任意路由中调用 getOAuthUserData(rawRequest)。在 Hono 中,rawRequest 是 c.req.raw。如果请求已通过身份验证,它会返回会话数据;否则返回 null。
interface SessionData {
user: {
id: string;
username: string | null;
email: string | null;
bio: string | null;
tier: "free" | "pro" | null;
type: "user" | "org";
url: string;
links: {
self: string;
profileImageUrl: string | null;
};
};
accessToken: string; // Val Town API token (act on behalf of the user)
refreshToken?: string;
idToken?: string;
expiresAt: number; // Unix timestamp (ms)
isOrgMember?: boolean; // true if user belongs to this val's org
}
app.get("/", async (c) => {
const session = await getOAuthUserData(c.req.raw);
if (session?.user) {
return c.html(
`<p>Logged in as ${session.user.username}</p>` +
`<form method="POST" action="/auth/logout"><button>Log out</button></form>`
);
}
return c.html(`<a href="/auth/login">Log in with Val Town</a>`);
});
限制路由访问
没有内置的“要求登录”辅助工具——可通过检查 getOAuthUserData 来限制路由访问,并在会话缺失时返回 401 或重定向到 /auth/login:
app.get("/dashboard", async (c) => {
const session = await getOAuthUserData(c.req.raw);
if (!session?.user) return c.redirect("/auth/login");
return c.html(`<h1>Welcome ${session.user.username}</h1>`);
});
无需配置的内容
- 无需环境变量——凭据和重定向 URL 均由平台处理。
- 无需设置回调 URL——
/auth/callback会自动配置。 - 无需会话存储——会话保存在加密 Cookie 中。
验证更改
添加 OAuth 后,对受限路由调用 fetch_val_endpoint,确认未通过身份验证时会触发重定向或返回 401。完整登录流程需要真实的浏览器会话,无法仅通过 fetch_val_endpoint 测试——分享线上 URL,并请用户尝试登录。