使用 ID 權杖透過後端進行驗證

One Tap 登入用戶端會在使用者選取 Google 帳戶。ID 權杖是使用者身分的已簽署斷言, 包含使用者的基本個人資料資訊 (可能包括電子郵件地址) 已經過 Google 驗證

如果有 ID 權杖,您就能使用這些權杖安全地透過 自動為使用者註冊新帳戶 而無須驗證使用者的電子郵件地址

如要使用 ID 權杖登入或註冊使用者,請將權杖傳送至應用程式的 後端。在後端使用 Google API 用戶端驗證權杖 或一般用途 JWT 程式庫如果使用者尚未登入您的 以這個 Google 帳戶存取應用程式,建立新帳戶。

如果您選擇使用 Nonce 以避免重送攻擊,請使用 getNonce 將其與 ID 權杖一起傳送至後端伺服器, 預期價值我們強烈建議您考慮使用 Nonce 進一步提升使用者的安全

從憑證物件取得 ID 權杖

擷取使用者的憑證後,請檢查憑證物件是否存在 包括 ID 權杖如果可以,請將資料傳送至後端。

Java

public class YourActivity extends AppCompatActivity {

  // ...
  private static final int REQ_ONE_TAP = 2;  // Can be any integer unique to the Activity.
  private boolean showOneTapUI = true;
  // ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      switch (requestCode) {
          case REQ_ONE_TAP:
              try {
                  SignInCredential credential = oneTapClient.getSignInCredentialFromIntent(data);
                  String idToken = credential.getGoogleIdToken();
                  if (idToken !=  null) {
                      // Got an ID token from Google. Use it to authenticate
                      // with your backend.
                      Log.d(TAG, "Got ID token.");
                  }
              } catch (ApiException e) {
                  // ...
              }
              break;
      }
  }
}

Kotlin

class YourActivity : AppCompatActivity() {

    // ...
    private val REQ_ONE_TAP = 2  // Can be any integer unique to the Activity
    private var showOneTapUI = true
    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
             REQ_ONE_TAP -> {
                try {
                    val credential = oneTapClient.getSignInCredentialFromIntent(data)
                    val idToken = credential.googleIdToken
                    when {
                        idToken != null -> {
                            // Got an ID token from Google. Use it to authenticate
                            // with your backend.
                            Log.d(TAG, "Got ID token.")
                        }
                        else -> {
                            // Shouldn't happen.
                            Log.d(TAG, "No ID token!")
                        }
                    }
                } catch (e: ApiException) {
                    // ...
            }
        }
    }
    // ...
}

驗證 ID 權杖的完整性

透過 HTTPS POST 收到 ID 權杖後,必須驗證完整性 符記

如要驗證權杖是否有效,請確認下列事項 符合下列條件:

  • Google 正確簽署 ID 權杖。使用 Google 的公開金鑰 (支援語言: JWKPEM 格式) 驗證權杖的簽章我們會定期輪替這些金鑰。檢查 回應中的 Cache-Control 標頭,判斷何時 您必須重新擷取這些資料
  • ID 權杖中的 aud 值等於應用程式的其中一個 用戶端 ID這項檢查是為了避免核發給惡意人士的 ID 權杖 應用程式存取同一使用者在應用程式後端伺服器上的資料。
  • ID 權杖中的 iss 值等於 accounts.google.comhttps://accounts.google.com
  • ID 權杖的到期時間 (exp) 已過。
  • 如需驗證 ID 權杖是否代表 Google Workspace 或 Cloud 機構帳戶,您可以檢查 hd 憑證附加資訊,也就是 使用者的網域將資源存取權授予僅限以下成員時,就必須使用這個引數 避免特定網域若沒有這項聲明,表示帳戶不屬於 Google 代管的網域。

您可以利用 emailemail_verifiedhd 欄位判斷 Google 代管且具公信力的電子郵件地址。如果 Google 的公信力 使用者是合法帳戶擁有者,您可以略過密碼或其他 挑戰方法

Google 具有公信力的案例:

  • email 的尾碼是 @gmail.com,這是 Gmail 帳戶。
  • email_verified」為 true,且已設定「hd」,代表這是 G Suite 帳戶。

使用者註冊 Google 帳戶時,不必使用 Gmail 或 G Suite。時間 email 未包含 @gmail.com 後置字串,但 hd 缺少,Google 不會 建議使用權威性密碼、密碼或其他驗證方法 使用者。email_verified 也可能是 true,因為 Google 一開始就驗證過 使用者就是 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 端點呼叫這個端點涉及 額外的網路要求,這類要求會對您進行大部分的驗證, 導入自己的 AI 程式碼驗證和酬載擷取機制。不適合用於正式環境 因為要求可能會受到節流限制 或以其他方式造成間歇性錯誤

如要使用 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 代管網域。

建立帳戶或工作階段

驗證權杖後,請檢查使用者是否已存在於你的使用者中 資料庫如果是的話,請為使用者建立已驗證的工作階段。如果使用者 尚未在您的使用者資料庫中,請從這份資訊中建立新的使用者記錄 並為使用者建立工作階段。您可以提示 要求使用者在偵測到 新建立的使用者

保護使用者帳戶採用跨帳戶防護功能

只要您仰賴 Google 登入使用者,就能自動享有 安全功能和基礎架構,這是 Google 為了保護使用者資料而打造。不過 萬一使用者的 Google 帳戶遭到入侵,或發生其他 重大安全性事件,您的應用程式也較容易受到攻擊。進一步保護 帳戶不受重大安全性事件影響,請使用跨帳戶 保護措施,可接收 Google 的安全性警示。收到這些活動後,您 掌握使用者的 Google 帳戶安全性重大異動 即可對你的服務採取行動來保護帳戶安全。