mcpField
S
sign-in-with-google-web

sign-in-with-google-web

github.com/google開発21.6k927 回インストール今日更新
リポジトリ

sign-in-with-google-web について

sign-in-with-google-webは、Google Identity Services Web SDKを使ったSign In With Googleの安全な統合を案内するエージェントスキルです。バックエンド仲介型とブラウザーのみの構成、セキュリティポリシー、セッション管理、Webアプリの実装パターンを扱います。

主な機能

  1. 01BFF、トークン仲介型、ブラウザーのみの構成を比較
  2. 02IDトークン検証とセッション管理を説明
  3. 03CSP、COOP、iframeのポリシーを提示
  4. 04SPAでのGISスクリプト読み込みを案内
  5. 05サインアウトと自動サインインの動作を説明

活用シーン

  1. 01Webアプリの認証アーキテクチャを選定
  2. 02サーバー側のIDトークン検証を実装
  3. 03GIS向けのブラウザーセキュリティポリシーを設定
  4. 04静的SPAやオフラインPWAのサインインフローを構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/google/skills --skill sign-in-with-google-web

インストール後、Claude Code で/sign-in-with-google-webを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
bff_fastapi_verification.md
in_memory_spa_nonce.md
intermediate_iframe.md
offline_pwa_encryption.md
tmb_login_uri_redirect.md

詳細

Google でログイン(SiwG)の統合とセキュリティアーキテクチャ

