oauth について
oauthは、Val Townのstd/oauthミドルウェアでHonoアプリにVal Townアカウントのログインを追加する方法を説明するスキルです。アプリ側でプロバイダーやセッションストアを設定せず、ログイン、ログアウト、セッションを管理できます。
主な機能
- 01OAuthミドルウェアでHono fetchハンドラーをラップ
- 02ログイン、コールバック、ログアウトのルートを追加
- 03認証済みユーザーのセッション情報を取得
- 04暗号化Cookieにセッションを保存
活用シーン
- 01Honoアプリに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 スキルをご覧ください。1 つの val に両方を適用しないでください。アクセス制限された val で oauthMiddleware も実行すると、訪問者は 2 回認証することになります。
インポート
import {
getOAuthUserData,
oauthMiddleware,
} from "https://esm.town/v/std/oauth/middleware.ts";
アプリのラップ
oauthMiddleware(handler) は Hono の fetch ハンドラーを受け取り、3 つの自動管理ルートを追加したラップ済みハンドラーを返します。
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 を共有して、ユーザーにログインを試してもらってください。