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には、独立した2つのアクセス設定があります。一方を変更しても、もう一方は変わりません。
- コード(
privacy:public/unlisted/private)— val.town上のソースを誰が読めるかを設定します。 - アプリへのアクセス(
httpPrivacy:public/restricted)— valのHTTPエンドポイントを誰が呼び出せるかを設定します。
コードが非公開でもエンドポイントが広く公開されているvalや、コードは公開されていてもエンドポイントが制限されているvalを作成できます。update_valのprivacyフィールドで変更できるのは前者だけです。アプリへのアクセスはset_http_privacyで変更します。
制限付きアプリアクセスは、この機能が有効になっている組織で利用できます。そのような組織で作成されたvalは、デフォルトでrestrictedになる場合があります。新しいvalのURLが公開されていると思い込まず、必ずget_val_detail、list_vals、create_val、またはremix_valのレスポンスからhttpPrivacyを確認してください。
このツールは適切ですか?
「アプリへのログインを必須にする」という意味に聞こえるものが2つあります。
- 制限付きアプリアクセス(このスキル)は、コードが実行される前にプラットフォームのエッジでエンドポイントへのアクセスを制限します。アクセス権のないユーザーからのリクエストは、valに到達しません。認証コードを書く必要はありません。アクセス権は個人ではなく、組織全体に付与されます。
std/oauth(oauthスキルを参照)は、valの内部で動作します。ハンドラーをラップすると、Val Townのアカウントを持つユーザーなら誰でもログインできます。セッションを管理し、ユーザーごとの機能を構築できます。
チームだけが利用する内部ツールには、制限付きアクセスを選択してください。Val Townのユーザーなら誰でもサインインでき、アプリ独自のログインユーザーの概念が必要な場合は、std/oauthを選択してください。
誤って両方を重ねて使わないようにしてください。すでに制限されているvalにoauthMiddlewareを追加すると、訪問者はアクセスゲートとコード内の両方で認証する必要があります。制限付きvalで閲覧者の身元を確認する必要がある場合は、OAuthを追加せず、以下のIDヘッダーを使用してください。
アクセスできるユーザー
アクセス権は個人ではなく、組織に付与されます。valに組織へのアクセス許可があり、閲覧者がその組織のメンバーである場合に、閲覧者はアクセスできます。どちらか一方を削除すると、次のリクエストからアクセスが取り消されます。セッションの有効期間中にキャッシュされることはありません。
アクセス許可の付与方法は次のとおりです。
- 直接付与 —
add_allowed_userで組織へのアクセスを許可し、list_allowed_usersで現在の許可を確認し、remove_allowed_userで許可を取り消します。 - ドメインルール — 特定のドメインのメールアドレスを持つ全員にアクセスを許可できます。これらは直接付与とともに
list_allowed_usersに表示されます。 - 招待 — 許可された組織の外部にいる人をメールで招待できます。招待を承諾するとアクセスできるようになります。
- バイパストークン — マシン用のプロジェクトスコープのシークレットです。以下を参照してください。
それ以外の人に表示される内容
未認証のリクエストはvalに到達しません。プラットフォームは、Val Townのログインページまたは認証ページへの302リダイレクトを返します。これは、制限付きvalのデバッグ時に最もよくある混乱の原因です。
fetch_val_endpointは、追従しないリダイレクトを報告します。curlには、応答ではなくval.townへの302が表示されます。- APIクライアントが、JSONを期待していたところでログインページのHTMLを受け取ります。
- アクセス権のない訪問者がログイン済みの場合、組織へのアクセス権が必要であることを説明する
403が表示されます。
これらは、いずれもvalのコードが壊れていることを意味しません。まずhttpPrivacyを確認してください。restrictedなら、アクセスゲートは正常に機能しています。set_http_privacyでvalを公開するか、呼び出し元の組織にアクセスを許可するか、バイパストークンを使用してください。
自動化とWebhook
マシンはログインリダイレクトを完了できません。そのため、Webhook(Stripe、GitHub、別のvalのcronジョブなど)を受け取る制限付き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")}`);
プラットフォームはハンドラーの実行前にヘッダーとクエリーパラメーターを取り除くため、コードからこれらを参照することはできません。バイパストークンを使ったリクエストには、閲覧者のIDが含まれません。これは匿名のマシンからの呼び出しです。
閲覧者の識別
ゲートを通ってアクセスした人間の閲覧者には、プラットフォームから有効期間の短い署名付きの X-Val-Town-User ヘッダーが転送されます。これは本人確認情報そのものではありません。val 独自の 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 やバイパストークンを使ったリクエストには閲覧者がいません。
!で存在を断定したり、null の結果にインデックスでアクセスしたりしないでください。 - リクエストごとにサーバー側で解決してください。 認証情報の有効期間は短く、あなたの val に紐づいています。永続化したり、ブラウザーに渡したり、クライアント側のコードから照会したりしないでください。
- 返されるのは公開プロフィール情報のみです — ハンドル、自己紹介、アバター、プロフィール URL です。メールアドレスや請求プランは含まれません。これらに依存する val を作成しないでください。
上記の転送方法(X-Val-Town-User と /v3/val/viewer の照会)は現時点での仕組みであり、変更される可能性があります。ただし、3 つのルールは変わりません。
アプリへのアクセスを管理する
| タスク | ツール |
|---|---|
| 現在の設定を確認する | 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 を iframe に埋め込めるのは val.town のみです。そのため、外部サイトに埋め込むと、ログインしているユーザーにかかわらずブラウザーによってブロックされます。