サーバー側で Google ID トークンを確認する

Google Identity Services または OAuth 2.0 認可コードフローを使用する場合、Google は POST メソッドを使用してリダイレクト エンドポイントに ID トークンを返します。または、OIDC 暗黙的フローは GET リクエストを使用します。したがって、受信した認証情報をサーバーに安全に送信するのは、アプリケーションの責任となります。

GET

これは暗黙的フローです。ID トークンは URL フラグメント内で返され、クライアントサイドの JavaScript が解析する必要があります。アプリケーションは、リクエストの信頼性を確保し、CSRF などの攻撃を防ぐための独自の検証メカニズムを実装する必要があります。

    HTTP/1.1 302 Found
    Location: https://<REDIRECT_URI>#access_token=<ACCESS_TOKEN>&token_type=bearer&expires_in=<TIME_IN_SECONDS>&scope=<SCOPE>&state=<STATE_STRING>
    
POST

ID トークンは credential フィールドとして返されます。GIS ライブラリは、ID トークンをサーバーに送信する準備を行う際に、g_csrf_token をヘッダー Cookie とリクエスト本文に自動的に追加します。POST リクエストの例を次に示します。

POST /auth/token-verification HTTP/1.1
Host: example.com
Content-Type: application/json;charset=UTF-8
Cookie: g_csrf_token=<CSRF_TOKEN>
Origin: https://example.com
Content-Length: <LENGTH_OF_JSON_BODY>
    {
      "credential": "<ID_TOKEN>",
      "g_csrf_token": "<CSRF_TOKEN>",
      "client_id": "<CLIENT_ID>"
    }

  1. g_csrf_token を検証してクロスサイト リクエスト フォージェリ(CSRF)攻撃を防ぐ:

    • g_csrf_token Cookie から CSRF トークン値を抽出します。
    • リクエスト本文から CSRF トークン値を抽出します。GIS ライブラリは、このトークンを g_csrf_token という名前のパラメータとして POST リクエストの本文に含めます。
    • 2 つのトークン値を比較します。
      • 両方の値が存在し、完全に一致する場合、リクエストは正当なものであり、ドメインから発信されたものとみなされます。
      • 値が存在しない場合や一致しない場合、リクエストはサーバーによって拒否される必要があります。このチェックにより、リクエストが独自のドメインで実行されている JavaScript から開始されたことが保証されます。これは、独自のドメインのみが g_csrf_token Cookie にアクセスできるためです。
  2. ID トークンを検証します。

    トークンが有効であることを確認するには、次の条件が満たされていることを確認します。

    • ID トークンは Google によって適切に署名されています。トークンの署名を確認するには、Google の公開鍵(JWK 形式または PEM 形式で入手可能)を使用します。これらの鍵は定期的にローテーションされます。レスポンスの Cache-Control ヘッダーを調べて、再度取得するタイミングを確認してください。
    • ID トークンの aud の値が、アプリのクライアント ID のいずれかと等しい。このチェックは、悪意のあるアプリに発行された ID トークンが、アプリのバックエンド サーバーで同じユーザーのデータにアクセスするために使用されるのを防ぐために必要です。
    • ID トークンの iss の値が accounts.google.com または https://accounts.google.com と等しい。
    • ID トークンの有効期限(exp)が切れていない。
    • ID トークンが Google Workspace または Cloud 組織アカウントを表していることを検証する必要がある場合は、ユーザーのホストドメインを示す hd クレームを確認します。これは、リソースへのアクセスを特定のドメインのメンバーのみに制限する場合に使用する必要があります。このクレームがない場合、アカウントが Google ホスト型ドメインに属していないことを示します。

    emailemail_verifiedhd の各フィールドを使用して、Google がメールアドレスをホストし、そのメールアドレスの権限を持っているかどうかを判断できます。Google が権限を持つ場合、ユーザーが正当なアカウント所有者であることがわかっているため、パスワードなどのチャレンジ方法をスキップできます。

    Google が信頼できる情報源となるケース:

    • email@gmail.com という接尾辞が付いている場合、これは Gmail アカウントです。
    • email_verified が true で hd が設定されている場合、これは Google Workspace アカウントです。

    ユーザーは Gmail や Google Workspace を使用せずに Google アカウントに登録できます。email@gmail.com 接尾辞が含まれておらず、hd が存在しない場合、Google は権威がなく、ユーザーの確認にはパスワードなどのチャレンジ メソッドが推奨されます。Google アカウントの作成時に Google がユーザーを最初に確認しているため、email_verified が true になることもあります。ただし、サードパーティのメール アカウントの所有権がその後変更されている可能性があります。

    これらの検証手順を実行する独自のコードを作成するのではなく、プラットフォーム用の Google API クライアント ライブラリまたは汎用 JWT ライブラリを使用することを強くおすすめします。開発とデバッグでは、tokeninfo 検証エンドポイントを呼び出すことができます。

    Google API クライアント ライブラリの使用

    Google API クライアント ライブラリのいずれか( JavaNode.jsPHPPython など) は、本番環境で Google ID トークンを検証する場合におすすめの方法です。

    <ph type="x-smartling-placeholder">
    </ph> <ph type="x-smartling-placeholder">
    </ph>
    Java

    Java で ID トークンを検証するには、 GoogleIdTokenVerifier オブジェクトです。例:

    import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
    import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
    import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
    
    ...
    
    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
        // Specify the WEB_CLIENT_ID of the app that accesses the backend:
        .setAudience(Collections.singletonList(WEB_CLIENT_ID))
        // Or, if multiple clients access the backend:
        //.setAudience(Arrays.asList(WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3))
        .build();
    
    // (Receive idTokenString by HTTPS POST)
    
    GoogleIdToken idToken = verifier.verify(idTokenString);
    if (idToken != null) {
      Payload payload = idToken.getPayload();
    
      // Print user identifier. This ID is unique to each Google Account, making it suitable for
      // use as a primary key during account lookup. Email is not a good choice because it can be
      // changed by the user.
      String userId = payload.getSubject();
      System.out.println("User ID: " + userId);
    
      // Get profile information from payload
      String email = payload.getEmail();
      boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
      String name = (String) payload.get("name");
      String pictureUrl = (String) payload.get("picture");
      String locale = (String) payload.get("locale");
      String familyName = (String) payload.get("family_name");
      String givenName = (String) payload.get("given_name");
    
      // Use or store profile information
      // ...
    
    } else {
      System.out.println("Invalid ID token.");
    }

    GoogleIdTokenVerifier.verify() メソッドで JWT を検証する 署名、aud クレーム、iss クレーム、 exp の申し立て。

    ID トークンが Google Workspace または Cloud 組織アカウントの場合は、ドメイン名を確認することで hd クレームを確認できます。 Payload.getHostedDomain() メソッドが返す値。ドメインの アカウントがドメインによって管理されていることを確認するには email 申請では不十分です できます。

    で確認できます。 <ph type="x-smartling-placeholder">
    </ph>
    Node.js

    Node.js で ID トークンを検証するには、Node.js 用 Google 認証ライブラリを使用します。 ライブラリをインストールします。

    npm install google-auth-library --save
    次に、verifyIdToken() 関数を呼び出します。例:

    const {OAuth2Client} = require('google-auth-library');
    const client = new OAuth2Client();
    async function verify() {
      const ticket = await client.verifyIdToken({
          idToken: token,
          audience: WEB_CLIENT_ID,  // Specify the WEB_CLIENT_ID of the app that accesses the backend
          // Or, if multiple clients access the backend:
          //[WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3]
      });
      const payload = ticket.getPayload();
      // This ID is unique to each Google Account, making it suitable for use as a primary key
      // during account lookup. Email is not a good choice because it can be changed by the user.
      const userid = payload['sub'];
      // If the request specified a Google Workspace domain:
      // const domain = payload['hd'];
    }
    verify().catch(console.error);

    verifyIdToken 関数は、以下を確認します。 JWT 署名、aud クレーム、exp クレーム、 iss クレーム。

    ID トークンが Google Workspace または Cloud 組織アカウントの場合は、hd クレームを確認できます。これは、ホストされている Pod が ユーザーのドメインです。これは、リソースへのアクセスをメンバーのみに制限する場合に使用します 特定のドメインのこの申し立てがない場合は、アカウントが Google がホストするドメインです。

    で確認できます。 <ph type="x-smartling-placeholder">
    </ph>
    PHP

    PHP で ID トークンを検証するには、PHP 用の Google API クライアント ライブラリを使用します。 ライブラリをインストールします(Composer を使用するなど)。

    composer require google/apiclient
    次に、verifyIdToken() 関数を呼び出します。例:

    require_once 'vendor/autoload.php';
    
    // Get $id_token via HTTPS POST.
    
    $client = new Google_Client(['client_id' => $WEB_CLIENT_ID]);  // Specify the WEB_CLIENT_ID of the app that accesses the backend
    $payload = $client->verifyIdToken($id_token);
    if ($payload) {
      // This ID is unique to each Google Account, making it suitable for use as a primary key
      // during account lookup. Email is not a good choice because it can be changed by the user.
      $userid = $payload['sub'];
      // If the request specified a Google Workspace domain
      //$domain = $payload['hd'];
    } else {
      // Invalid ID token
    }

    verifyIdToken 関数は、以下を確認します。 JWT 署名、aud クレーム、exp クレーム、 iss クレーム。

    ID トークンが Google Workspace または Cloud 組織アカウントの場合は、hd クレームを確認できます。これは、ホストされている Pod が ユーザーのドメインです。これは、リソースへのアクセスをメンバーのみに制限する場合に使用します 特定のドメインのこの申し立てがない場合は、アカウントが Google がホストするドメインです。

    で確認できます。 <ph type="x-smartling-placeholder">
    </ph>
    Python

    Python で ID トークンを検証するには、 verify_oauth2_token 使用します。例:

    from google.oauth2 import id_token
    from google.auth.transport import requests
    
    # (Receive token by HTTPS POST)
    # ...
    
    try:
        # Specify the WEB_CLIENT_ID of the app that accesses the backend:
        idinfo = id_token.verify_oauth2_token(token, requests.Request(), WEB_CLIENT_ID)
    
        # Or, if multiple clients access the backend server:
        # idinfo = id_token.verify_oauth2_token(token, requests.Request())
        # if idinfo['aud'] not in [WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3]:
        #     raise ValueError('Could not verify audience.')
    
        # If the request specified a Google Workspace domain
        # if idinfo['hd'] != DOMAIN_NAME:
        #     raise ValueError('Wrong domain name.')
    
        # ID token is valid. Get the user's Google Account ID from the decoded token.
        # This ID is unique to each Google Account, making it suitable for use as a primary key
        # during account lookup. Email is not a good choice because it can be changed by the user.
        userid = idinfo['sub']
    except ValueError:
        # Invalid token
        pass

    verify_oauth2_token 関数で JWT を検証する aud クレーム、exp クレームの 3 つが存在します。 hd も確認する必要があります (該当する場合は)そのオブジェクトを調べ、 verify_oauth2_token が返品可能。複数のクライアントが aud クレームも手動で検証します。

  3. トークンの有効性が確認されたら、Google ID トークンの情報を使用して、サイトのアカウント ステータスを関連付けることができます。

    • 登録していないユーザー: 必要に応じて、ユーザーが追加のプロフィール情報を入力できる登録ユーザー インターフェース(UI)を表示できます。また、ユーザーは新しいアカウントとログイン ユーザー セッションをサイレントに作成することもできます。

    • サイトにすでに存在する既存のアカウント: エンドユーザーがパスワードを入力して、以前のアカウントを Google 認証情報にリンクできるウェブページを表示できます。これにより、ユーザーが既存のアカウントにアクセスできることが確認されます。

    • フェデレーション ユーザーが再ログインする場合: ユーザーをサイレントでログインさせることができます。