バックエンドサーバーで認証する

バックエンドサーバーと通信するアプリまたはサイトでGoogleサインインを使用する場合は、サーバーで現在サインインしているユーザーを特定する必要がある場合があります。これを安全に行うには、ユーザーが正常にサインインした後、HTTPSを使用してユーザーのIDトークンをサーバーに送信します。次に、サーバーでIDトークンの整合性を確認し、トークンに含まれているユーザー情報を使用してセッションを確立するか、新しいアカウントを作成します。

IDトークンをサーバーに送信します

ユーザーが正常にサインインした後、ユーザーのIDトークンを取得します。

function onSignIn(googleUser) {
  var id_token = googleUser.getAuthResponse().id_token;
  ...
}

次に、HTTPSPOSTリクエストを使用してIDトークンをサーバーに送信します。

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://yourbackend.example.com/tokensignin');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
  console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('idtoken=' + id_token);

IDトークンの整合性を確認します

HTTPS POSTでIDトークンを受け取ったら、トークンの整合性を確認する必要があります。トークンが有効であることを確認するには、次の基準が満たされていることを確認してください。

  • IDトークンはGoogleによって適切に署名されています。 Googleの公開鍵( JWKまたはPEM形式で利用可能)を使用して、トークンの署名を確認します。これらのキーは定期的に回転します。応答のCache-Controlヘッダーを調べて、いつそれらを再度取得する必要があるかを判断します。
  • IDトークンのaudの値は、アプリのクライアントIDの1つと同じです。このチェックは、悪意のあるアプリに発行されたIDトークンが、アプリのバックエンドサーバー上の同じユーザーに関するデータにアクセスするために使用されるのを防ぐために必要です。
  • IDトークンのissの値は、 accounts.google.comまたはhttps://accounts.google.comと同じです。
  • IDトークンの有効期限( exp )が経過していません。
  • G Suiteドメインのメンバーのみにアクセスを制限する場合は、IDトークンにhdドメイン名と一致するhdクレームがあることを確認してください。

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

GoogleAPIクライアントライブラリの使用

実稼働環境でGoogleIDトークンを検証するには、 Google APIクライアントライブラリの1つ( JavaNode.jsPHPPythonなど)を使用することをお勧めします。

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 CLIENT_ID of the app that accesses the backend:
    .setAudience(Collections.singletonList(CLIENT_ID))
    // Or, if multiple clients access the backend:
    //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
    .build();

// (Receive idTokenString by HTTPS POST)

GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
  Payload payload = idToken.getPayload();

  // Print user identifier
  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クレームを検証します。

G Suiteドメインのメンバーのみにアクセスを制限する場合は、 Payload.getHostedDomain()メソッドによって返されるドメイン名を確認してhdクレームも確認します。

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(CLIENT_ID);
async function verify() {
  const ticket = await client.verifyIdToken({
      idToken: token,
      audience: CLIENT_ID,  // Specify the CLIENT_ID of the app that accesses the backend
      // Or, if multiple clients access the backend:
      //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
  });
  const payload = ticket.getPayload();
  const userid = payload['sub'];
  // If request specified a G Suite domain:
  // const domain = payload['hd'];
}
verify().catch(console.error);

verifyIdToken関数は、JWT署名、 audクレーム、 expクレーム、およびissクレームを検証します。

G Suiteドメインのメンバーのみにアクセスを制限する場合は、 hdクレームがhdドメイン名と一致することも確認してください。

PHP

PHPでIDトークンを検証するには、PHP用のGoogleAPIクライアントライブラリを使用します。ライブラリをインストールします(たとえば、Composerを使用)。

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

require_once 'vendor/autoload.php';

// Get $id_token via HTTPS POST.

$client = new Google_Client(['client_id' => $CLIENT_ID]);  // Specify the CLIENT_ID of the app that accesses the backend
$payload = $client->verifyIdToken($id_token);
if ($payload) {
  $userid = $payload['sub'];
  // If request specified a G Suite domain:
  //$domain = $payload['hd'];
} else {
  // Invalid ID token
}

