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

バックエンド サーバーと通信するアプリやサイトで Google ログインを使用する場合、そのサーバーで現在ログインしているユーザーを特定しなければならない場合があります。安全にログインできるよう、ユーザーがログインに成功したら、ユーザーの ID トークンを HTTPS を使用してサーバーに送信します。次に、サーバーで ID トークンの整合性を確認し、トークンに含まれるユーザー情報を使用してセッションを確立するか、新しいアカウントを作成します。

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

ユーザーがログインに成功したら、ユーザーの ID トークンを取得します。

Swift

GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
    guard error == nil else { return }
    guard let signInResult = signInResult else { return }

    signInResult.user.refreshTokensIfNeeded { user, error in
        guard error == nil else { return }
        guard let user = user else { return }

        let idToken = user.idToken
        // Send ID token to backend (example below).
    }
}

Objective-C

[GIDSignIn.sharedInstance signInWithPresentingViewController:self
                                              completion:^(GIDSignInResult * _Nullable signInResult,
                                                           NSError * _Nullable error) {
      if (error) { return; }
      if (signInResult == nil) { return; }

      [signInResult.user refreshTokensIfNeededWithCompletion:^(GIDGoogleUser * _Nullable user,
                                                               NSError * _Nullable error) {
          if (error) { return; }
          if (user == nil) { return; }

          NSString *idToken = user.idToken;
          // Send ID token to backend (example below).
      }];
}];

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

Swift

func tokenSignInExample(idToken: String) {
    guard let authData = try? JSONEncoder().encode(["idToken": idToken]) else {
        return
    }
    let url = URL(string: "https://yourbackend.example.com/tokensignin")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    let task = URLSession.shared.uploadTask(with: request, from: authData) { data, response, error in
        // Handle response from your backend.
    }
    task.resume()
}

Objective-C

NSString *signinEndpoint = @"https://yourbackend.example.com/tokensignin";
NSDictionary *params = @{@"idtoken": idToken};

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:signinEndpoint];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[self httpBodyForParamsDictionary:params]];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request
                                   queue:queue
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                         if (error) {
                           NSLog(@"Error: %@", error.localizedDescription);
                         } else {
                           NSLog(@"Signed in as %@", data.bytes);
                         }
                       }];

ID トークンの整合性を検証する

HTTPS POST で 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 クレームを確認できます。これは、ホストされている Pod が ユーザーのドメインです。これは、リソースへのアクセスをそのメンバーのみに制限する場合に できます。このクレームがない場合、アカウントは Google がホストするドメイン。

emailemail_verifiedhd フィールドを使用して、 Google はメールアドレスをホストし、その権威があります。Google が信頼できる場合、 そのユーザーが正当なアカウント所有者であることがわかっているため、パスワードやその他の 確認しましょう。

Google が信頼できるケース:

  • email には @gmail.com という接尾辞が付いています。これは Gmail アカウントです。
  • email_verified は true で、hd が設定されています。これは G Suite アカウントです。

ユーザーは Gmail や G Suite を使用せずに Google アカウントを登録できます。日時 email@gmail.com という接尾辞がなく、hd が存在しないと、Google にはありません。 認証には、信頼できる認証方法、パスワード、その他の本人確認方法を使用することが推奨されます。 できます。email_verified も、Google が最初に検証した ユーザーに付与できますが、サードパーティの所有権が付与されます。 アカウントが変更された可能性があります。

これらの検証ステップを実行するために独自のコードを記述するのではなく、 プラットフォームに適した 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 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 项版权主张。

如果您需要验证 ID 令牌是否代表 Google Workspace 或 Cloud 组织账号,您可以通过检查域名来验证 hd 所有权声明 由 Payload.getHostedDomain() 方法返回。该 email 声明不足以保证账号是由网域管理 或组织。

。 <ph type="x-smartling-placeholder">
</ph>
Node.js

要在 Node.js 中验证 ID 令牌,请使用适用于 Node.js 的 Google Auth 库。 安装该库:

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: 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 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 声明,该声明表示托管的 用户的网域。将资源访问权限限制为仅允许成员访问时,必须使用此设置 特定网域的用户缺少此声明即表示该账号不属于 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' => $CLIENT_ID]);  // Specify the CLIENT_ID of the app that accesses the backend
$payload = $client->verifyIdToken($id_token);
if ($payload) {
  $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 声明,该声明表示托管的 用户的网域。将资源访问权限限制为仅允许成员访问时,必须使用此设置 特定网域的用户缺少此声明即表示该账号不属于 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 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 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.
    userid = idinfo['sub']
except ValueError:
    # Invalid token
    pass

verify_oauth2_token 函数验证 JWT 签名、aud 声明和 exp 声明。 您还必须验证 hd 检查 verify_oauth2_token 返回。如果多个客户端访问 后端服务器,另请手动验证 aud 声明。

调用 tokeninfo 端点

调试验证 ID 令牌签名的一种简单方法是 使用 tokeninfo 端点。调用此端点涉及 这个额外的网络请求会为您完成大部分的验证工作, 验证和载荷提取。不适合在生产环境中使用 因为请求可能会受到限制或出现间歇性错误。

如需使用 tokeninfo 端点验证 ID 令牌,请创建 HTTPS POST 或 GET 请求发送到端点,并在 id_token 参数。 例如,要验证令牌“XYZ123”,请发出以下 GET 请求:

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

如果令牌经过正确签名,并且 issexp 具有预期值,就会收到 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"
}

如果您需要验证 ID 令牌是否代表 Google Workspace 账号,可以先查看 hd 声明,指示用户的托管网域。只有在以下情况下, 从而仅允许特定网域中的成员访问资源。缺少此声明 表示该账号不属于 Google Workspace 托管网域。

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

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

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

Google を利用してユーザーのログインを行うことで、ユーザーデータを保護するために Google が構築したすべてのセキュリティ機能とインフラストラクチャを利用できます。しかし、万が一ユーザーの Google アカウントが不正使用された場合や、他の重大なセキュリティ イベントが発生した場合、アプリが攻撃を受けやすくなる可能性があります。重大なセキュリティ イベントからアカウントをより適切に保護するには、クロスアカウント保護機能を使用して、Google からセキュリティ通知を受信します。イベントを受信すると、ユーザーの Google アカウントのセキュリティに関する重要な変更を把握して、アカウントを保護するための措置を講じることができます。