ตรวจสอบสิทธิ์กับเซิร์ฟเวอร์แบ็กเอนด์

หากคุณใช้ฟีเจอร์ลงชื่อเข้าใช้ด้วย Google กับแอปหรือเว็บไซต์ที่สื่อสารกับเซิร์ฟเวอร์แบ็กเอนด์ คุณอาจต้องระบุผู้ใช้ที่ลงชื่อเข้าใช้อยู่ในเซิร์ฟเวอร์ ซึ่งทำได้อย่างปลอดภัย หลังจากที่ผู้ใช้ลงชื่อเข้าใช้สำเร็จแล้ว ให้ส่ง โทเค็นรหัสไปยังเซิร์ฟเวอร์ของคุณโดยใช้ HTTPS จากนั้นในเซิร์ฟเวอร์ ให้ยืนยันความสมบูรณ์ของโทเค็นระบุตัวตน และใช้ข้อมูลผู้ใช้ที่อยู่ในโทเค็นเพื่อสร้างเซสชันหรือสร้างบัญชีใหม่

ส่งโทเค็นระบุตัวตนไปยังเซิร์ฟเวอร์

ก่อนอื่น เมื่อผู้ใช้ลงชื่อเข้าใช้ ให้รับโทเค็นระบุตัวตนของผู้ใช้โดยทำดังนี้

  1. เมื่อคุณ กำหนดค่า Google Sign-In โทรหา requestIdToken และส่งต่อ รหัสเว็บไคลเอ็นต์ของเซิร์ฟเวอร์

    // Request only the user's ID token, which can be used to identify the
    // user securely to your backend. This will contain the user's basic
    // profile (name, profile picture URL, etc) so you should not need to
    // make an additional call to personalize your application.
    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.server_client_id))
            .requestEmail()
            .build();
  2. เมื่อแอปเริ่มทํางาน ให้ตรวจสอบว่าผู้ใช้ลงชื่อเข้าใช้แอปของคุณโดยใช้ Google อยู่แล้วในอุปกรณ์นี้หรืออุปกรณ์อื่นหรือไม่ โดยเรียกใช้ silentSignIn ดังนี้

    GoogleSignIn.silentSignIn()
        .addOnCompleteListener(
            this,
            new OnCompleteListener<GoogleSignInAccount>() {
              @Override
              public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
                handleSignInResult(task);
              }
            });
  3. หากผู้ใช้ลงชื่อเข้าใช้แบบเงียบไม่ได้ ให้แสดงการออกจากระบบตามปกติ เพื่อให้ผู้ใช้มีตัวเลือกในการลงชื่อเข้าใช้ เมื่อผู้ใช้ สัญลักษณ์ ใน รับ GoogleSignInAccount ของผู้ใช้ในกิจกรรม ผลลัพธ์ของ Intent ในการลงชื่อเข้าใช้:

    // This task is always completed immediately, there is no need to attach an
    // asynchronous listener.
    Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
    handleSignInResult(task);
  4. หลังจากที่ผู้ใช้ลงชื่อเข้าใช้โดยไม่แจ้งให้ทราบหรือแสดงอย่างชัดแจ้ง ให้รับโทเค็นรหัสจาก ออบเจ็กต์ GoogleSignInAccount:

    private void handleSignInResult(@NonNull Task<GoogleSignInAccount> completedTask) {
        try {
            GoogleSignInAccount account = completedTask.getResult(ApiException.class);
            String idToken = account.getIdToken();
    
            // TODO(developer): send ID Token to server and validate
    
            updateUI(account);
        } catch (ApiException e) {
            Log.w(TAG, "handleSignInResult:error", e);
            updateUI(null);
        }
    }

จากนั้นส่งโทเค็นรหัสไปยังเซิร์ฟเวอร์ของคุณด้วยคำขอ HTTPS POST ดังนี้

HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("https://yourbackend.example.com/tokensignin");

try {
  List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
  nameValuePairs.add(new BasicNameValuePair("idToken", idToken));
  httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

  HttpResponse response = httpClient.execute(httpPost);
  int statusCode = response.getStatusLine().getStatusCode();
  final String responseBody = EntityUtils.toString(response.getEntity());
  Log.i(TAG, "Signed in as: " + responseBody);
} catch (ClientProtocolException e) {
  Log.e(TAG, "Error sending ID token to backend.", e);
} catch (IOException e) {
  Log.e(TAG, "Error sending ID token to backend.", e);
}

