統合ID基盤(IdP)モノレポ — Google OAuth ラッパー + サービス管理基盤
Hono + Cloudflare Workers + D1 で構築した軽量な認証基盤です。
id.0g0.xyz — IdP コアAPI(認証・JWT・サービス管理)
user.0g0.xyz — ユーザー向け画面(ログイン・プロフィール)
admin.0g0.xyz — 管理画面(サービス管理・ユーザー管理)
mcp.0g0.xyz — MCP Worker(Claude Code連携)
0g0-id/
├── packages/shared/ # 共通ライブラリ(型定義・JWT・DB操作)
├── workers/
│ ├── id/ # IdP コアAPI
│ ├── user/ # ユーザー向け BFF + Astro MPA
│ │ └── frontend/ # Astro フロントエンド
│ ├── admin/ # 管理画面 BFF + Astro MPA
│ │ └── frontend/ # Astro フロントエンド
│ └── mcp/ # MCP Worker
└── migrations/ # D1 スキーマ
- Hono — Webフレームワーク(API・BFF)
- Astro — フロントエンド静的MPA(user/admin)
- Tailwind CSS v4 — スタイリング
- Cloudflare Workers — エッジランタイム
- Cloudflare Workers Assets — 静的アセット配信(SPA fallback:
not_found_handling = "single-page-application") - Cloudflare D1 — SQLiteベースDB
- jose — ES256 JWT(WebCrypto)
- Service Bindings — Worker間通信(ネットワーク経由なし)
| 項目 | 実装 |
|---|---|
| JWT署名 | ES256 + JWKS(kid 付き) |
| OAuth | state + PKCE (S256) 必須 |
| Cookie | __Host- prefix / HttpOnly / Secure / SameSite=Lax |
| CSRF | Origin ヘッダー検証 |
| リフレッシュトークン | ローテーション + reuse detection(family 全失効) |
| DBSC | Device Bound Session Credentials(端末公開鍵バインド + チャレンジ・レスポンス) |
| 管理者権限 | DB ロールベース + 初回ブートストラップ |
共有 logger は任意の例外・extra を出力せず、固定イベントと検証済みの HTTP / cleanup metadata だけを記録します。動的メッセージや他のログは対象外です。 互換性への影響と残る制限は Error log privacy を参照してください。
- Node.js: v22.23.4 以上(両 Astro frontend の Prism 依存を含む開発環境の要件。CI は v22.x)
- npm: v11.x 以上(
packageManagerフィールドでnpm@11.21.2に固定)
npm installwrangler d1 create 0g0-id-db
# 出力された database_id を workers/id/wrangler.toml に設定# ローカル D1 に全マイグレーション適用
cd workers/id
npx wrangler d1 migrations apply 0g0-id-db --local
# 本番 D1 に適用(push 前に必ず実行)
npm run migrate:id
⚠️ 新しいマイグレーションを追加して適用せずにデプロイするとD1_ERROR: no such columnが本番で発生します。
openssl ecparam -genkey -name prime256v1 -noout -out private.pem
openssl ec -in private.pem -pubout -out public.pemcd workers/id
npx wrangler secret put GOOGLE_CLIENT_ID
npx wrangler secret put GOOGLE_CLIENT_SECRET
npx wrangler secret put JWT_PRIVATE_KEY # private.pem の内容
npx wrangler secret put JWT_PUBLIC_KEY # public.pem の内容
npx wrangler secret put BOOTSTRAP_ADMIN_EMAIL
⚠️ 初回管理者で管理画面への新しいログインと管理者専用一覧の読み取りを確認したら、BOOTSTRAP_ADMIN_EMAILを削除してください。既存の管理者ロールは DB に保持されます。 削除の前提条件・即時デプロイへの注意・再投入の防止は 初回管理者の作成後 を参照してください。
cd workers/user/frontend && npm install && npm run build
cd workers/admin/frontend && npm install && npm run buildnpm run deploy:id
npm run deploy:user # フロントエンドビルド込み
npm run deploy:admin # フロントエンドビルド込み
npm run deploy:mcpnpm run dev:id # :8787
npm run dev:user # :8788
npm run dev:admin # :8789
npm run typecheck # 全ワークスペース型チェックcd workers/user/frontend && npm run dev # Astro dev server
cd workers/admin/frontend && npm run dev # Astro dev serverローカル開発時は workers/id/.dev.vars にシークレットを記載します(.gitignore 済み):
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
JWT_PRIVATE_KEY=...
JWT_PUBLIC_KEY=...
BOOTSTRAP_ADMIN_EMAIL=admin@example.com
user/admin Worker は Hono(API)+ Astro MPA(UI) のハイブリッド構成です。
workers/{user,admin}/src/— Hono API(認証・プロキシ)workers/{user,admin}/frontend/— Astro MPA(Astro static output)- ビルド成果物は
workers/{user,admin}/dist/に出力 - Cloudflare Workers Assets がSPA配信を担当
run_worker_firstで API/auth パスは Worker に、静的アセットは Assets に振り分けnot_found_handling = "single-page-application"で未知のパスはindex.htmlにフォールバック
ブラウザ → user.0g0.xyz/auth/login
→ state + PKCE 生成 → Cookie 保存
→ id.0g0.xyz/auth/login → Google 認可画面
→ id.0g0.xyz/auth/callback(Google コード受取)
→ ワンタイム認可コード発行
→ user.0g0.xyz/auth/callback
→ id への内部 API(Service Bindings)でコード交換
→ __Host- Cookie にトークン設定
外部サービスが /auth/login を呼び出す場合は client_id が必須です。
ブラウザ → https://id.0g0.xyz/auth/login
?client_id=<CLIENT_ID>
&redirect_to=<登録済みリダイレクトURI>
&state=<STATE>
→ Google 認可画面
→ id.0g0.xyz/auth/callback
→ 認可コードを redirect_to に付与してリダイレクト
→ 外部サービスが /auth/exchange でトークン交換
| 呼び出し元 | client_id | 動作 |
|---|---|---|
| BFF オリジン(user.0g0.xyz, admin.0g0.xyz, EXTRA_BFF_ORIGINS) | 省略可 | BFF フローで処理 |
| 外部サービス(非 BFF オリジン) | 必須 | 省略すると 400 エラー |
client_idなしで外部オリジンから呼び出すと400 Bad Request—"client_id is required for external services"client_idなしでログインするとユーザーとサービスの関係が記録されず、/api/users/me/connections(連携サービス一覧)に表示されない
/.well-known/jwks.json で ES256 公開鍵を公開しています。各サービスはこの JWKS を取得してローカルで JWT を検証できます。
詳細なエンドポイント一覧は .claude/rules/api.md を参照してください。
MCP 対応クライアント(Claude Code 等)から管理操作を実行するためのサーバーです。
- エンドポイント:
https://mcp.0g0.xyz/mcp - 認証: Bearer Token (ES256 JWT) + 管理者ロール必須
ツール一覧・認証方式・セットアップ方法は docs/mcp-server.md を参照してください。
BFF セッションに端末の公開鍵を結びつけ、チャレンジ・レスポンスで端末所有を証明する機構です。 機密操作(アカウント削除・SNS 連携解除等)をデバイスバインド済みセッションに限定できます。
- Phase 1: デバイス公開鍵のバインド(ES256 JWK)
- Phase 2: チャレンジ・レスポンス(nonce 発行 → Proof JWT 検証)
- Phase 3: 機密操作の DBSC 必須化(
DBSC_ENFORCE_SENSITIVEで制御)
詳細は docs/dbsc.md を参照。
npm run deploy:{id,user,admin} は wrangler deploy の前に preflight スクリプトを実行し、必須シークレットの登録漏れを検出する。
| Worker | 検査対象シークレット |
|---|---|
| id | INTERNAL_SERVICE_SECRET_USER, INTERNAL_SERVICE_SECRET_ADMIN |
| user | DBSC_ENFORCE_SENSITIVE |
| admin | DBSC_ENFORCE_SENSITIVE |
ローカルでは warn のみ(fail-open)。CI では PREFLIGHT_STRICT=1 を設定して必須化を推奨。
docs/cloudflare-github-setup.md を参照してください。
開発フロー・コミット規約・PR レビュー観点は CONTRIBUTING.md を参照してください。
連携サービス画面の「コピー」で端末のクリップボードを使えない場合は、既に表示している同じ Sub ID の読み取り専用欄と手動コピー案内を表示します。追加のアクセス権は要求しません。失敗を成功とは表示せず、自動再試行やサービス解除はしません。詳しくは コピー復旧手順 を参照してください。
保存ボタンを押した後も入力できます。先に送った内容の保存が完了しても、その間に編集した値は消しません。保存済みの名前・画像と未保存の入力を区別して案内し、残った変更はもう一度「保存」で送れます。同じページ内の保護であり、別タブ・別端末の同時更新は調停しません。詳しくは プロフィールの復旧と保存 を参照してください。