ยืนยันโทเค็นรหัส Google ในฝั่งเซิร์ฟเวอร์ของคุณ

หลังจากที่ Google ส่งคืนโทเค็นรหัสแล้ว โทเค็นนี้จะส่งโดยคำขอเมธอด HTTP POST ที่มีชื่อพารามิเตอร์ credential ไปยังปลายทางการเข้าสู่ระบบ

ตัวอย่างในภาษา Python ที่แสดงขั้นตอนทั่วไปในการตรวจสอบและใช้โทเค็นรหัส มีดังนี้

  1. ยืนยันโทเค็น Cross-Site Request Forgery (CSRF) เมื่อคุณส่งข้อมูลเข้าสู่ระบบไปยังปลายทางการเข้าสู่ระบบ เราจะใช้รูปแบบการส่งคุกกี้ซ้ำเพื่อป้องกันการโจมตี CSRF ก่อนที่จะส่งแต่ละครั้ง เราจะสร้างโทเค็น จากนั้น ระบบจะใส่โทเค็นลงในทั้งคุกกี้และส่วนเนื้อความของโพสต์ ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้

    csrf_token_cookie = self.request.cookies.get('g_csrf_token')
    if not csrf_token_cookie:
        webapp2.abort(400, 'No CSRF token in Cookie.')
    csrf_token_body = self.request.get('g_csrf_token')
    if not csrf_token_body:
        webapp2.abort(400, 'No CSRF token in post body.')
    if csrf_token_cookie != csrf_token_body:
        webapp2.abort(400, 'Failed to verify double submit cookie.')
    
  2. ยืนยันโทเค็นรหัส

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

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

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

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

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

    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 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

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

    Node.js

    หากต้องการตรวจสอบโทเค็นรหัสใน Node.js ให้ใช้ไลบรารี Google Auth สำหรับ 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: 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

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

    PHP

    หากต้องการตรวจสอบโทเค็นรหัสใน PHP ให้ใช้ไลบรารีของไคลเอ็นต์ Google API สำหรับ PHP ติดตั้งไลบรารี (เช่น การใช้คอมโพสเซอร์) ดังนี้

    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

    หากต้องการตรวจสอบว่าโทเค็นรหัสแสดงถึงบัญชี 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 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 คุณต้องยืนยันการอ้างสิทธิ์ hd (หากมี) โดยการตรวจสอบออบเจ็กต์ที่ verify_oauth2_token ส่งคืนด้วย หากมีไคลเอ็นต์หลายคนเข้าถึงเซิร์ฟเวอร์แบ็กเอนด์ ให้ตรวจสอบการอ้างสิทธิ์ aud ด้วยตนเองด้วย

  3. เมื่อยืนยันความถูกต้องของโทเค็นแล้ว คุณจะใช้ข้อมูลในโทเค็นรหัส Google เพื่อเชื่อมโยงสถานะบัญชีของเว็บไซต์ได้ ดังนี้

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

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

    • ผู้ใช้ที่กลับมาแบบรวมศูนย์ที่กลับมา: คุณสามารถให้ผู้ใช้ลงชื่อเข้าใช้แบบเงียบ