ยืนยันความสมบูรณ์ของโทเค็นรหัส

หลังจากได้รับโทเค็นรหัสทาง HTTPS POST แล้ว คุณต้องยืนยันความสมบูรณ์ ของโทเค็น

หากต้องการยืนยันว่าโทเค็นใช้งานได้ ให้ตรวจสอบว่าโทเค็นเป็นไปตามเกณฑ์ต่อไปนี้

  • Google ลงนามโทเค็นรหัสอย่างถูกต้อง ใช้คีย์สาธารณะของ Google (มีอยู่ในรูปแบบ JWK หรือ PEM) เพื่อยืนยันลายเซ็นของโทเค็น เราจะหมุนเวียนคีย์เหล่านี้เป็นประจำ โปรดตรวจสอบ ส่วนหัว Cache-Control ในการตอบกลับเพื่อดูว่าคุณควร เรียกข้อมูลคีย์อีกครั้งเมื่อใด
  • ค่าของ aud ในโทเค็นรหัสจะเท่ากับรหัสไคลเอ็นต์ของแอป รายการใดรายการหนึ่ง การตรวจสอบนี้จำเป็นเพื่อป้องกันไม่ให้มีการใช้โทเค็นรหัสที่ออกให้กับแอปที่เป็นอันตราย เพื่อเข้าถึงข้อมูลเกี่ยวกับผู้ใช้รายเดียวกันในเซิร์ฟเวอร์แบ็กเอนด์ของแอป
  • ค่าของ iss ในโทเค็นรหัสเท่ากับ accounts.google.com หรือ https://accounts.google.com
  • เวลาหมดอายุ (exp) ของโทเค็นรหัสยังไม่ผ่าน
  • หากต้องการตรวจสอบว่าโทเค็นรหัสแสดงบัญชี Google Workspace หรือ Cloud ขององค์กร คุณสามารถตรวจสอบhdอ้างสิทธิ์ ซึ่งระบุโดเมนที่โฮสต์ ของผู้ใช้ ต้องใช้เมื่อจำกัดการเข้าถึงทรัพยากรไว้เฉพาะสมาชิกของโดเมนบางโดเมนเท่านั้น การไม่มีการอ้างสิทธิ์นี้แสดงว่าบัญชีไม่ได้เป็นของโดเมนที่ Google โฮสต์

การใช้ช่อง email, email_verified และ hd จะช่วยให้คุณระบุได้ว่า Google เป็นผู้โฮสต์และเป็นผู้มีอำนาจสำหรับอีเมลหรือไม่ ในกรณีที่ Google เป็นผู้มีอำนาจ ระบบจะทราบว่าผู้ใช้เป็นเจ้าของบัญชีที่ถูกต้องตามกฎหมาย และคุณอาจข้ามรหัสผ่านหรือวิธีการ ท้าทายอื่นๆ ได้

กรณีที่ Google เป็นแหล่งข้อมูลที่เชื่อถือได้

  • email มีคำต่อท้ายเป็น @gmail.com แสดงว่าเป็นบัญชี Gmail
  • email_verifiedเป็นจริงและตั้งค่า hd ไว้ แสดงว่านี่คือบัญชี Google Workspace

ผู้ใช้สามารถลงทะเบียนบัญชี Google ได้โดยไม่ต้องใช้ Gmail หรือ Google Workspace เมื่อ email ไม่มีคำต่อท้าย @gmail.com และไม่มี hd Google จะไม่ มีสิทธิ์และขอแนะนำให้ใช้รหัสผ่านหรือวิธีการท้าทายอื่นๆ เพื่อยืนยัน ผู้ใช้ email_verified อาจเป็นจริงได้เช่นกัน เนื่องจาก Google ได้ยืนยันผู้ใช้ ในตอนแรกเมื่อสร้างบัญชี Google แต่การเป็นเจ้าของบัญชีอีเมลของบุคคลที่สาม อาจมีการเปลี่ยนแปลงในภายหลัง

เราขอแนะนำอย่างยิ่งให้ใช้ไลบรารีของไคลเอ็นต์ Google API สำหรับแพลตฟอร์มของคุณ หรือไลบรารี JWT แบบอเนกประสงค์ แทนการเขียนโค้ดของคุณเองเพื่อทำขั้นตอนการยืนยันเหล่านี้ คุณเรียกใช้tokeninfo ปลายทางการตรวจสอบของเราเพื่อการพัฒนาและการแก้ไขข้อบกพร่องได้

