การใช้โมเดลโทเค็น

google.accounts.oauth2 ไลบรารี JavaScript ช่วยให้คุณขอความยินยอมจากผู้ใช้และรับโทเค็นการเข้าถึงเพื่อทํางานกับข้อมูลผู้ใช้ได้ ซึ่งอิงตามขั้นตอนการให้สิทธิ์โดยนัยของ OAuth 2.0 และออกแบบมาเพื่อให้คุณเรียกใช้ Google API ได้โดยตรงโดยใช้ REST และ CORS หรือใช้ไลบรารีไคลเอ็นต์ Google API สําหรับ JavaScript (หรือที่เรียกว่า gapi.client) เพื่อเข้าถึง API ที่ซับซ้อนมากขึ้นได้อย่างง่ายดายและยืดหยุ่น

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

ในรูปแบบการให้สิทธิ์ตามโทเค็น คุณไม่จําเป็นต้องจัดเก็บโทเค็นรีเฟรชต่อผู้ใช้ในเซิร์ฟเวอร์แบ็กเอนด์

เราขอแนะนำให้คุณทำตามแนวทางที่ระบุไว้ที่นี่แทนเทคนิคที่ระบุไว้ในคู่มือ OAuth 2.0 สําหรับเว็บแอปพลิเคชันฝั่งไคลเอ็นต์เวอร์ชันเก่า

ตั้งค่า

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

เริ่มต้นไคลเอ็นต์โทเค็น

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

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  callback: (response) => {
    ...
  },
});

ทริกเกอร์ขั้นตอนการสร้างโทเค็น OAuth 2.0

ใช้เมธอด requestAccessToken() เพื่อเรียกใช้ขั้นตอน UX ของโทเค็นและรับโทเค็นการเข้าถึง Google จะแจ้งให้ผู้ใช้ดำเนินการต่อไปนี้

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

ท่าทางสัมผัสของผู้ใช้ทริกเกอร์ขั้นตอนโทเค็น: <button onclick="client.requestAccessToken();">Authorize me</button>

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

ผู้ใช้อาจปิดเครื่องมือเลือกบัญชีหรือหน้าต่างลงชื่อเข้าใช้ ซึ่งในกรณีนี้ ระบบจะไม่เรียกใช้ฟังก์ชันการเรียกกลับ

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

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

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

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

ดูข้อมูลโดยละเอียดได้ที่วิธีจัดการสิทธิ์แบบละเอียด

การให้สิทธิ์เพิ่มเติม

สําหรับเว็บแอป 2 สถานการณ์ระดับสูงต่อไปนี้จะแสดงการให้สิทธิ์เพิ่มเติมโดยใช้

  • แอป Ajax แบบหน้าเว็บเดียว ซึ่งมักใช้ XMLHttpRequest ที่มีการเข้าถึงทรัพยากรแบบไดนามิก
  • หน้าเว็บหลายหน้าและทรัพยากรจะแยกกันและจัดการในแต่ละหน้า

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

อาแจ๊กซ์

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

แอป Ajax
เริ่มต้นคลีเอนต์โทเค็นเมื่อโหลดหน้าเว็บ
        const client = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_GOOGLE_CLIENT_ID',
          callback: "onTokenResponse",
        });
      
ขอความยินยอมและรับโทเค็นการเข้าถึงผ่านท่าทางสัมผัสของผู้ใช้ โดยคลิกเครื่องหมาย "+" เพื่อเปิด

เอกสารน่าอ่าน

แสดงเอกสารล่าสุด

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/documents.readonly'
             })
           );
        

กิจกรรมที่กำลังจะมีขึ้น

แสดงข้อมูลปฏิทิน

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/calendar.readonly'
             })
           );
        

แสดงรูปภาพ

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/photoslibrary.readonly'
             })
           );
        

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

หน้าเว็บหลายหน้า

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