verifyIdToken関数は、JWT署名、 audクレーム、 expクレーム、およびissクレームを検証します。

G Suiteドメインのメンバーのみにアクセスを制限する場合は、 hdクレームがhdドメイン名と一致することも確認してください。

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 CLIENT_ID of the app that accesses the backend:
    idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)

    # Or, if multiple clients access the backend server:
    # idinfo = id_token.verify_oauth2_token(token, requests.Request())
    # if idinfo['aud'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
    #     raise ValueError('Could not verify audience.')

    # If auth request is from a G Suite domain:
    # if idinfo['hd'] != GSUITE_DOMAIN_NAME:
    #     raise ValueError('Wrong hosted domain.')

    # ID token is valid. Get the user's Google Account ID from the decoded token.
    userid = idinfo['sub']
except ValueError:
    # Invalid token
    pass

verify_oauth2_token関数は、JWT署名、 audクレーム、およびexpクレームを検証します。また、 verify_oauth2_tokenが返すオブジェクトを調べて、 hdクレームを確認する必要があります(該当する場合)。複数のクライアントがバックエンドサーバーにアクセスする場合は、 audクレームも手動で確認してください。

tokeninfoエンドポイントの呼び出し

デバッグ用にIDトークン署名を検証する簡単な方法は、 tokeninfoエンドポイントを使用することtokeninfo 。このエンドポイントの呼び出しには、独自のコードで適切な検証とペイロード抽出をテストしている間、ほとんどの検証を行う追加のネットワーク要求が含まれます。リクエストが抑制されるか、断続的なエラーが発生する可能性があるため、本番コードでの使用には適していません。

tokeninfoエンドポイントを使用してIDトークンを検証するには、エンドポイントにHTTPS POSTまたはGETリクエストを行い、 id_tokenパラメーターでIDトークンをid_tokenます。たとえば、トークン「XYZ123」を検証するには、次のGETリクエストを実行します。

https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123

トークンが適切に署名されており、 issおよびexpクレームに期待値がある場合、HTTP 200応答が返されます。ここで、本文にはJSON形式のIDトークンクレームが含まれています。応答の例を次に示します。

{
 // These six fields are included in all Google ID Tokens.
 "iss": "https://accounts.google.com",
 "sub": "110169484474386276334",
 "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "iat": "1433978353",
 "exp": "1433981953",

 // These seven fields are only included when the user has granted the "profile" and
 // "email" OAuth scopes to the application.
 "email": "testuser@gmail.com",
 "email_verified": "true",
 "name" : "Test User",
 "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
 "given_name": "Test",
 "family_name": "User",
 "locale": "en"
}

G Suiteをご利用の場合は、ユーザーのホストドメインを示すhdクレームにも関心があるかもしれません。これを使用して、リソースへのアクセスを特定のドメインのメンバーのみに制限できます。この主張がないことは、ユーザーがGSuiteでホストされているドメインに属していないことを示しています。

アカウントまたはセッションを作成する

トークンを確認したら、ユーザーが既にユーザーデータベースに存在するかどうかを確認します。その場合、ユーザーの認証済みセッションを確立します。ユーザーがまだユーザーデータベースにない場合は、IDトークンペイロードの情報から新しいユーザーレコードを作成し、ユーザーのセッションを確立します。アプリで新しく作成されたユーザーを検出したときに、必要な追加のプロファイル情報をユーザーに求めることができます。

クロスアカウント保護でユーザーのアカウントを保護する

ユーザーのログインをGoogleに依存すると、ユーザーのデータを保護するためにGoogleが構築したすべてのセキュリティ機能とインフラストラクチャを自動的に利用できます。ただし、万が一、ユーザーのGoogleアカウントが侵害されたり、その他の重大なセキュリティイベントが発生したりした場合でも、アプリは攻撃に対して脆弱になる可能性があります。主要なセキュリティイベントからアカウントをより適切に保護するには、クロスアカウント保護を使用してGoogleからセキュリティアラートを受信します。これらのイベントを受信すると、ユーザーのGoogleアカウントのセキュリティに対する重要な変更を確認できるようになり、サービスに対してアクションを実行してアカウントを保護できます。