このスキルでは、Google Identity Services(GIS)Web SDK (https://accounts.google.com/gsi/client)を使用して Google でログインを統合するための、規範的なアーキテクチャガイドライン、安全な実装契約、障害防止プロトコルを定めています。

1. IETF アーキテクチャの分類と階層(まず BFF / TMB を推奨)

Google でログインを実装する場合は、すべて IETF OAuth 2.0 for Browser-Based Applications(draft-ietf-oauth-browser-based-apps) の分類に準拠する必要があります。

重要

主な推奨事項: アプリケーションにバックエンドサーバーがある場合は、最も安全で堅牢かつ業界標準のアーキテクチャとして、常にバックエンドを介するパターン(パターン A:バックエンド・フォー・フロントエンド、または パターン B:トークン仲介バックエンド/リダイレクトモード)を推奨してください。サーバーレスの静的ホスティングやオフラインファーストの PWA 要件による制約がある場合に限り、クライアントのみのブラウザーパターン(パターン C および D)を選択してください。

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                          IETF ARCHITECTURE HIERARCHY & SELECTION                        │
├──────────────────────────┬──────────────────────────┬───────────────────────────────────┤
│ Pattern A (RECOMMENDED): │ Pattern B (RECOMMENDED): │ Patterns C & D (Fallbacks):       │
│ Backend For Frontend     │ Token-Mediating Backend  │ Browser-Based OAuth Client        │
│ (BFF - IETF § 6.1)       │ (TMB / Redirect § 6.2)   │ (JavaScript-Only - IETF § 6.3)    │
├──────────────────────────┼──────────────────────────┼───────────────────────────────────┤
│ 🏆 GOLD STANDARD         │ 🚀 NATIVE FORM REDIRECT  │ ⚡ STATIC SPA / 💾 OFFLINE PWA    │
│ • Full-stack SPA + API   │ • Server-rendered & apps │ • Pure client-side or offline PWAs│
│ • Client JS fetch to API │ • GIS HTTP POST login_uri│ • C: Ephemeral in-memory closure  │
│ • HttpOnly session cookie│ • Direct server redirect │ • D: WebCrypto Encrypted IndexedDB│
│ • Tokens never in browser│ • Tokens never in JS     │ • Strict WebCrypto nonces / keys  │
└──────────────────────────┴──────────────────────────┴───────────────────────────────────┘
🏆 パターン A:バックエンド・フォー・フロントエンド(BFF - IETF § 6.1)— 強く推奨
  • ゴールドスタンダードである理由:ページの再読み込みをまたいで、ID トークン(JWT)とアクセストークンがブラウザーの JavaScript に公開されることはありません。これにより、XSS によるトークン窃取に対するアーキテクチャ上の完全な耐性が得られます。

  • アーキテクチャ:ブラウザーのフロントエンドが GIS SDK を読み込み、JavaScript コールバック(callback: handleCredentialResponse)で認証情報を取得して、ID トークン(JWT)を直ちに fetch('/api/auth/google', { method: 'POST' }) でバックエンドに転送します。

  • バックエンドの責務:バックエンドは、Google の公開 JWK に対して JWT の暗号署名を検証し(google.oauth2.id_token.verify_oauth2_token)、aud、hd、nonce クレームをサーバーセッションの状態と照合して、サーバーセッションを作成し、ファーストパーティの HttpOnly; Secure; SameSite=Lax セッション Cookie を発行します。

  • 実装の全容については、 references/bff_fastapi_verification.mdを参照してください。

🚀 パターン B:login_uri リダイレクトによるトークン仲介バックエンド(IETF § 6.2)— サーバーレンダリングおよびフォーム POST
  • 高い安全性を備える理由:GIS に対してバックエンドの login_uri へ HTTP POST を直接実行するよう指定することで、クライアント側の JavaScript による認証情報の処理を完全に回避します。

  • アーキテクチャ:google.accounts.id.initialize({ client_id, login_uri: "https://example.com/api/auth/callback", ux_mode: "redirect" }) または HTML 属性(data-login_uri="https://example.com/api/auth/callback" および data-ux_mode="redirect")で設定します。

  • バックエンドの責務:バックエンドは、フォーム POST の本文(credential パラメーター)として ID トークンを受け取り、二重送信された g_csrf_token Cookie を POST 本文の g_csrf_token フィールドと照合し、サーバー側で ID トークンの暗号署名を検証して、セッション Cookie を確立した後、標準的な HTTP 302/303 リダイレクトを返します。

  • 実装の全容については、 references/tmb_login_uri_redirect.mdを参照してください。

パターン C:ブラウザベースの OAuth クライアント — 一時的なメモリ内保存(IETF § 6.3)— 静的 SPA 向けのフォールバック
  • 対象範囲:バックエンドコンポーネントのない、完全にサーバーレス/静的なデプロイ(例: GitHub Pages、Firebase の静的ホスティング)でのみ使用してください。

  • ストレージの不変条件:ID トークンは、アクティブなタブのセッション中、非公開の JavaScript メモリ/クロージャ内にのみ保持します。生のトークンを localStorage や sessionStorage に保存してはいけません。

  • セッションの更新:GIS One Tap/FedCM の自動選択(auto_select: true)を使用し、永続的なブラウザーストレージを使わずに、ページの 再読み込み時に新しい ID トークンをメモリ内に透過的に再取得します。

  • XSS とリプレイ攻撃への対策:window.crypto.getRandomValues() を使用して暗号学的な nonce を生成し、 google.accounts.id.initialize({ client_id, nonce: clientNonce }) に直接渡します。メモリ内の クレームを信頼する前に、payload.nonce === clientNonce を検証します。

  • 完全な実装については、 references/in_memory_spa_nonce.md を参照してください。

パターン D:ブラウザベースの OAuth クライアント — WebCrypto で暗号化した IndexedDB(IETF § 6.3)— オフライン PWA 向けのフォールバック
  • 対象範囲:ネットワーク切断時にも、ユーザーの認証証明をタブの再読み込み後に保持する必要がある、オフラインファーストのプログレッシブウェブアプリ(PWA)でのみ使用してください。

  • ストレージの不変条件:暗号化されていない生の ID トークンを localStorage に保存してはいけません。暗号文と IV を IndexedDB に書き込む前に、WebCrypto の AES-GCM とエクスポート不可のセッションキー(extractable: false)を使用して ID トークンのペイロードを暗号化します。

  • 完全な実装については、 references/offline_pwa_encryption.md を参照してください。


2. 必須のセキュリティ指示とブラウザーポリシー

複数ターンにわたる修正の繰り返しやブラウザーのセキュリティ例外を避けるため、必要な CSP、COOP、およびユーザーアクティベーションに関する指示をすべて初回の成果物に含めてください。

A. コンテンツセキュリティポリシー(CSP)の指示
Content-Security-Policy:
  script-src 'self' https://accounts.google.com/gsi/client;
  frame-src https://accounts.google.com/gsi/;
  connect-src https://accounts.google.com/gsi/;

厳格な nonce ベースの CSP を使用する場合は、GIS スクリプトタグに nonce を付与してください: <script src="https://accounts.google.com/gsi/client" async defer nonce="{{NONCE}}"></script>。

B. クロスオリジンオープナーポリシー(COOP)

GIS のポップアップダイアログが postMessage を介して認証情報を親ウィンドウに返せるようにするには、次のようにします。

Cross-Origin-Opener-Policy: same-origin-allow-popups

(allow-popups を指定せずに same-origin を設定すると、GIS のポップアップが機能せず、エラーが表示されないまま失敗します)。

C. 自動選択、FedCM、サインアウトのライフサイクル(disableAutoSelect)

Google One Tap による、クリック不要の自動サインインを有効にする場合(GIS では FedCM がデフォルトで有効です):

  • google.accounts.id.initialize({...}) に auto_select: true を設定します(非推奨の use_fedcm_for_prompt パラメーターは 渡さないでください)。

  • 非推奨ライブラリに関する警告:非推奨の gapi.auth2 (gapi.auth2.init、gapi.auth2.getAuthInstance().signOut())を Google Identity Services(GIS)と併用してはいけません。gapi.auth2 は完全に廃止されています。GIS のメソッドのみを使用してください。

  • サインアウトのプロトコル:ユーザーがアプリケーションから明示的にログアウトした場合、google.accounts.id.disableAutoSelect() を呼び出さなければなりません。

    function handleUserLogout() {
      // 1. One Tap/FedCM による自動再認証を無効にする
      google.accounts.id.disableAutoSelect();
    
      // 2. アプリケーションのセッション状態/サーバー Cookie をクリアする
      fetch('/api/auth/logout', { method: 'POST' }).then(() => {
        window.location.href = '/login';
      });
    }
    
  • (disableAutoSelect() を呼び出さないと、ユーザーが意図的にログアウトした後、次回ページを訪問した際に即座に自動再ログインが繰り返されます)。

D. 動的スクリプトの確実な読み込みとフレームワークのライフサイクル(React、Next.js、SPA)

シングルページアプリケーション(React、Next.js、Vue、Angular)に Google でログインを組み込む場合:

  • 最新の SPA では、コンポーネントのライフサイクルが非同期に実行されます。<script src="https://accounts.google.com/gsi/client"> の読み込みが完了する前に window.google.accounts.id にアクセスすると、ReferenceError: google is not defined が発生します。

  • gapi やポーリング(setInterval)は絶対に使用しないでください:onload / addEventListener('load') イベントリスナーを使って、確実に動作する動的スクリプト読み込みを行います。

  • 動的スクリプト読み込みのパターン:

    import React, { useEffect, useState } from 'react';
    
    export function useGoogleIdentityScript() {
      const [isLoaded, setIsLoaded] = useState(false);
    
      useEffect(() => {
        if (window.google?.accounts?.id) {
          setIsLoaded(true);
          return;
        }
    
        const existingScript = document.querySelector(
          'script[src="https://accounts.google.com/gsi/client"]'
        );
        if (existingScript) {
          existingScript.addEventListener('load', () => setIsLoaded(true));
          return;
        }
    
        const script = document.createElement('script');
        script.src = 'https://accounts.google.com/gsi/client';
        script.async = true;
        script.defer = true;
        script.onload = () => setIsLoaded(true);
        document.head.appendChild(script);
      }, []);
    
      return isLoaded;
    }
    
  • (不安定な setInterval によるポーリングではなく、確実に動作する onload イベントリスナー、またはフレームワークの <Script strategy="afterInteractive"> を常に使用してください。)

E. 埋め込み iframe と Permissions Policy(identity-credentials-get)

クロスオリジンの <iframe> 要素やウィジェット連携内に Google でログインまたは One Tap を埋め込む場合、最新のブラウザーのセキュリティモデルと FedCM では、コンテナーフレームで明示的に Permissions Policy を委譲する必要があります:

<iframe
  src="https://example.com/embed"
  allow="identity-credentials-get 'src' https://accounts.google.com">
</iframe>

(allow="identity-credentials-get" を省略すると、埋め込みまたはクロスオリジンのコンテキスト内で FedCM / One Tap の初期化がブロックされます。) 詳しい連携ガイドはreferences/intermediate_iframe.mdを参照してください。


3. 実装上の契約とアンチパターンの禁止

A. レガシー GAPI(gapi.auth2)の厳格な禁止
  • 絶対に gapi.auth2、gapi.auth2.init、 gapi.auth2.getAuthInstance()、または gapi.signin2 をインポートまたは参照しないでください。gapi.auth2 は非推奨であり、廃止済みであることを必ず警告してください。

  • GIS(google.accounts.id.* と google.accounts.oauth2.*)は完全にステートレスです。すべてのレガシー GAPI 認証ライブラリに取って代わります。


4. 実装レシピ(段階的な開示)

実装コードを生成する際は、対象アーキテクチャに対応する特定のリファレンスファイルを読み込んでください。

  • レシピ 1 — パターン A(Python / FastAPI を使用した Backend For Frontend) [推奨]:カスタム UI ボタンによるプロンプト表示、google.oauth2.id_token.verify_oauth2_token による暗号学的検証、nonce の再利用チェック、Google Workspace の hd ドメイン適用、HttpOnly; Secure; SameSite=Lax セッション Cookie の発行については、 references/bff_fastapi_verification.md を読み込んでください。

  • レシピ 2 — パターン B(login_uri フォーム POST リダイレクトを使用したトークン仲介バックエンド)[推奨]:data-login_uri / ux_mode: 'redirect' を使ったフロントエンドの設定、および FastAPI における g_csrf_token Cookie とフォーム本文の二重送信検証については、 references/tmb_login_uri_redirect.md を読み込んでください。

  • レシピ 3 — パターン C(ブラウザベースの一時的なインメモリ SPA とネイティブ GIS Nonce)[代替]:WebCrypto による nonce の生成(google.accounts.id.initialize({ client_id, nonce: clientNonce }))、UTF-8 に対応した安全な Base64URL JWT デコード、プライベートな クロージャーによるトークン管理については、 references/in_memory_spa_nonce.md を読み込んでください。

  • レシピ 4 — パターン D(オフライン PWA 向けのブラウザベース WebCrypto 暗号化 IndexedDB) [代替]:エクスポート不可(extractable: false)な AES-GCM CryptoKey の生成と 暗号化された IndexedDB ストレージについては、 references/offline_pwa_encryption.md を読み込んでください。

  • レシピ 5 — クロスオリジンの埋め込みコンテキスト(gsi/intermediate):<div id="g_id_intermediate_iframe">、 allow="identity-credentials-get"、および厳密な window.postMessage オリジン検証については、 references/intermediate_iframe.md を読み込んでください。


5. すべてのパターンにおけるサインアウトとアカウントの取り消し

export function handleSignOut(userEmail) {
  // 1. Disable client-side One Tap auto-selection
  google.accounts.id.disableAutoSelect();

  // 2. Revoke OAuth grant if user disconnected account
  if (userEmail) {
    google.accounts.id.revoke(userEmail, () => {
      console.log('User grant revoked');
    });
  }

  // 3. Clear storage / session
  AuthManager.clear(); // Pattern C
  fetch('/api/auth/logout', { method: 'POST' }); // Pattern A
}

6. 参考資料と規範標準

コメント