แอปแบบหลายหน้า
หน้าเว็บ รหัส
หน้า 1 เอกสารที่ควรอ่าน
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/documents.readonly',
  });
  client.requestAccessToken();
          
หน้า 2 กิจกรรมที่กําลังจะมีขึ้น
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/calendar.readonly',
  });
  client.requestAccessToken();
          
หน้า 3 ภาพสไลด์
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/photoslibrary.readonly',
  });
  client.requestAccessToken();
          

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

สิทธิ์แบบละเอียด

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

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly \
          https://www.googleapis.com/auth/documents.readonly \
          https://www.googleapis.com/auth/photoslibrary.readonly',
  callback: (tokenResponse) => {
    if (tokenResponse && tokenResponse.access_token) {
      if (google.accounts.oauth2.hasGrantedAnyScope(tokenResponse,
          'https://www.googleapis.com/auth/photoslibrary.readonly')) {
        // Look at pictures
        ...
      }
      if (google.accounts.oauth2.hasGrantedAllScopes(tokenResponse,
          'https://www.googleapis.com/auth/calendar.readonly',
          'https://www.googleapis.com/auth/documents.readonly')) {
        // Meeting planning and review documents
        ...
      }
    }
  },
});

เงินสนับสนุนที่ยอมรับก่อนหน้านี้จากเซสชันหรือคำขอก่อนหน้าจะรวมอยู่ในคำตอบด้วย ระบบจะเก็บบันทึกความยินยอมของผู้ใช้ต่อผู้ใช้และรหัสไคลเอ็นต์ และบันทึกดังกล่าวจะยังคงอยู่ในการเรียกใช้ initTokenClient() หรือ requestAccessToken() หลายครั้ง โดยค่าเริ่มต้น ความยินยอมของผู้ใช้จําเป็นเฉพาะในครั้งแรกที่ผู้ใช้เข้าชมเว็บไซต์และขอขอบเขตใหม่ แต่อาจขอเมื่อโหลดหน้าเว็บทุกครั้งโดยใช้ prompt=consent ในออบเจ็กต์การกําหนดค่าไคลเอ็นต์โทเค็น

การทำงานกับโทเค็น

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

การใช้ REST และ CORS กับ Google APIs

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

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

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
xhr.setRequestHeader('Authorization', 'Bearer ' + tokenResponse.access_token);
xhr.send();

ดูข้อมูลเพิ่มเติมได้ที่วิธีใช้ CORS เพื่อเข้าถึง Google API

ส่วนถัดไปจะอธิบายวิธีผสานรวมกับ API ที่ซับซ้อนมากขึ้นอย่างง่ายดาย

การใช้ไลบรารี JavaScript ของ Google APIs

ไคลเอ็นต์โทเค็นทํางานร่วมกับไลบรารีของไคลเอ็นต์ Google API สําหรับ JavaScript ดูข้อมูลโค้ดด้านล่าง

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  callback: (tokenResponse) => {
    if (tokenResponse && tokenResponse.access_token) {
      gapi.client.setApiKey('YOUR_API_KEY');
      gapi.client.load('calendar', 'v3', listUpcomingEvents);
    }
  },
});

function listUpcomingEvents() {
  gapi.client.calendar.events.list(...);
}

การหมดอายุของโทเค็น

โทเค็นการเข้าถึงมีอายุการใช้งานสั้นตามการออกแบบ หากโทเค็นการเข้าถึงหมดอายุก่อนสิ้นสุดเซสชันของผู้ใช้ ให้รับโทเค็นใหม่โดยเรียกใช้ requestAccessToken() จากเหตุการณ์ที่ผู้ใช้ดำเนินการ เช่น การกดปุ่ม

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

google.accounts.oauth2.revoke('414a76cb127a7ece7ee4bf287602ca2b56f8fcbf7fcecc2cd4e0509268120bd7', done => {
    console.log(done);
    console.log(done.successful);
    console.log(done.error);
    console.log(done.error_description);
  });