การใช้ไลบรารีของไคลเอ็นต์ Google API

การใช้ไลบรารีของไคลเอ็นต์ Google API รายการใดรายการหนึ่ง (เช่น Java Node.js PHP Python) เป็นวิธีที่แนะนำในการตรวจสอบโทเค็น Google ID ในสภาพแวดล้อมการใช้งานจริง

Java

ในการตรวจสอบโทเค็นรหัสใน Java ให้ใช้ ออบเจ็กต์ 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
  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

หากต้องการตรวจสอบว่าโทเค็นรหัสแสดงถึง Google Workspace หรือ Cloud บัญชีขององค์กร คุณสามารถยืนยันการอ้างสิทธิ์ hd โดยการตรวจสอบชื่อโดเมน แสดงผลโดยเมธอด Payload.getHostedDomain() โดเมนของ การอ้างสิทธิ์ email รายการไม่เพียงพอที่จะตรวจสอบว่าบัญชีได้รับการจัดการโดยโดเมน หรือองค์กรต่างๆ

Node.js

ในการตรวจสอบโทเค็นรหัสใน Node.js ให้ใช้ไลบรารีการตรวจสอบสิทธิ์ของ Google สำหรับ Node.js วิธีติดตั้งไลบรารี

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();
  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

หากต้องการตรวจสอบว่าโทเค็นรหัสแสดงถึง Google Workspace หรือ Cloud บัญชีขององค์กร คุณสามารถตรวจสอบการอ้างสิทธิ์ hd ซึ่งระบุการอ้างสิทธิ์ที่โฮสต์ไว้ โดเมนของผู้ใช้ ต้องใช้เมื่อจำกัดการเข้าถึงทรัพยากรให้กับสมาชิกเท่านั้น ของโดเมนหนึ่งๆ การที่ไม่มีการอ้างสิทธิ์นี้แสดงว่าบัญชีนี้ไม่ได้เป็นของ โดเมนที่ Google โฮสต์

PHP

หากต้องการตรวจสอบโทเค็นรหัสใน PHP ให้ใช้ไลบรารีของไคลเอ็นต์ Google API สำหรับ PHP ติดตั้งไลบรารี (เช่น โดยใช้ 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) {
  $userid = $payload['sub'];
  // If the request specified a Google Workspace domain
  //$domain = $payload['hd'];
} else {
  // Invalid ID token
}

ฟังก์ชัน verifyIdToken จะยืนยัน ลายเซ็น JWT, การอ้างสิทธิ์ aud, การอ้างสิทธิ์ exp และการอ้างสิทธิ์ iss

หากต้องการตรวจสอบว่าโทเค็นรหัสแสดงถึง Google Workspace หรือ Cloud บัญชีขององค์กร คุณสามารถตรวจสอบการอ้างสิทธิ์ hd ซึ่งระบุการอ้างสิทธิ์ที่โฮสต์ไว้ โดเมนของผู้ใช้ ต้องใช้เมื่อจำกัดการเข้าถึงทรัพยากรให้กับสมาชิกเท่านั้น ของโดเมนหนึ่งๆ การที่ไม่มีการอ้างสิทธิ์นี้แสดงว่าบัญชีนี้ไม่ได้เป็นของ โดเมนที่ Google โฮสต์

Python

หากต้องการตรวจสอบโทเค็นรหัสใน Python ให้ใช้เมธอด 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.
    userid = idinfo['sub']
except ValueError:
    # Invalid token
    pass

ฟังก์ชัน verify_oauth2_token จะยืนยัน JWT ลายเซ็น, การอ้างสิทธิ์ aud และการอ้างสิทธิ์ exp คุณต้องยืนยัน hd ด้วย การอ้างสิทธิ์ (หากมี) โดยการตรวจสอบออบเจ็กต์ที่ verify_oauth2_token กลับมา หากมีลูกค้าหลายรายเข้าถึง เซิร์ฟเวอร์ส่วนหลัง ให้ยืนยันการอ้างสิทธิ์ aud ด้วยตนเองด้วย

การเรียกใช้ปลายทางTokeninfo

