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

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

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

  1. ยืนยันโทเค็น Cross-Site Request Forgery (CSRF) เมื่อคุณส่งข้อมูลเข้าสู่ระบบไปยังปลายทางการเข้าสู่ระบบ เราจะใช้รูปแบบการส่งคุกกี้ 2 ครั้งเพื่อป้องกันการโจมตี 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 โฮสต์

    การใช้ช่อง 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 ด้วยตนเองด้วย

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

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

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

    • ผู้ใช้ที่กลับมาซึ่งใช้การรวมข้อมูล: คุณสามารถลงชื่อเข้าใช้ผู้ใช้โดยอัตโนมัติได้