Skip to content

feat: MCP サーバ用 OAuth2 scope と firewall を追加#190

Open
dotani1111 wants to merge 11 commits into
4.4from
feat/mcp-server-scorp
Open

feat: MCP サーバ用 OAuth2 scope と firewall を追加#190
dotani1111 wants to merge 11 commits into
4.4from
feat/mcp-server-scorp

Conversation

@dotani1111

Copy link
Copy Markdown
Contributor
  • scopes.available に mcp:product:read / mcp:order:read / mcp:customer:read / mcp:plugin:read を追加 (本体同梱の MCP サーバ機能の領域別 read scope。 GraphQL の read/write とは名前空間 mcp: で分離)
  • ApiExtension::prepend で ^//mcp 用の OAuth2 stateless firewall を admin の前に注入 (本体側 Tool の IsGranted と AND 評価で認可)
  • ApiExtensionTest: firewall 順序 / shape / 既存 admin/customer の csrf_token_generator・anonymous 削除を検証

- scopes.available に mcp:product:read / mcp:order:read / mcp:customer:read / mcp:plugin:read を追加
  (本体同梱の MCP サーバ機能の領域別 read scope。 GraphQL の read/write とは名前空間 mcp: で分離)
- ApiExtension::prepend で ^/<admin>/mcp 用の OAuth2 stateless firewall を admin の前に注入
  (本体側 Tool の IsGranted と AND 評価で認可)
- ApiExtensionTest: firewall 順序 / shape / 既存 admin/customer の csrf_token_generator・anonymous 削除を検証

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c94214dc-2201-40c9-8735-1f11b283b4eb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mcp-server-scorp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

dotani1111 and others added 8 commits June 17, 2026 09:13
削除リンク生成に使う admin_api_oauth_delete の identifier 制約が \w+ で
ハイフンを許さないため、 ハイフン入り client_id があると一覧の URL 生成で例外になる。
[\w-]+ に緩める。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MCP サーバは OAuth 自動探索メタデータを持たず、 店舗オーナーが自力でトークンを得る
手段が無かった。 league の AccessToken を再利用し、 管理画面で手動発行 (1 度だけ表示)
する PAT 型の UX を追加する。 認証・失効は既存 firewall の経路にそのまま乗る。

- OAuth 管理画面を「API 新規追加 / MCP 新規追加」の 2 ボタンに分離
- MCP 発行: ラベル + MCP scope (mcp:*:read) + 有効期限 (30/90/180/365日) → JWT を 1 度表示
- 一覧に MCP トークンを表示し失効ボタンを追加 (revoke → 即 401)
- 専用クライアント mcp_pat を PluginManager::enable で冪等生成し API 一覧からは除外
- 発行は scope/期限/発行者を防御的に検証し、 AccessToken model とメタを 1 トランザクションで
  永続化してから署名する (途中失敗で失効不能な生トークンを残さない)
- McpToken エンティティ (plg_api_mcp_token) で表示用メタ (ラベル/scope/期限/発行者) を保持

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 発行したトークンが /admin/mcp の firewall を通る (200)
- 失効すると同じトークンで 401
- mcp:*:read 以外の scope / プリセット外の有効期限は発行できない (フォームバイパス防御)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
テストが redirectUris を持たない client を拾い league の検証で 400 になっていた。
setUp で redirectUris/scope を持つ confidential client を生成して使う。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
手動トークンに代わり、 クライアントが 401→メタデータ→動的登録→管理ログイン+同意→
トークン取得 を自動で行えるようにする。 手動トークン方式は併存。

- 401 の WWW-Authenticate に RFC 9728 の resource_metadata ポインタ (mcp firewall の entry_point)
- GET /.well-known/oauth-protected-resource (RFC 9728) / oauth-authorization-server (RFC 8414) を公開
  (Host 詐称によるキャッシュ汚染を避けるため no-store)
- POST /register (RFC 7591 DCR): public client を作成。 redirect_uri は https/loopback のみ許可し、
  パーサ差異 (parse_url vs WHATWG) による host 詐称 (\ / @ / fragment) を fail-closed で拒否。
  grant=authorization_code+refresh_token・scope=mcp:*:read に固定、 IP+グローバルの 2 段レート制限、
  save 失敗と拒否を監査ログに記録
- 公開ルートは実在 2 well-known と /register に完全一致で限定 (security:false)
- 同意画面に MCP scope の説明文言を追加 (read/write と同様、 未翻訳キーの露出を解消)
- メタデータ/WWW-Authenticate を OAuthMetadataBuilder で一元生成 (canonical resource URI を一致)
- 本番は TRUSTED_HOSTS / TRUSTED_PROXIES の設定が前提

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- .well-known 2 本が公開で正しい JSON を返す
- /admin/mcp 401 が resource_metadata を広告する
- DCR は loopback redirect を受理し scope/grant を MCP read に固定
- 外部 http redirect・パーサ差異 (バックスラッシュ) redirect は拒否

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
eccube:plugin:enable の時点では league の doctrine persistence が未配線で、
Model\Client のメタデータが解決できず enable が MappingException で失敗する。
クライアント生成を実行時 (初回トークン発行時) の冪等生成に移し、 enable は
EC-CUBE エンティティのみ触るようにする。 grant=authorization_code・
scope=mcp:*:read 固定は維持。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /admin/mcp (本体同梱の route _mcp_endpoint) を叩く統合テストは、 プラグイン単体 CI に
  MCP サーバが無く実行不能のため削除。 トークン受理/失効は本体 mcp ジョブの
  McpTokenRevocationContractTest、 401 の resource_metadata 広告は McpFirewallContractTest が担う
- McpTokenControllerTest はプラグインが所有する発行ロジック (scope 限定・有効日数縛り) に絞る
- mcp_oauth_public firewall 追加で変わった順序・公開条件 (security:false/完全一致) と
  mcp の entry_point を ApiExtensionTest の期待値に反映

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dotani1111 dotani1111 force-pushed the feat/mcp-server-scorp branch from 8633afd to 9649345 Compare June 19, 2026 07:30
dotani1111 and others added 2 commits June 19, 2026 17:48
DCR (RFC 7591) で量産される放置クライアントを定期削除する。

- eccube:api:mcp:cleanup-dcr-clients [--days=30] [--dry-run]: 登録から grace 日数を
  過ぎ、 有効な access/refresh トークンを持たない DCR client を削除
- league の oauth2_client に作成時刻が無いため、 grace 判定用の作成時刻を側テーブル
  plg_api_dcr_client (DcrClient) で追跡し、 DCR 登録成功時に記録する
- 削除は 1 トランザクションで client と追跡レコードをまとめて消す (部分失敗での不整合を防ぐ)。
  client の access token は FK ON DELETE CASCADE、 孤立する refresh は明示削除する
- 掃除対象は追跡レコードのある client のみ (導入前/記録漏れは作成時刻が無く対象外)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mix-up 攻撃対策として、 認可応答 (oauth2_authorize の redirect) に発行元 issuer を示す
iss パラメータを付ける。 league は認可コード応答にフックが無いため kernel.response で
Location に後付けし、 issuer は AS メタデータと同一値 (OAuthMetadataBuilder::baseUrl) を使う。

- AuthorizationResponseIssListener: code/error を持つ認可 redirect にのみ iss を付与
  (ログイン redirect 等は対象外)
- メタデータの authorization_response_iss_parameter_supported を true 化

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant