ย้ายข้อมูลไปยังบริการ Google Identity

ภาพรวม

Google มีไลบรารี JavaScript หลายรายการเพื่อรับโทเค็นเพื่อการเข้าถึงต่อผู้ใช้สำหรับการเรียกใช้ Google APIs ดังนี้

คู่มือนี้จะแสดงวิธีการย้ายข้อมูลจากไลบรารีเหล่านี้ไปยังไลบรารี Google Identity Services

เมื่อทำตามคู่มือนี้ คุณจะสามารถทำสิ่งต่อไปนี้ได้

  • แทนที่ไลบรารีแพลตฟอร์มที่เลิกใช้งานแล้วด้วยไลบรารีบริการระบุตัวตน และ
  • หากใช้ไลบรารีของไคลเอ็นต์ API ให้นำโมดูล gapi.auth2 ที่เลิกใช้งานแล้ว รวมถึงเมธอดและออบเจ็กต์ของโมดูลดังกล่าวออก แล้วแทนที่ด้วยเมธอดและออบเจ็กต์ที่เทียบเท่าของบริการระบุตัวตน

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

หากต้องการการตรวจสอบสิทธิ์สำหรับการลงชื่อสมัครใช้และการลงชื่อเข้าใช้ของผู้ใช้ โปรดดูการย้ายข้อมูลจาก Google Sign-In แทน

ระบุขั้นตอนการให้สิทธิ์

ขั้นตอนการให้สิทธิ์ผู้ใช้มี 2 รูปแบบ ได้แก่ แบบไม่ชัดเจนและแบบรหัสการให้สิทธิ์

ตรวจสอบเว็บแอปเพื่อระบุประเภทของขั้นตอนการให้สิทธิ์ที่ใช้

สัญญาณที่บ่งบอกว่าเว็บแอปของคุณใช้ขั้นตอนการให้สิทธิ์โดยนัยมีดังนี้

สัญญาณที่บ่งบอกว่าเว็บแอปของคุณใช้ขั้นตอนรหัสการให้สิทธิ์มีดังนี้

  • การติดตั้งใช้งานจะขึ้นอยู่กับสิ่งต่อไปนี้

  • แอปของคุณจะทำงานทั้งในเบราว์เซอร์ของผู้ใช้และในแพลตฟอร์มแบ็กเอนด์

  • แพลตฟอร์มแบ็กเอนด์ของคุณโฮสต์ปลายทางของรหัสการให้สิทธิ์

  • แพลตฟอร์มแบ็กเอนด์จะเรียกใช้ Google APIs ในนามของผู้ใช้โดยไม่จำเป็นต้องมีผู้ใช้อยู่ด้วย หรือที่เรียกว่าโหมดออฟไลน์

  • แพลตฟอร์มแบ็กเอนด์จะจัดการและจัดเก็บโทเค็นการรีเฟรช

ในบางกรณี ฐานของโค้ดอาจรองรับทั้ง 2 ขั้นตอน

เลือกขั้นตอนการให้สิทธิ์

ก่อนเริ่มการย้ายข้อมูล คุณต้องพิจารณาว่าการใช้ขั้นตอนที่มีอยู่ต่อไปหรือการใช้ขั้นตอนอื่นจะตอบสนองความต้องการของคุณได้ดีที่สุด

โปรดดูการเลือกขั้นตอนการให้สิทธิ์เพื่อทำความเข้าใจความแตกต่างที่สำคัญและข้อดีข้อเสียระหว่างทั้ง 2 ขั้นตอน

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

เลือกขั้นตอนการให้สิทธิ์โดยใช้ตัวเลือก

ขั้นตอนการให้สิทธิ์โดยนัย

รับโทเค็นเพื่อการเข้าถึงสำหรับการใช้งานในเบราว์เซอร์ขณะที่ผู้ใช้อยู่

ตัวอย่างขั้นตอนการให้สิทธิ์โดยนัยแสดงเว็บแอปก่อนและหลังการย้ายข้อมูลไปยังบริการระบุตัวตน

ขั้นตอนรหัสการให้สิทธิ์

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

ตัวอย่างขั้นตอนรหัสการให้สิทธิ์แสดงเว็บแอปก่อนและหลังการย้ายข้อมูลไปยังบริการระบุตัวตน

ตลอดทั้งคู่มือนี้ ให้ทำตามวิธีการที่แสดงเป็นตัวหนาเพื่อเพิ่ม นำออก อัปเดต หรือแทนที่ฟังก์ชันการทำงานที่มีอยู่

การเปลี่ยนแปลงในเว็บแอปในเบราว์เซอร์

ส่วนนี้จะตรวจสอบการเปลี่ยนแปลงที่คุณจะทำกับเว็บแอปในเบราว์เซอร์เมื่อย้ายข้อมูลไปยังไลบรารี JavaScript ของ Google Identity Services

ระบุโค้ดที่ได้รับผลกระทบและทดสอบ

คุกกี้แก้ไขข้อบกพร่องช่วยค้นหาโค้ดที่ได้รับผลกระทบและทดสอบลักษณะการทำงานหลังการเลิกใช้งานได้

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

หากต้องการยืนยันลักษณะการทำงานของแอปเมื่อไม่มีการโหลดโมดูล gapi.auth2 อีกต่อไป ให้ตั้งค่าคุกกี้ G_AUTH2_MIGRATION เป็น enforced ซึ่งจะช่วยให้ทดสอบลักษณะการทำงานหลังการเลิกใช้งานได้ก่อนวันที่บังคับใช้

ค่าที่เป็นไปได้ของคุกกี้ G_AUTH2_MIGRATION มีดังนี้

  • enforced อย่าโหลดโมดูล gapi.auth2
  • informational บันทึกการใช้ฟังก์ชันการทำงานที่เลิกใช้งานแล้วไปยังคอนโซล JS และบันทึกลงในพื้นที่เก็บข้อมูลเซสชันเมื่อมีการตั้งชื่อคีย์ที่ไม่บังคับ: informational:key-name

เพื่อลดผลกระทบต่อผู้ใช้ เราขอแนะนำให้คุณตั้งค่าคุกกี้นี้ในเครื่องก่อนในระหว่างการพัฒนาซอฟต์แวร์และการทดสอบ ก่อนที่จะใช้ในสภาพแวดล้อมฮาร์ดแวร์และซอฟต์แวร์

ไลบรารีและโมดูล

โมดูล gapi.auth2 จะจัดการการตรวจสอบสิทธิ์ผู้ใช้สำหรับการลงชื่อเข้าใช้และขั้นตอนการให้สิทธิ์โดยนัย ให้แทนที่โมดูลที่เลิกใช้งานนี้ รวมถึงออบเจ็กต์และเมธอดของโมดูลด้วยไลบรารี Google Identity Services

เพิ่มไลบรารีบริการระบุตัวตนลงในเว็บแอปโดยรวมไว้ในเอกสารดังนี้

<script src="https://accounts.google.com/gsi/client" async defer></script>

นำอินสแตนซ์ทั้งหมดของการโหลดโมดูล auth2 โดยใช้ gapi.load('auth2', function) ออก

ไลบรารี Google Identity Services จะแทนที่การใช้งานโมดูล gapi.auth2 คุณสามารถใช้โมดูล gapi.client จากไลบรารีของไคลเอ็นต์ Google API สำหรับ JavaScript ต่อไปได้อย่างปลอดภัย และใช้ประโยชน์จากการสร้างเมธอด JS ที่เรียกใช้ได้โดยอัตโนมัติจากเอกสารการค้นพบ การรวมการเรียก API หลายรายการ และฟังก์ชันการจัดการ CORS

คุกกี้

การให้สิทธิ์ผู้ใช้ไม่จำเป็นต้องใช้คุกกี้

ดูรายละเอียดเกี่ยวกับวิธีที่การตรวจสอบสิทธิ์ผู้ใช้ใช้คุกกี้ได้ที่การย้ายข้อมูลจาก Google Sign-In และดูการใช้คุกกี้โดยผลิตภัณฑ์และบริการอื่นๆ ของ Google ได้ที่วิธีที่ Google ใช้คุกกี้

ข้อมูลเข้าสู่ระบบ

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

หากต้องการดูการเปลี่ยนแปลงเหล่านี้ โปรดดูข้อมูลเข้าสู่ระบบตัวอย่าง

ขั้นตอนการให้สิทธิ์โดยนัย

แยกการตรวจสอบสิทธิ์และการให้สิทธิ์ของผู้ใช้ออกจากกันโดยนำการจัดการโปรไฟล์ผู้ใช้ออกจากขั้นตอนการให้สิทธิ์

นำการอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ต่อไปนี้ออก

เมธอด

  • GoogleUser.getBasicProfile()
  • GoogleUser.getId()

ขั้นตอนรหัสการให้สิทธิ์

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

สถานะเซสชัน

ก่อนหน้านี้ Google Sign-In ช่วยคุณจัดการสถานะการลงชื่อเข้าใช้ของผู้ใช้โดยใช้สิ่งต่อไปนี้

คุณมีหน้าที่รับผิดชอบในการจัดการสถานะการลงชื่อเข้าใช้และเซสชันของผู้ใช้ในเว็บแอป

นำการอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ต่อไปนี้ออก

วัตถุ:

  • gapi.auth2.SignInOptions

วิธีการ:

  • GoogleAuth.attachClickHandler()
  • GoogleAuth.isSignedIn()
  • GoogleAuth.isSignedIn.get()
  • GoogleAuth.isSignedIn.listen()
  • GoogleAuth.signIn()
  • GoogleAuth.signOut()
  • GoogleAuth.currentUser.get()
  • GoogleAuth.currentUser.listen()
  • GoogleUser.isSignedIn()

การกำหนดค่าไคลเอ็นต์

อัปเดตเว็บแอปเพื่อเริ่มต้นไคลเอ็นต์โทเค็นสำหรับขั้นตอนการให้สิทธิ์โดยนัยหรือขั้นตอนรหัสการให้สิทธิ์

นำการอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ต่อไปนี้ออก

วัตถุ:

  • gapi.auth2.ClientConfig
  • gapi.auth2.OfflineAccessOptions

วิธีการ:

  • gapi.auth2.getAuthInstance()
  • GoogleUser.grant()

ขั้นตอนการให้สิทธิ์โดยนัย

เพิ่มออบเจ็กต์ TokenClientConfig และการเรียก initTokenClient() เพื่อกำหนดค่าเว็บแอป โดยทำตามตัวอย่างในเริ่มต้นไคลเอ็นต์โทเค็น

แทนที่ การอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ด้วย Google Identity Services โดยทำดังนี้

วัตถุ:

  • gapi.auth2.AuthorizeConfig กับ TokenClientConfig

วิธีการ:

  • gapi.auth2.init() กับ google.accounts.oauth2.initTokenClient()

พารามิเตอร์ ได้แก่

  • gapi.auth2.AuthorizeConfig.login_hint ด้วย TokenClientConfig.login_hint
  • gapi.auth2.GoogleUser.getHostedDomain() เมื่อใช้ TokenClientConfig.hd

ขั้นตอนรหัสการให้สิทธิ์

เพิ่มออบเจ็กต์ CodeClientConfig และการเรียก initCodeClient() เพื่อกำหนดค่าเว็บแอป โดยทำตามตัวอย่างในเริ่มต้นไคลเอ็นต์โค้ด

เมื่อเปลี่ยนจากขั้นตอนโดยนัยเป็นขั้นตอนรหัสการให้สิทธิ์ ให้ทำดังนี้

นำ การอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ออก

วัตถุ:

  • gapi.auth2.AuthorizeConfig

วิธีการ:

  • gapi.auth2.init()

พารามิเตอร์ ได้แก่

  • gapi.auth2.AuthorizeConfig.login_hint
  • gapi.auth2.GoogleUser.getHostedDomain()

คำขอโทเค็น

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

ขั้นตอนการให้สิทธิ์โดยนัย

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

แทนที่ การอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ด้วย Google Identity Services:

วิธีการ:

  • gapi.auth2.authorize() กับ TokenClient.requestAccessToken()
  • GoogleUser.reloadAuthResponse() ที่มี TokenClient.requestAccessToken()

เพิ่มลิงก์หรือปุ่มเพื่อเรียกใช้ requestAccessToken() เพื่อเริ่มขั้นตอน UX ของป๊อปอัปเพื่อขอโทเค็นเพื่อการเข้าถึง หรือเพื่อรับโทเค็นใหม่เมื่อโทเค็นที่มีอยู่หมดอายุ

อัปเดตฐานของโค้ดเป็นรายการต่อไปนี้

  • ทริกเกอร์โฟลว์โทเค็น OAuth 2.0 ด้วย requestAccessToken()
  • รองรับการให้สิทธิ์แบบเพิ่มขึ้นโดยใช้ requestAccessToken และ OverridableTokenClientConfig เพื่อแยกคำขอเดียวสำหรับหลายขอบเขตออกเป็นคำขอขนาดเล็กหลายรายการ
  • ขอโทเค็นใหม่เมื่อโทเค็นที่มีอยู่หมดอายุหรือถูกเพิกถอน

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

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

ขั้นตอนรหัสการให้สิทธิ์

เพิ่มลิงก์หรือปุ่มเพื่อเรียกใช้ requestCode() เพื่อขอรหัสการให้สิทธิ์จาก Google ดูตัวอย่างได้ที่ทริกเกอร์ขั้นตอนการให้สิทธิ์ของ OAuth 2.0

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีตอบสนองต่อโทเค็นเพื่อการเข้าถึงที่หมดอายุหรือถูกเพิกถอนได้ที่ส่วนการจัดการโทเค็น

การจัดการโทเค็น

เพิ่มการจัดการข้อผิดพลาดเพื่อตรวจหาการเรียก Google API ที่ไม่สำเร็จเมื่อมีการใช้โทเค็นเพื่อการเข้าถึงที่หมดอายุหรือถูกเพิกถอน และเพื่อขอโทเค็นเพื่อการเข้าถึงใหม่ที่ถูกต้อง

Google APIs จะแสดงรหัสสถานะ HTTP 401 Unauthorized และข้อความแสดงข้อผิดพลาด invalid_token เมื่อมีการใช้โทเค็นเพื่อการเข้าถึงที่หมดอายุหรือถูกเพิกถอน ดูตัวอย่างได้ที่การตอบกลับโทเค็นไม่ถูกต้อง

โทเค็นที่หมดอายุ

โทเค็นเพื่อการเข้าถึงมีอายุการใช้งานสั้น และมักจะใช้ได้เพียงไม่กี่นาที

การเพิกถอนโทเค็น

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

แทนที่ การอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ด้วย Google Identity Services:

วิธีการ:

  • getAuthInstance().disconnect() กับ google.accounts.oauth2.revoke()
  • GoogleUser.disconnect() กับ google.accounts.oauth2.revoke()

เรียกใช้ revoke เมื่อผู้ใช้ลบบัญชีของตนในแพลตฟอร์มของคุณ หรือต้องการนำความยินยอมในการแชร์ข้อมูลที่แชร์กับแอปของคุณออก

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

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

การลงชื่อเข้าใช้ของผู้ใช้

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

  • ลดจำนวนครั้งที่ผู้ใช้ต้องลงชื่อเข้าใช้ โดยการขอโทเค็นเพื่อการเข้าถึงจะเริ่มกระบวนการลงชื่อเข้าใช้บัญชี Google หากยังไม่มีเซสชันที่ใช้งานอยู่
  • การใช้ช่อง credential email ของโทเค็นรหัส JWT โดยตรงเป็นค่าของพารามิเตอร์ login_hint ในออบเจ็กต์ CodeClientConfig หรือ TokenClientConfig ซึ่งจะเป็นประโยชน์อย่างยิ่งหากแพลตฟอร์มของคุณไม่ได้ดูแลรักษาระบบการจัดการบัญชีผู้ใช้
  • ค้นหาและเชื่อมโยงบัญชี Google กับบัญชีผู้ใช้ในพื้นที่ที่มีอยู่บนแพลตฟอร์มของคุณ ซึ่งจะช่วยลดบัญชีที่ซ้ำกันบนแพลตฟอร์ม
  • เมื่อมีการสร้างบัญชีใหม่ในเครื่อง กล่องโต้ตอบและขั้นตอนการลงชื่อสมัครใช้จะแยกออกจากกล่องโต้ตอบและขั้นตอนการตรวจสอบสิทธิ์ผู้ใช้อย่างชัดเจน ซึ่งจะช่วยลดจำนวนขั้นตอนที่จำเป็นและปรับปรุงอัตราการออกจากเว็บไซต์

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

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

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

ขั้นตอนการให้สิทธิ์โดยนัย

แทนที่ การอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ด้วย Google Identity Services โดยทำดังนี้

วัตถุ:

  • gapi.auth2.AuthorizeResponse กับ TokenClient.TokenResponse
  • gapi.auth2.AuthResponse กับ TokenClient.TokenResponse

วิธีการ:

  • GoogleUser.hasGrantedScopes() ที่มี google.accounts.oauth2.hasGrantedAllScopes()
  • GoogleUser.getGrantedScopes() ที่มี google.accounts.oauth2.hasGrantedAllScopes()

นำ การอ้างอิงไคลเอ็นต์ JavaScript ของ Google Sign-In ออกโดยทำดังนี้

วิธีการ:

  • GoogleUser.getAuthResponse()

อัปเดตเว็บแอปด้วย hasGrantedAllScopes() และ hasGrantedAnyScope() โดยทำตามตัวอย่างสิทธิ์แบบละเอียดนี้

ขั้นตอนรหัสการให้สิทธิ์

อัปเดตหรือเพิ่มปลายทางของรหัสการให้สิทธิ์ลงในแพลตฟอร์มแบ็กเอนด์โดยทำตามวิธีการในการจัดการรหัสการให้สิทธิ์

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

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

ตัวอย่างขั้นตอนการให้สิทธิ์โดยนัย

วิธีเดิม

ไลบรารีของไคลเอ็นต์ GAPI

ตัวอย่างไลบรารีของไคลเอ็นต์ Google API สำหรับ JavaScript ที่ทำงานในเบราว์เซอร์โดยใช้กล่องโต้ตอบป๊อปอัปเพื่อขอความยินยอมจากผู้ใช้

โมดูล gapi.auth2 จะโหลดและใช้โดย gapi.client.init() โดยอัตโนมัติ จึงถูกซ่อนไว้

<!DOCTYPE html>
  <html>
    <head>
      <script src="https://apis.google.com/js/api.js"></script>
      <script>
        function start() {
          gapi.client.init({
            'apiKey': 'YOUR_API_KEY',
            'clientId': 'YOUR_CLIENT_ID',
            'scope': 'https://www.googleapis.com/auth/cloud-translation',
            'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
          }).then(function() {
            // Execute an API request which is returned as a Promise.
            // The method name language.translations.list comes from the API discovery.
            return gapi.client.language.translations.list({
              q: 'hello world',
              source: 'en',
              target: 'de',
            });
          }).then(function(response) {
            console.log(response.result.data.translations[0].translatedText);
          }, function(reason) {
            console.log('Error: ' + reason.result.error.message);
          });
        };

        // Load the JavaScript client library and invoke start afterwards.
        gapi.load('client', start);
      </script>
    </head>
    <body>
      <div id="results"></div>
    </body>
  </html>

ไลบรารีของไคลเอ็นต์ JS

OAuth 2.0 สำหรับเว็บแอปพลิเคชันฝั่งไคลเอ็นต์ที่ทำงานในเบราว์เซอร์โดยใช้กล่องโต้ตอบป๊อปอัปเพื่อขอความยินยอมจากผู้ใช้

โมดูล gapi.auth2 จะโหลดด้วยตนเอง

<!DOCTYPE html>
<html><head></head><body>
<script>
  var GoogleAuth;
  var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
  function handleClientLoad() {
    // Load the API's client and auth2 modules.
    // Call the initClient function after the modules load.
    gapi.load('client:auth2', initClient);
  }

  function initClient() {
    // In practice, your app can retrieve one or more discovery documents.
    var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';

    // Initialize the gapi.client object, which app uses to make API requests.
    // Get API key and client ID from Google Cloud console.
    // 'scope' field specifies space-delimited list of access scopes.
    gapi.client.init({
        'apiKey': 'YOUR_API_KEY',
        'clientId': 'YOUR_CLIENT_ID',
        'discoveryDocs': [discoveryUrl],
        'scope': SCOPE
    }).then(function () {
      GoogleAuth = gapi.auth2.getAuthInstance();

      // Listen for sign-in state changes.
      GoogleAuth.isSignedIn.listen(updateSigninStatus);

      // Handle initial sign-in state. (Determine if user is already signed in.)
      var user = GoogleAuth.currentUser.get();
      setSigninStatus();

      // Call handleAuthClick function when user clicks on
      //      "Sign In/Authorize" button.
      $('#sign-in-or-out-button').click(function() {
        handleAuthClick();
      });
      $('#revoke-access-button').click(function() {
        revokeAccess();
      });
    });
  }

  function handleAuthClick() {
    if (GoogleAuth.isSignedIn.get()) {
      // User is authorized and has clicked "Sign out" button.
      GoogleAuth.signOut();
    } else {
      // User is not signed in. Start Google auth flow.
      GoogleAuth.signIn();
    }
  }

  function revokeAccess() {
    GoogleAuth.disconnect();
  }

  function setSigninStatus() {
    var user = GoogleAuth.currentUser.get();
    var isAuthorized = user.hasGrantedScopes(SCOPE);
    if (isAuthorized) {
      $('#sign-in-or-out-button').html('Sign out');
      $('#revoke-access-button').css('display', 'inline-block');
      $('#auth-status').html('You are currently signed in and have granted ' +
          'access to this app.');
    } else {
      $('#sign-in-or-out-button').html('Sign In/Authorize');
      $('#revoke-access-button').css('display', 'none');
      $('#auth-status').html('You have not authorized this app or you are ' +
          'signed out.');
    }
  }

  function updateSigninStatus() {
    setSigninStatus();
  }
</script>

<button id="sign-in-or-out-button"
        style="margin-left: 25px">Sign In/Authorize</button>
<button id="revoke-access-button"
        style="display: none; margin-left: 25px">Revoke access</button>

<div id="auth-status" style="display: inline; padding-left: 25px"></div><hr>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
        onload="this.onload=function(){};handleClientLoad()"
        onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body></html>

ปลายทางของ OAuth 2.0

OAuth 2.0 สำหรับเว็บแอปพลิเคชันฝั่งไคลเอ็นต์ที่ทำงานในเบราว์เซอร์โดยใช้การเปลี่ยนเส้นทางไปยังเซิร์ฟเวอร์การให้สิทธิ์ของ Google เพื่อขอความยินยอมของผู้ใช้

ตัวอย่างนี้แสดงการเรียกใช้ปลายทาง OAuth 2.0 ของ Google โดยตรงจากเบราว์เซอร์ของผู้ใช้ และไม่ได้ใช้โมดูล gapi.auth2 หรือไลบรารี JavaScript

<!DOCTYPE html>
<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';
  var fragmentString = location.hash.substring(1);

  // Parse query string to see if page request is coming from the Google authorization server.
  var params = {};
  var regex = /([^&=]+)=([^&]*)/g, m;
  while (m = regex.exec(fragmentString)) {
    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
  }
  if (Object.keys(params).length > 0) {
    localStorage.setItem('oauth2-test-params', JSON.stringify(params) );
    if (params['state'] && params['state'] == 'try_sample_request') {
      trySampleRequest();
    }
  }

  // If there's an access token, try an API request.
  // Otherwise, start OAuth 2.0 flow.
  function trySampleRequest() {
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    if (params && params['access_token']) {
      var xhr = new XMLHttpRequest();
      xhr.open('GET',
          'https://www.googleapis.com/drive/v3/about?fields=user&' +
          'access_token=' + params['access_token']);
      xhr.onreadystatechange = function (e) {
        if (xhr.readyState === 4 && xhr.status === 200) {
          console.log(xhr.response);
        } else if (xhr.readyState === 4 && xhr.status === 401) {
          // Token invalid, so prompt for user permission.
          oauth2SignIn();
        }
      };
      xhr.send(null);
    } else {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from the Google authorization server.
   */
function oauth2SignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

    // Create element to open OAuth 2.0 endpoint in new window.
    var form = document.createElement('form');
    form.setAttribute('method', 'GET'); // Send as a GET request.
    form.setAttribute('action', oauth2Endpoint);

    // Parameters to pass to OAuth 2.0 endpoint.
    var params = {'client_id': YOUR_CLIENT_ID,
                  'redirect_uri': YOUR_REDIRECT_URI,
                  'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
                  'state': 'try_sample_request',
                  'include_granted_scopes': 'true',
                  'response_type': 'token'};

    // Add form parameters as hidden input values.
    for (var p in params) {
      var input = document.createElement('input');
      input.setAttribute('type', 'hidden');
      input.setAttribute('name', p);
      input.setAttribute('value', params[p]);
      form.appendChild(input);
    }

    // Add form to page and submit it to open the OAuth 2.0 endpoint.
    document.body.appendChild(form);
    form.submit();
  }
</script>

<button onclick="trySampleRequest();">Try sample request</button>
</body></html>

วิธีใหม่

GIS เท่านั้น

ตัวอย่างนี้แสดงเฉพาะไลบรารี JavaScript ของ Google Identity Service ที่ใช้โมเดลโทเค็นและกล่องโต้ตอบป๊อปอัปสำหรับความยินยอมของผู้ใช้ โดยมีไว้เพื่อแสดงจำนวนขั้นตอนขั้นต่ำที่จำเป็นในการกำหนดค่าไคลเอ็นต์ ขอและรับโทเค็นเพื่อการเข้าถึง และเรียกใช้ Google API

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      var access_token;

      function initClient() {
        client = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly \
                  https://www.googleapis.com/auth/contacts.readonly',
          callback: (tokenResponse) => {
            access_token = tokenResponse.access_token;
          },
        });
      }
      function getToken() {
        client.requestAccessToken();
      }
      function revokeToken() {
        google.accounts.oauth2.revoke(access_token, () => {console.log('access token revoked')});
      }
      function loadCalendar() {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
        xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
        xhr.send();
      }
    </script>
    <h1>Google Identity Services Authorization Token model</h1>
    <button onclick="getToken();">Get access token</button><br><br>
    <button onclick="loadCalendar();">Load Calendar</button><br><br>
    <button onclick="revokeToken();">Revoke token</button>
  </body>
</html>

GAPI async/await

ตัวอย่างนี้แสดงวิธีเพิ่มไลบรารี Google Identity Service โดยใช้โมเดลโทเค็น นำโมดูล gapi.auth2 ออก และเรียกใช้ API โดยใช้ไลบรารีของไคลเอ็นต์ Google API สำหรับ JavaScript

ระบบจะใช้ Promises, async และ await เพื่อบังคับใช้ลำดับการโหลดไลบรารี รวมถึงตรวจจับและลองข้อผิดพลาดในการให้สิทธิ์อีกครั้ง การเรียก API จะเกิดขึ้นหลังจากมีโทเค็นเพื่อการเข้าถึงที่ถูกต้องเท่านั้น

ผู้ใช้ควรจะกดปุ่ม "แสดงปฏิทิน" เมื่อไม่มีโทเค็นเพื่อการเข้าถึงเมื่อโหลดหน้าเว็บเป็นครั้งแรก หรือหลังจากนั้นเมื่อโทเค็นเพื่อการเข้าถึงหมดอายุ

<!DOCTYPE html>
<html>
<head>
    <title>GAPI and GIS Example</title>
    <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
    <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoad()"></script>
</head>
<body>
    <h1>GAPI Client with GIS Authorization</h1>
    <button id="authorizeBtn" style="visibility:hidden;">Authorize and Load Events</button>
    <button id="revokeBtn" style="visibility:hidden;">Revoke Access</button>
    <div id="content"></div>

    <script>
        const YOUR_CLIENT_ID = "YOUR_CLIENT_ID";
        const YOUR_API_KEY = 'YOUR_API_KEY';
        const CALENDAR_SCOPE = 'https://www.googleapis.com/auth/calendar.readonly';

        let tokenClient;
        let libsLoaded = 0;

        function gapiLoad() {
            gapi.load('client', initGapiClient);
        }

        async function initGapiClient() {
            try {
                await gapi.client.init({ apiKey: YOUR_API_KEY });
                await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
                console.log('GAPI client initialized.');
                checkAllLoaded();
            } catch (err) {
                handleError('GAPI initialization failed:', err);
            }
        }

        function gisLoad() {
            try {
                tokenClient = google.accounts.oauth2.initTokenClient({
                    client_id: YOUR_CLIENT_ID,
                    scope: CALENDAR_SCOPE,
                    callback: '', // Will be set dynamically
                    error_callback: handleGisError,
                });
                console.log('GIS TokenClient initialized.');
                checkAllLoaded();
            } catch (err) {
                handleError('GIS initialization failed:', err);
            }
        }

        function checkAllLoaded() {
            libsLoaded++;
            if (libsLoaded === 2) {
                document.getElementById('authorizeBtn').style.visibility = 'visible';
                document.getElementById('revokeBtn').style.visibility = 'visible';
                document.getElementById('authorizeBtn').onclick = makeApiCall;
                document.getElementById('revokeBtn').onclick = revokeAccess;
                console.log('Ready to authorize.');
            }
        }

        function handleGisError(err) {
            console.error('GIS Error:', err);
            let message = 'An error occurred during authorization.';
            if (err && err.type === 'popup_failed_to_open') {
                message = 'Failed to open popup. Please disable popup blockers.';
            } else if (err && err.type === 'popup_closed') {
                message = 'Authorization popup was closed.';
            }
            document.getElementById('content').textContent = message;
        }

        function handleError(message, error) {
            console.error(message, error);
            document.getElementById('content').textContent = `${message} ${error.message || JSON.stringify(error)}`;
        }

        async function makeApiCall() {
            document.getElementById('content').textContent = 'Processing...';
            try {
                let token = gapi.client.getToken();
                if (!token || !token.access_token) {
                    console.log('No token, fetching one...');
                    await getToken();
                }

                console.log('Calling Calendar API...');
                const response = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
                displayEvents(response.result);
            } catch (err) {
                console.error('API call failed:', err);
                const errorInfo = err.result && err.result.error;
                if (errorInfo && (errorInfo.code === 401 || (errorInfo.code === 403 && errorInfo.status === "PERMISSION_DENIED"))) {
                    console.log('Auth error on API call, refreshing token...');
                    try {
                        await getToken({ prompt: 'consent' }); // Force refresh
                        const retryResponse = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
                        displayEvents(retryResponse.result);
                    } catch (refreshErr) {
                        handleError('Failed to refresh token or retry API call:', refreshErr);
                    }
                } else {
                    handleError('Error loading events:', err.result ? err.result.error : err);
                }
            }
        }

        async function getToken(options = { prompt: '' }) {
            return new Promise((resolve, reject) => {
                if (!tokenClient) return reject(new Error("GIS TokenClient not initialized."));
                tokenClient.callback = (tokenResponse) => {
                    if (tokenResponse.error) {
                        reject(new Error(`Token Error: ${tokenResponse.error} - ${tokenResponse.error_description}`));
                    } else {
                        console.log('Token acquired.');
                        resolve(tokenResponse);
                    }
                };
                tokenClient.requestAccessToken(options);
            });
        }

        function displayEvents(result) {
            const events = result.items;
            if (events && events.length > 0) {
                let eventList = '<h3>Upcoming Events:</h3><ul>' + events.map(event =>
                    `<li>${event.summary} (${event.start.dateTime || event.start.date})</li>`
                ).join('') + '</ul>';
                document.getElementById('content').innerHTML = eventList;
            } else {
                document.getElementById('content').textContent = 'No upcoming events found.';
            }
        }

        function revokeAccess() {
            const token = gapi.client.getToken();
            if (token && token.access_token) {
                google.accounts.oauth2.revoke(token.access_token, () => {
                    console.log('Access revoked.');
                    document.getElementById('content').textContent = 'Access has been revoked.';
                    gapi.client.setToken(null);
                });
            } else {
                document.getElementById('content').textContent = 'No token to revoke.';
            }
        }
    </script>
</body>
</html>

Callback ของ GAPI

ตัวอย่างนี้แสดงวิธีเพิ่มไลบรารี Google Identity Service โดยใช้โมเดลโทเค็น นำโมดูล gapi.auth2 ออก และเรียกใช้ API โดยใช้ไลบรารีของไคลเอ็นต์ Google API สำหรับ JavaScript

ระบบจะใช้ตัวแปรเพื่อบังคับใช้ลำดับการโหลดไลบรารี ระบบจะเรียกใช้ GAPI จากภายใน Callback หลังจากที่แสดงผลโทเค็นเพื่อการเข้าถึงที่ถูกต้อง

ผู้ใช้จะต้องกดปุ่ม "แสดงปฏิทิน" เมื่อโหลดหน้าเว็บเป็นครั้งแรก และกดอีกครั้งเมื่อต้องการรีเฟรชข้อมูลปฏิทิน

<!DOCTYPE html>
<html>
<head>
  <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
  <script async defer src="https://accounts.google.com/gsi/client" onload="gisInit()"></script>
</head>
<body>
  <h1>GAPI with GIS callbacks</h1>
  <button id="showEventsBtn" onclick="showEvents();">Show Calendar</button><br><br>
  <button id="revokeBtn" onclick="revokeToken();">Revoke access token</button>
  <script>
    let tokenClient;
    let gapiInited;
    let gisInited;

    document.getElementById("showEventsBtn").style.visibility="hidden";
    document.getElementById("revokeBtn").style.visibility="hidden";

    function checkBeforeStart() {
       if (gapiInited && gisInited){
          // Start only when both gapi and gis are initialized.
          document.getElementById("showEventsBtn").style.visibility="visible";
          document.getElementById("revokeBtn").style.visibility="visible";
       }
    }

    function gapiInit() {
      gapi.client.init({
        // NOTE: OAuth2 'scope' and 'client_id' parameters have moved to initTokenClient().
      })
      .then(function() {  // Load the Calendar API discovery document.
        gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
        gapiInited = true;
        checkBeforeStart();
      });
    }

    function gapiLoad() {
        gapi.load('client', gapiInit)
    }

    function gisInit() {
     tokenClient = google.accounts.oauth2.initTokenClient({
                client_id: 'YOUR_CLIENT_ID',
                scope: 'https://www.googleapis.com/auth/calendar.readonly',
                callback: '',  // defined at request time
            });
      gisInited = true;
      checkBeforeStart();
    }

    function showEvents() {

      tokenClient.callback = (resp) => {
        if (resp.error !== undefined) {
          throw(resp);
        }
        // GIS has automatically updated gapi.client with the newly issued access token.
        console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));

        gapi.client.calendar.events.list({ 'calendarId': 'primary' })
        .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
        .catch(err => console.log(err));

        document.getElementById("showEventsBtn").innerText = "Refresh Calendar";
      }

      // Conditionally ask users to select the Google Account they'd like to use,
      // and explicitly obtain their consent to fetch their Calendar.
      // NOTE: To request an access token a user gesture is necessary.
      if (gapi.client.getToken() === null) {
        // Prompt the user to select a Google Account and asked for consent to share their data
        // when establishing a new session.
        tokenClient.requestAccessToken({prompt: 'consent'});
      } else {
        // Skip display of account chooser and consent dialog for an existing session.
        tokenClient.requestAccessToken({prompt: ''});
      }
    }

    function revokeToken() {
      let cred = gapi.client.getToken();
      if (cred !== null) {
        google.accounts.oauth2.revoke(cred.access_token, () => {console.log('Revoked: ' + cred.access_token)});
        gapi.client.setToken('');
        document.getElementById("showEventsBtn").innerText = "Show Calendar";
      }
    }
  </script>
</body>
</html>

ตัวอย่างขั้นตอนรหัสการให้สิทธิ์

UX ป๊อปอัปของไลบรารี Google Identity Service สามารถใช้การเปลี่ยนเส้นทาง URL เพื่อแสดงผลรหัสการให้สิทธิ์ไปยังปลายทางโทเค็นแบ็กเอนด์โดยตรง หรือใช้ Callback Handler ของ JavaScript ที่ทำงานในเบราว์เซอร์ของผู้ใช้ซึ่งทำหน้าที่เป็นพร็อกซีการตอบกลับไปยังแพลตฟอร์มของคุณ ไม่ว่าในกรณีใด แพลตฟอร์มแบ็กเอนด์ของคุณจะทำตามขั้นตอน OAuth 2.0 จนเสร็จสมบูรณ์เพื่อรับโทเค็นเพื่อการเข้าถึงและโทเค็นเพื่อรีเฟรชที่ถูกต้อง

วิธีเดิม

เว็บแอปฝั่งเซิร์ฟเวอร์

Google Sign-In สำหรับแอปฝั่งเซิร์ฟเวอร์ที่ทำงานบนแพลตฟอร์มแบ็กเอนด์โดยใช้การเปลี่ยนเส้นทางไปยัง Google เพื่อขอความยินยอมของผู้ใช้

<!DOCTYPE html>
<html>
  <head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer></script>
    <script>
      function start() {
        gapi.load('auth2', function() {
          auth2 = gapi.auth2.init({
            client_id: 'YOUR_CLIENT_ID',
            api_key: 'YOUR_API_KEY',
            discovery_docs: ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
            // Scopes to request in addition to 'profile' and 'email'
            scope: 'https://www.googleapis.com/auth/cloud-translation',
          });
        });
      }
      function signInCallback(authResult) {
        if (authResult['code']) {
          console.log("sending AJAX request");
          // Send authorization code obtained from Google to backend platform
          $.ajax({
            type: 'POST',
            url: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URL',
            // Always include an X-Requested-With header to protect against CSRF attacks.
            headers: {
              'X-Requested-With': 'XMLHttpRequest'
            },
            contentType: 'application/octet-stream; charset=utf-8',
            success: function(result) {
              console.log(result);
            },
            processData: false,
            data: authResult['code']
          });
        } else {
          console.log('error: failed to obtain authorization code')
        }
      }
    </script>
  </head>
  <body>
    <button id="signinButton">Sign In With Google</button>
    <script>
      $('#signinButton').click(function() {
        // Obtain an authorization code from Google
        auth2.grantOfflineAccess().then(signInCallback);
      });
    </script>
  </body>
</html>

HTTP/REST โดยใช้การเปลี่ยนเส้นทาง

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

/*
 * Create form to request access token from the Google authorization server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
  // Create &lt;form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement('form');
  form.setAttribute('method', 'GET'); // Send as a GET request.
  form.setAttribute('action', oauth2Endpoint);
  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {'client\_id': 'YOUR_CLIENT_ID',
                'redirect\_uri': 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URL',
                'response\_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
                'include\_granted\_scopes': 'true',
                'state': 'pass-through value'};
  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', p);
    input.setAttribute('value', params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

วิธีใหม่

UX ของป๊อปอัป GIS

ตัวอย่างนี้แสดงเฉพาะไลบรารี JavaScript ของ Google Identity Services ที่ใช้โมเดลรหัสการให้สิทธิ์ กล่องโต้ตอบป๊อปอัปสำหรับความยินยอมของผู้ใช้ และ Callback Handler เพื่อรับรหัสการให้สิทธิ์จาก Google ซึ่งมีไว้เพื่อแสดงขั้นตอนขั้นต่ำที่จำเป็นในการกำหนดค่าไคลเอ็นต์ ขอความยินยอม และส่งรหัสการให้สิทธิ์ไปยังแพลตฟอร์มแบ็กเอนด์

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      function initClient() {
        client = google.accounts.oauth2.initCodeClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly',
          ux_mode: 'popup',
          callback: (response) => {
            var code_receiver_uri = 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI',
            // Send auth code to your backend platform
            const xhr = new XMLHttpRequest();
            xhr.open('POST', code_receiver_uri, true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
            xhr.onload = function() {
              console.log('Signed in as: ' + xhr.responseText);
            };
            xhr.send('code=' + response.code);
            // After receipt, the code is exchanged for an access token and
            // refresh token, and the platform then updates this web app
            // running in user's browser with the requested calendar info.
          },
        });
      }
      function getAuthCode() {
        // Request authorization code and obtain user consent
        client.requestCode();
      }
    </script>
    <button onclick="getAuthCode();">Load Your Calendar</button>
  </body>
</html>

UX การเปลี่ยนเส้นทาง GIS

โมเดลรหัสการให้สิทธิ์รองรับโหมด UX แบบป๊อปอัปและการเปลี่ยนเส้นทางเพื่อส่งรหัสการให้สิทธิ์ต่อผู้ใช้ไปยังปลายทางที่โฮสต์โดยแพลตฟอร์มของคุณ โหมด UX ของการเปลี่ยนเส้นทางจะแสดงที่นี่

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      function initClient() {
        client = google.accounts.oauth2.initCodeClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly \
                  https://www.googleapis.com/auth/photoslibrary.readonly',
          ux_mode: 'redirect',
          redirect_uri: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI'
        });
      }
      // Request an access token
      function getAuthCode() {
        // Request authorization code and obtain user consent
        client.requestCode();
      }
    </script>
    <button onclick="getAuthCode();">Load Your Calendar</button>
  </body>
</html>

ไลบรารี JavaScript

Google Identity Services เป็นไลบรารี JavaScript รายการเดียวที่ใช้สำหรับการตรวจสอบสิทธิ์และการให้สิทธิ์ผู้ใช้ ซึ่งรวมและแทนที่ฟีเจอร์และฟังก์ชันการทำงานที่พบในไลบรารีและโมดูลต่างๆ ดังนี้

สิ่งที่ต้องทำเมื่อย้ายข้อมูลไปยังบริการระบุตัวตน

ไลบรารี JS ที่มีอยู่ ไลบรารี JS ใหม่ หมายเหตุ
apis.google.com/js/api.js accounts.google.com/gsi/client เพิ่มไลบรารีใหม่และทำตามขั้นตอนการให้สิทธิ์โดยนัย
apis.google.com/js/client.js accounts.google.com/gsi/client เพิ่มไลบรารีใหม่และทำตามขั้นตอนรหัสการให้สิทธิ์

ข้อมูลอ้างอิงโดยย่อเกี่ยวกับคลัง

การเปรียบเทียบออบเจ็กต์และเมธอดระหว่างไลบรารีไคลเอ็นต์ JavaScript ของ Google Sign-In แบบเก่ากับไลบรารี Google Identity Services แบบใหม่ และหมายเหตุพร้อมข้อมูลเพิ่มเติมและการดำเนินการที่ต้องทำระหว่างการย้ายข้อมูล

เก่า ใหม่ หมายเหตุ
ออบเจ็กต์ GoogleAuth และเมธอดที่เชื่อมโยง
GoogleAuth.attachClickHandler() นำออก
GoogleAuth.currentUser.get() นำออก
GoogleAuth.currentUser.listen() นำออก
GoogleAuth.disconnect() google.accounts.oauth2.revoke แทนที่ของเก่าด้วยของใหม่ การเพิกถอนอาจเกิดขึ้นจาก https://myaccount.google.com/permissions ด้วย
GoogleAuth.grantOfflineAccess() หากต้องการนำออก ให้ทำตามขั้นตอนรหัสการให้สิทธิ์
GoogleAuth.isSignedIn.get() นำออก
GoogleAuth.isSignedIn.listen() นำออก
GoogleAuth.signIn() นำออก
GoogleAuth.signOut() นำออก
GoogleAuth.then() นำออก
ออบเจ็กต์ GoogleUser และเมธอดที่เชื่อมโยง
GoogleUser.disconnect() google.accounts.id.revoke แทนที่ของเก่าด้วยของใหม่ การเพิกถอนอาจเกิดขึ้นจาก https://myaccount.google.com/permissions ด้วย
GoogleUser.getAuthResponse() requestCode() หรือ requestAccessToken() แทนที่รายการเก่าด้วยรายการใหม่
GoogleUser.getBasicProfile() นำออก โปรดใช้โทเค็นระบุตัวตนแทน โดยดูที่หัวข้อการย้ายข้อมูลจาก Google Sign-In
GoogleUser.getGrantedScopes() hasGrantedAnyScope() แทนที่รายการเก่าด้วยรายการใหม่
GoogleUser.getHostedDomain() นำออก
GoogleUser.getId() นำออก
GoogleUser.grantOfflineAccess() หากต้องการนำออก ให้ทำตามขั้นตอนรหัสการให้สิทธิ์
GoogleUser.grant() นำออก
GoogleUser.hasGrantedScopes() hasGrantedAnyScope() แทนที่รายการเก่าด้วยรายการใหม่
GoogleUser.isSignedIn() นำออก
GoogleUser.reloadAuthResponse() requestAccessToken() นำโทเค็นเก่าออก แล้วเรียกใช้โทเค็นใหม่เพื่อแทนที่โทเค็นเพื่อการเข้าถึงที่หมดอายุหรือถูกเพิกถอน
ออบเจ็กต์ gapi.auth2 และเมธอดที่เชื่อมโยง
วัตถุ gapi.auth2.AuthorizeConfig รายการ TokenClientConfig หรือ CodeClientConfig แทนที่รายการเก่าด้วยรายการใหม่
วัตถุ gapi.auth2.AuthorizeResponse รายการ นำออก
วัตถุ gapi.auth2.AuthResponse รายการ นำออก
gapi.auth2.authorize() requestCode() หรือ requestAccessToken() แทนที่รายการเก่าด้วยรายการใหม่
gapi.auth2.ClientConfig() TokenClientConfig หรือ CodeClientConfig แทนที่รายการเก่าด้วยรายการใหม่
gapi.auth2.getAuthInstance() นำออก
gapi.auth2.init() initTokenClient() หรือ initCodeClient() แทนที่รายการเก่าด้วยรายการใหม่
วัตถุ gapi.auth2.OfflineAccessOptions รายการ นำออก
วัตถุ gapi.auth2.SignInOptions รายการ นำออก
ออบเจ็กต์ gapi.signin2 และเมธอดที่เชื่อมโยง
gapi.signin2.render() นำออก การโหลด HTML DOM ขององค์ประกอบ g_id_signin หรือการเรียก JS ไปยัง google.accounts.id.renderButton จะทริกเกอร์การลงชื่อเข้าใช้บัญชี Google ของผู้ใช้

ตัวอย่างข้อมูลเข้าสู่ระบบ

ข้อมูลเข้าสู่ระบบที่มีอยู่

ไลบรารีแพลตฟอร์ม Google Sign-In, ไลบรารีของไคลเอ็นต์ Google API สำหรับ JavaScript หรือการเรียกใช้ปลายทาง Google OAuth 2.0 โดยตรงจะแสดงทั้งโทเค็นเพื่อการเข้าถึง OAuth 2.0 และโทเค็นระบุตัวตน OpenID Connect ในการตอบกลับเดียว

ตัวอย่างการตอบกลับที่มีทั้ง access_token และ id_token

  {
    "token_type": "Bearer",
    "access_token": "ya29.A0ARrdaM-SmArZaCIh68qXsZSzyeU-8mxhQERHrP2EXtxpUuZ-3oW8IW7a6D2J6lRnZrRj8S6-ZcIl5XVEqnqxq5fuMeDDH_6MZgQ5dgP7moY-yTiKR5kdPm-LkuPM-mOtUsylWPd1wpRmvw_AGOZ1UUCa6UD5Hg",
    "scope": "https://www.googleapis.com/auth/calendar.readonly",
    "login_hint": "AJDLj6I2d1RH77cgpe__DdEree1zxHjZJr4Q7yOisoumTZUmo5W2ZmVFHyAomUYzLkrluG-hqt4RnNxrPhArd5y6p8kzO0t8xIfMAe6yhztt6v2E-_Bb4Ec3GLFKikHSXNh5bI-gPrsI",
    "expires_in": 3599,
    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjkzNDFhYmM0MDkyYjZmYzAzOGU0MDNjOTEwMjJkZDNlNDQ1MzliNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTE3NzI2NDMxNjUxOTQzNjk4NjAwIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkJBSW55TjN2MS1ZejNLQnJUMVo0ckEiLCJuYW1lIjoiQnJpYW4gRGF1Z2hlcnR5IiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdnenAyTXNGRGZvbVdMX3VDemRYUWNzeVM3ZGtxTE5ybk90S0QzVXNRPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkJyaWFuIiwiZmFtaWx5X25hbWUiOiJEYXVnaGVydHkiLCJsb2NhbGUiOiJlbiIsImlhdCI6MTYzODk5MTYzOCwiZXhwIjoxNjM4OTk1MjM4LCJqdGkiOiI5YmRkZjE1YWFiNzE2ZDhjYmJmNDYwMmM1YWM3YzViN2VhMDQ5OTA5In0.K3EA-3Adw5HA7O8nJVCsX1HmGWxWzYk3P7ViVBb4H4BoT2-HIgxKlx1mi6jSxIUJGEekjw9MC-nL1B9Asgv1vXTMgoGaNna0UoEHYitySI23E5jaMkExkTSLtxI-ih2tJrA2ggfA9Ekj-JFiMc6MuJnwcfBTlsYWRcZOYVw3QpdTZ_VYfhUu-yERAElZCjaAyEXLtVQegRe-ymScra3r9S92TA33ylMb3WDTlfmDpWL0CDdDzby2asXYpl6GQ7SdSj64s49Yw6mdGELZn5WoJqG7Zr2KwIGXJuSxEo-wGbzxNK-mKAiABcFpYP4KHPEUgYyz3n9Vqn2Tfrgp-g65BQ",
    "session_state": {
      "extraQueryParams": {
        "authuser": "0"
      }
    },
    "first_issued_at": 1638991637982,
    "expires_at": 1638995236982,
    "idpId": "google"
  }

ข้อมูลเข้าสู่ระบบ Google Identity Services

ไลบรารี Google Identity Services จะแสดงผลดังนี้

  • โทเค็นเพื่อการเข้าถึงเมื่อใช้สำหรับการให้สิทธิ์

    {
      "access_token": "ya29.A0ARrdaM_LWSO-uckLj7IJVNSfnUityT0Xj-UCCrGxFQdxmLiWuAosnAKMVQ2Z0LLqeZdeJii3TgULp6hR_PJxnInBOl8UoUwWoqsrGQ7-swxgy97E8_hnzfhrOWyQBmH6zs0_sUCzwzhEr_FAVqf92sZZHphr0g",
      "token_type": "Bearer",
      "expires_in": 3599,
      "scope": "https://www.googleapis.com/auth/calendar.readonly"
    }
    
  • หรือโทเค็นระบุตัวตนเมื่อใช้สำหรับการตรวจสอบสิทธิ์

    {
      "clientId": "538344653255-758c5h5isc45vgk27d8h8deabovpg6to.apps.googleusercontent.com",
      "credential": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxODkyZWI0OWQ3ZWY5YWRmOGIyZTE0YzA1Y2EwZDAzMjcxNGEyMzciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2MzkxNTcyNjQsImF1ZCI6IjUzODM0NDY1MzI1NS03NThjNWg1aXNjNDV2Z2syN2Q4aDhkZWFib3ZwZzZ0by5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjExNzcyNjQzMTY1MTk0MzY5ODYwMCIsIm5vbmNlIjoiZm9vYmFyIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwibmFtZSI6IkJyaWFuIERhdWdoZXJ0eSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQU9oMTRHZ3pwMk1zRkRmb21XTF91Q3pkWFFjc3lTN2RrcUxOcm5PdEtEM1VzUT1zOTYtYyIsImdpdmVuX25hbWUiOiJCcmlhbiIsImZhbWlseV9uYW1lIjoiRGF1Z2hlcnR5IiwiaWF0IjoxNjM5MTU3NTY0LCJleHAiOjE2MzkxNjExNjQsImp0aSI6IjRiOTVkYjAyZjU4NDczMmUxZGJkOTY2NWJiMWYzY2VhYzgyMmI0NjUifQ.Cr-AgMsLFeLurnqyGpw0hSomjOCU4S3cU669Hyi4VsbqnAV11zc_z73o6ahe9Nqc26kPVCNRGSqYrDZPfRyTnV6g1PIgc4Zvl-JBuy6O9HhClAK1HhMwh1FpgeYwXqrng1tifmuotuLQnZAiQJM73Gl-J_6s86Buo_1AIx5YAKCucYDUYYdXBIHLxrbALsA5W6pZCqqkMbqpTWteix-G5Q5T8LNsfqIu_uMBUGceqZWFJALhS9ieaDqoxhIqpx_89QAr1YlGu_UO6R6FYl0wDT-nzjyeF5tonSs3FHN0iNIiR3AMOHZu7KUwZaUdHg4eYkU-sQ01QNY_11keHROCRQ",
      "select_by": "user"
    }
    

การตอบกลับโทเค็นไม่ถูกต้อง

ตัวอย่างการตอบกลับจาก Google เมื่อพยายามส่งคำขอ API โดยใช้โทเค็นเพื่อการเข้าถึงที่หมดอายุ ถูกเพิกถอน หรือไม่ถูกต้อง

ส่วนหัวการตอบกลับ HTTP

  www-authenticate: Bearer realm="https://accounts.google.com/", error="invalid_token"

เนื้อหาการตอบกลับ

  {
    "error": {
      "code": 401,
      "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
      "errors": [
        {
          "message": "Invalid Credentials",
          "domain": "global",
          "reason": "authError",
          "location": "Authorization",
          "locationType": "header"
        }
      ],
      "status": "UNAUTHENTICATED"
    }
  }