วิธีง่ายๆ ในการตรวจสอบลายเซ็นโทเค็นรหัสเพื่อแก้ไขข้อบกพร่องคือ ใช้ปลายทาง tokeninfo การเรียกใช้ปลายทางนี้ต้องใช้ คำขอเครือข่ายเพิ่มเติมซึ่งทำหน้าที่ตรวจสอบส่วนใหญ่ให้คุณในขณะที่คุณทดสอบ การตรวจสอบความถูกต้องและการแยกเพย์โหลดในโค้ดของคุณเอง ไม่เหมาะกับการใช้งานจริง รหัสเพราะคำขออาจถูกควบคุมหรือมีข้อผิดพลาดเป็นระยะๆ

หากต้องการตรวจสอบโทเค็นรหัสโดยใช้ปลายทาง tokeninfo ให้สร้าง HTTPS คำขอ POST หรือ GET ไปยังปลายทาง แล้วส่งโทเค็นรหัสใน พารามิเตอร์ id_token เช่น หากต้องการตรวจสอบโทเค็น "XYZ123" ให้ส่งคำขอ GET ต่อไปนี้

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

หากโทเค็นมีการรับรองอย่างถูกต้อง และ iss และ exp จะมีค่าที่คาดไว้ คุณจะได้รับการตอบสนอง HTTP 200 โดยที่ส่วนเนื้อหา มีการอ้างสิทธิ์โทเค็นรหัสในรูปแบบ JSON ตัวอย่างคำตอบมีดังนี้

{
 // 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"
}

หากต้องการตรวจสอบว่าโทเค็นรหัสแสดงถึงบัญชี Google Workspace ให้ตรวจสอบ การอ้างสิทธิ์ hd ซึ่งระบุโดเมนที่โฮสต์ของผู้ใช้ ต้องใช้เมื่อ การจำกัดการเข้าถึงทรัพยากรไว้เฉพาะสมาชิกของบางโดเมนเท่านั้น การไม่มีการอ้างสิทธิ์นี้ ระบุว่าบัญชีไม่ได้เป็นของโดเมนที่โฮสต์ของ Google Workspace

สร้างบัญชีหรือเซสชัน

หลังจากยืนยันโทเค็นแล้ว ให้ตรวจสอบว่าผู้ใช้อยู่ในฐานข้อมูลผู้ใช้ของคุณอยู่แล้วหรือไม่ ในกรณีนี้ ให้สร้างเซสชันที่มีการตรวจสอบสิทธิ์สำหรับผู้ใช้ หากผู้ใช้ยังไม่อยู่ในฐานข้อมูลผู้ใช้ ให้สร้างระเบียนผู้ใช้ใหม่จากข้อมูลในเพย์โหลดโทเค็นระบุตัวตน และสร้างเซสชันสําหรับผู้ใช้ คุณสามารถแจ้งให้ผู้ใช้ระบุข้อมูลโปรไฟล์เพิ่มเติมที่ต้องการเมื่อตรวจพบผู้ใช้ที่สร้างขึ้นใหม่ในแอป

การรักษาความปลอดภัยให้บัญชีผู้ใช้ด้วยการป้องกันแบบครอบคลุมหลายบริการ

เมื่อใช้ Google เพื่อลงชื่อเข้าใช้ผู้ใช้ คุณจะได้รับประโยชน์จากฟีเจอร์และโครงสร้างพื้นฐานด้านความปลอดภัยทั้งหมดที่ Google สร้างขึ้นเพื่อปกป้องข้อมูลของผู้ใช้โดยอัตโนมัติ อย่างไรก็ตาม หากบัญชี Google ของผู้ใช้ถูกบุกรุกหรือเกิดเหตุการณ์ด้านความปลอดภัยที่สำคัญขึ้น แอปของคุณก็อาจถูกโจมตีได้เช่นกัน (ซึ่งมีโอกาสเกิดขึ้นน้อยมาก) เพื่อปกป้อง บัญชีที่มาจากการดำเนินการด้านความปลอดภัยที่สำคัญใดๆ ให้ใช้ข้ามบัญชี การปกป้อง เพื่อรับการแจ้งเตือนความปลอดภัยจาก Google เมื่อได้รับเหตุการณ์เหล่านี้ คุณจะเห็นการเปลี่ยนแปลงที่สำคัญเกี่ยวกับความปลอดภัยของบัญชี Google ของผู้ใช้ และสามารถดำเนินการกับบริการเพื่อรักษาความปลอดภัยให้บัญชีได้