การใช้ OAuth 2.0 สำหรับแอปพลิเคชันเว็บเซิร์ฟเวอร์

เอกสารนี้อธิบายวิธีที่แอปพลิเคชันเว็บเซิร์ฟเวอร์ใช้ไลบรารีของไคลเอ็นต์ Google API หรือปลายทาง Google OAuth 2.0 เพื่อใช้การให้สิทธิ์ OAuth 2.0 ในการเข้าถึง Google API

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

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

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

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

ตัวอย่างเฉพาะภาษาในหน้านี้ใช้ไลบรารีของไคลเอ็นต์ Google API เพื่อใช้การให้สิทธิ์ OAuth 2.0 หากต้องการเรียกใช้ตัวอย่างโค้ด คุณต้องติดตั้งไลบรารีของไคลเอ็นต์สำหรับภาษาของคุณก่อน

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

ไลบรารีของไคลเอ็นต์ Google API สำหรับแอปพลิเคชันฝั่งเซิร์ฟเวอร์พร้อมใช้งานในภาษาต่อไปนี้

ข้อกำหนดเบื้องต้น

เปิดใช้ API สำหรับโปรเจ็กต์

แอปพลิเคชันที่เรียกใช้ Google API จะต้องเปิดใช้ API เหล่านั้นในคอนโซล API

วิธีเปิดใช้ API สำหรับโปรเจ็กต์

  1. เปิดคลัง API ในคอนโซล Google API
  2. หากได้รับข้อความแจ้ง ให้เลือกโปรเจ็กต์หรือสร้างโปรเจ็กต์ใหม่
  3. ไลบรารี API จะแสดง API ทั้งหมดที่มีอยู่ โดยจัดกลุ่มตามตระกูลผลิตภัณฑ์และความนิยม หาก API ที่ต้องการเปิดใช้ไม่ปรากฏในรายการ ให้ใช้การค้นหาเพื่อค้นหา API หรือคลิกดูทั้งหมดในกลุ่มผลิตภัณฑ์ที่ API นั้นอยู่
  4. เลือก API ที่ต้องการเปิดใช้ แล้วคลิกปุ่มเปิดใช้
  5. เปิดใช้การเรียกเก็บเงินเมื่อได้รับข้อความแจ้ง
  6. หากได้รับข้อความแจ้ง ให้อ่านและยอมรับข้อกำหนดในการให้บริการของ API

สร้างข้อมูลเข้าสู่ระบบสำหรับการให้สิทธิ์

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

  1. ไปที่หน้าลูกค้า
  2. คลิกสร้างไคลเอ็นต์
  3. เลือกประเภทแอปพลิเคชันเป็นเว็บแอปพลิเคชัน
  4. กรอกแบบฟอร์ม แล้วคลิกสร้าง แอปพลิเคชันที่ใช้ภาษาและเฟรมเวิร์ก เช่น PHP, Java, Python, Ruby และ .NET ต้องระบุ URI เปลี่ยนเส้นทางที่ได้รับอนุญาต URI เปลี่ยนเส้นทางคือปลายทางที่เซิร์ฟเวอร์ OAuth 2.0 สามารถส่งการตอบกลับได้ ปลายทางเหล่านี้ต้องเป็นไปตามกฎการตรวจสอบของ Google

    สำหรับการทดสอบ คุณสามารถระบุ URI ที่อ้างอิงถึงเครื่องในเครื่อง เช่น http://localhost:8080 ตัวอย่างทั้งหมดในเอกสารนี้ใช้ http://localhost:8080 เป็น URI การเปลี่ยนเส้นทาง

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

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

ระบุขอบเขตการเข้าถึง

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

ก่อนเริ่มใช้การให้สิทธิ์ OAuth 2.0 เราขอแนะนำให้คุณระบุขอบเขตที่แอปจะต้องได้รับสิทธิ์เข้าถึง

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

เอกสารขอบเขต OAuth 2.0 API มีรายการขอบเขตทั้งหมดที่คุณอาจใช้เพื่อเข้าถึง Google APIs

ข้อกำหนดเฉพาะภาษา

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

PHP

หากต้องการเรียกใช้ตัวอย่างโค้ด PHP ในเอกสารนี้ คุณจะต้องมีสิ่งต่อไปนี้

  • PHP 8.0 ขึ้นไปที่มีอินเทอร์เฟซบรรทัดคำสั่ง (CLI) และส่วนขยาย JSON ติดตั้งอยู่
  • เครื่องมือการจัดการทรัพยากร Dependency ของ Composer
  • ไลบรารีของไคลเอ็นต์ Google APIs สำหรับ PHP มีดังนี้

    composer require google/apiclient:^2.15.0

ดูข้อมูลเพิ่มเติมได้ที่ไลบรารีของไคลเอ็นต์ Google APIs สำหรับ PHP

Python

หากต้องการเรียกใช้ตัวอย่างโค้ด Python ในเอกสารนี้ คุณจะต้องมีสิ่งต่อไปนี้

  • Python 3.7 ขึ้นไป
  • เครื่องมือการจัดการแพ็กเกจ pip
  • ไลบรารีของไคลเอ็นต์ Google APIs สำหรับ Python 2.0 มีการเผยแพร่ดังนี้
    pip install --upgrade google-api-python-client
  • google-auth, google-auth-oauthlib และ google-auth-httplib2 สำหรับการให้สิทธิ์ผู้ใช้
    pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
  • เฟรมเวิร์กเว็บแอปพลิเคชัน Flask Python
    pip install --upgrade flask
  • ไลบรารี HTTP requests
    pip install --upgrade requests

อ่านบันทึกประจำรุ่นของไลบรารีของไคลเอ็นต์ Python สำหรับ Google API หากคุณอัปเกรด Python และคำแนะนำในการย้ายข้อมูลที่เกี่ยวข้องไม่ได้

Ruby

หากต้องการเรียกใช้ตัวอย่างโค้ด Ruby ในเอกสารนี้ คุณจะต้องมีสิ่งต่อไปนี้

  • Ruby 2.6 ขึ้นไป
  • ไลบรารีการตรวจสอบสิทธิ์ของ Google สำหรับ Ruby:

    gem install googleauth
  • ไลบรารีของไคลเอ็นต์สำหรับ Google APIs ของไดรฟ์และปฏิทินมีดังนี้

    gem install google-apis-drive_v3 google-apis-calendar_v3
  • เฟรมเวิร์กเว็บแอปพลิเคชัน Sinatra Ruby

    gem install sinatra

Node.js

หากต้องการเรียกใช้ตัวอย่างโค้ด Node.js ในเอกสารนี้ คุณจะต้องมีสิ่งต่อไปนี้

  • LTS สำหรับการบำรุงรักษา, LTS ที่ใช้งานอยู่ หรือ Node.js รุ่นปัจจุบัน
  • ไคลเอ็นต์ Node.js ของ Google APIs

    npm install googleapis crypto express express-session

HTTP/REST

คุณไม่จำเป็นต้องติดตั้งไลบรารีใดๆ เพื่อให้สามารถเรียกใช้ปลายทาง OAuth 2.0 ได้โดยตรง

การรับโทเค็นเพื่อการเข้าถึง OAuth 2.0

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

รายการต่อไปนี้จะสรุปขั้นตอนเหล่านี้อย่างรวดเร็ว

  1. แอปพลิเคชันจะระบุสิทธิ์ที่ต้องการ
  2. แอปพลิเคชันจะเปลี่ยนเส้นทางผู้ใช้ไปยัง Google พร้อมกับรายการสิทธิ์ที่ขอ
  3. ผู้ใช้จะตัดสินใจว่าจะให้สิทธิ์แก่แอปพลิเคชันของคุณหรือไม่
  4. แอปพลิเคชันจะค้นหาสิ่งที่ผู้ใช้ตัดสินใจ
  5. หากผู้ใช้ให้สิทธิ์ที่ขอ แอปพลิเคชันจะดึงข้อมูลโทเค็นที่จำเป็นในการส่งคำขอ API ในนามของผู้ใช้

ขั้นตอนที่ 1: ตั้งค่าพารามิเตอร์การให้สิทธิ์

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

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

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

PHP

ข้อมูลโค้ดต่อไปนี้จะสร้างออบเจ็กต์ Google\Client() ซึ่งกำหนดพารามิเตอร์ในคำขอการให้สิทธิ์

ออบเจ็กต์ดังกล่าวจะใช้ข้อมูลจากไฟล์ client_secret.json เพื่อระบุแอปพลิเคชันของคุณ (ดูข้อมูลเพิ่มเติมเกี่ยวกับไฟล์ดังกล่าวได้ที่การสร้างข้อมูลเข้าสู่ระบบเพื่อการให้สิทธิ์) ออบเจ็กต์ยังระบุขอบเขตที่แอปพลิเคชันของคุณขอสิทธิ์เข้าถึงและ URL ไปยังปลายทางการตรวจสอบสิทธิ์ของแอปพลิเคชัน ซึ่งจะจัดการการตอบกลับจากเซิร์ฟเวอร์ OAuth 2.0 ของ Google สุดท้าย โค้ดจะตั้งค่าพารามิเตอร์ access_type และ include_granted_scopes ที่ไม่บังคับ

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

use Google\Client;

$client = new Client();

// Required, call the setAuthConfig function to load authorization credentials from
// client_secret.json file.
$client->setAuthConfig('client_secret.json');

// Required, to set the scope value, call the addScope function
$client->addScope([Google\Service\Drive::DRIVE_METADATA_READONLY, Google\Service\Calendar::CALENDAR_READONLY]);

// Required, call the setRedirectUri function to specify a valid redirect URI for the
// provided client_id
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');

// Recommended, offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType('offline');

// Recommended, call the setState function. Using a state value can increase your assurance that
// an incoming connection is the result of an authentication request.
$client->setState($sample_passthrough_value);

// Optional, if your application knows which user is trying to authenticate, it can use this
// parameter to provide a hint to the Google Authentication Server.
$client->setLoginHint('hint@example.com');

// Optional, call the setPrompt function to set "consent" will prompt the user for consent
$client->setPrompt('consent');

// Optional, call the setIncludeGrantedScopes function with true to enable incremental
// authorization
$client->setIncludeGrantedScopes(true);

Python

ข้อมูลโค้ดต่อไปนี้ใช้โมดูล google-auth-oauthlib.flow เพื่อสร้างคำขอการให้สิทธิ์

โค้ดจะสร้างออบเจ็กต์ Flow ซึ่งระบุแอปพลิเคชันของคุณโดยใช้ข้อมูลจากไฟล์ client_secret.json ที่คุณดาวน์โหลดหลังจากสร้างข้อมูลเข้าสู่ระบบสำหรับการให้สิทธิ์ ออบเจ็กต์ดังกล่าวยังระบุขอบเขตที่แอปพลิเคชันของคุณขอสิทธิ์เข้าถึงและ URL ไปยังปลายทางการให้สิทธิ์ของแอปพลิเคชัน ซึ่งจะจัดการการตอบกลับจากเซิร์ฟเวอร์ OAuth 2.0 ของ Google สุดท้าย โค้ดจะตั้งค่าพารามิเตอร์ access_type และ include_granted_scopes ที่ไม่บังคับ

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

import google.oauth2.credentials
import google_auth_oauthlib.flow

# Required, call the from_client_secrets_file method to retrieve the client ID from a
# client_secret.json file. The client ID (from that file) and access scopes are required. (You can
# also use the from_client_config method, which passes the client configuration as it originally
# appeared in a client secrets file but doesn't access the file itself.)
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly',
            'https://www.googleapis.com/auth/calendar.readonly'])

# Required, indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
flow.redirect_uri = 'https://www.example.com/oauth2callback'

# Generate URL for request to Google's OAuth 2.0 server.
# Use kwargs to set optional request parameters.
authorization_url, state = flow.authorization_url(
    # Recommended, enable offline access so that you can refresh an access token without
    # re-prompting the user for permission. Recommended for web server apps.
    access_type='offline',
    # Optional, enable incremental authorization. Recommended as a best practice.
    include_granted_scopes='true',
    # Optional, if your application knows which user is trying to authenticate, it can use this
    # parameter to provide a hint to the Google Authentication Server.
    login_hint='hint@example.com',
    # Optional, set prompt to 'consent' will prompt the user for consent
    prompt='consent')

Ruby

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

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

require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'

require 'google/apis/drive_v3'
require 'google/apis/calendar_v3'

# Required, call the from_file method to retrieve the client ID from a
# client_secret.json file.
client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json')

# Required, scope value 
# Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY',
         'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY']

# Required, Authorizers require a storage instance to manage long term persistence of
# access and refresh tokens.
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)

# Required, indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
callback_uri = '/oauth2callback'

# To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
# from the client_secret.json file. To get these credentials for your application, visit
# https://console.cloud.google.com/apis/credentials.
authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope,
                                                token_store, callback_uri)

แอปพลิเคชันจะใช้ออบเจ็กต์ไคลเอ็นต์เพื่อดำเนินการ OAuth 2.0 เช่น การสร้าง URL คำขอการให้สิทธิ์และการใช้โทเค็นเพื่อการเข้าถึงกับคำขอ HTTP

Node.js

ข้อมูลโค้ดต่อไปนี้จะสร้างออบเจ็กต์ google.auth.OAuth2 ซึ่งกำหนดพารามิเตอร์ในคำขอการให้สิทธิ์

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

const {google} = require('googleapis');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');

/**
 * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
 * from the client_secret.json file. To get these credentials for your application, visit
 * https://console.cloud.google.com/apis/credentials.
 */
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
const scopes = [
  'https://www.googleapis.com/auth/drive.metadata.readonly',
  'https://www.googleapis.com/auth/calendar.readonly'
];

// Generate a secure random state value.
const state = crypto.randomBytes(32).toString('hex');

// Store state in the session
req.session.state = state;

// Generate a url that asks permissions for the Drive activity and Google Calendar scope
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  /** Pass in the scopes array defined above.
    * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
  scope: scopes,
  // Enable incremental authorization. Recommended as a best practice.
  include_granted_scopes: true,
  // Include the state parameter to reduce the risk of CSRF attacks.
  state: state
});

หมายเหตุสำคัญ - ระบบจะแสดงผล refresh_token เฉพาะในการให้สิทธิ์ครั้งแรกเท่านั้น ดูรายละเอียดเพิ่มเติมได้ ที่นี่

HTTP/REST

ปลายทาง OAuth 2.0 ของ Google อยู่ที่ https://accounts.google.com/o/oauth2/v2/auth ปลายทางนี้เข้าถึงได้ผ่าน HTTPS เท่านั้น ระบบจะปฏิเสธการเชื่อมต่อ HTTP แบบธรรมดา

เซิร์ฟเวอร์การให้สิทธิ์ของ Google รองรับพารามิเตอร์สตริงการค้นหาต่อไปนี้สำหรับแอปพลิเคชันเว็บเซิร์ฟเวอร์

พารามิเตอร์
client_id จำเป็น

รหัสไคลเอ็นต์สำหรับแอปพลิเคชันของคุณ คุณดูค่านี้ได้ในหน้าไคลเอ็นต์ของคอนโซล Cloud

redirect_uri จำเป็น

กำหนดตำแหน่งที่เซิร์ฟเวอร์ API จะเปลี่ยนเส้นทางผู้ใช้หลังจากที่ผู้ใช้ทำขั้นตอนการให้สิทธิ์เสร็จสมบูรณ์ ค่าต้องตรงกับ URI การเปลี่ยนเส้นทางที่ได้รับอนุญาตรายการใดรายการหนึ่งสำหรับไคลเอ็นต์ OAuth 2.0 ซึ่งคุณกำหนดค่าไว้ในหน้าไคลเอ็นต์ของ Cloud Console ของไคลเอ็นต์ หากค่านี้ไม่ตรงกับ URI การเปลี่ยนเส้นทางที่ได้รับอนุญาตสำหรับ client_id ที่ระบุ คุณจะได้รับข้อผิดพลาด redirect_uri_mismatch

โปรดทราบว่ารูปแบบ http หรือ https, ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ และเครื่องหมายทับต่อท้าย ('/') ต้องตรงกันทั้งหมด

response_type จำเป็น

กำหนดว่าปลายทาง Google OAuth 2.0 จะแสดงผลรหัสการให้สิทธิ์หรือไม่

ตั้งค่าพารามิเตอร์เป็น code สำหรับแอปพลิเคชันเว็บเซิร์ฟเวอร์

scope จำเป็น

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

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

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

access_type แนะนำ

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

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

state แนะนำ

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

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

include_granted_scopes ไม่บังคับ

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

login_hint ไม่บังคับ

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

ตั้งค่าพารามิเตอร์เป็นอีเมลหรือตัวระบุ sub ซึ่งเทียบเท่ากับรหัส Google ของผู้ใช้

prompt ไม่บังคับ

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

ค่าที่เป็นไปได้มีดังนี้

none อย่าแสดงหน้าจอการตรวจสอบสิทธิ์หรือหน้าจอขอความยินยอม ต้องไม่ระบุค่าอื่นๆ
consent แจ้งให้ผู้ใช้ให้ความยินยอม
select_account แจ้งให้ผู้ใช้เลือกบัญชี

ขั้นตอนที่ 2: เปลี่ยนเส้นทางไปยังเซิร์ฟเวอร์ OAuth 2.0 ของ Google

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

PHP

  1. สร้าง URL เพื่อขอสิทธิ์เข้าถึงจากเซิร์ฟเวอร์ OAuth 2.0 ของ Google โดยทำดังนี้
    $auth_url = $client->createAuthUrl();
  2. เปลี่ยนเส้นทางผู้ใช้ไปยัง $auth_url โดยทำดังนี้
    header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));

Python

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

return flask.redirect(authorization_url)

Ruby

  1. สร้าง URL เพื่อขอสิทธิ์เข้าถึงจากเซิร์ฟเวอร์ OAuth 2.0 ของ Google โดยทำดังนี้
    auth_uri = authorizer.get_authorization_url(request: request)
  2. เปลี่ยนเส้นทางผู้ใช้ไปยัง auth_uri

Node.js

  1. ใช้ URL authorizationUrl ที่สร้างขึ้นจากเมธอดgenerateAuthUrlในขั้นตอนที่ 1 เพื่อขอสิทธิ์เข้าถึงจากเซิร์ฟเวอร์ OAuth 2.0 ของ Google
  2. เปลี่ยนเส้นทางผู้ใช้ไปยัง authorizationUrl
    res.redirect(authorizationUrl);

HTTP/REST

ตัวอย่างการเปลี่ยนเส้นทางไปยังเซิร์ฟเวอร์การให้สิทธิ์ของ Google

ตัวอย่าง URL แสดงอยู่ด้านล่าง โดยมีการขึ้นบรรทัดใหม่และเว้นวรรคเพื่อให้อ่านง่าย

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 access_type=offline&
 include_granted_scopes=true&
 response_type=code&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//developers.google.com/oauthplayground&
 client_id=client_id

หลังจากสร้าง URL คำขอแล้ว ให้เปลี่ยนเส้นทางผู้ใช้ไปยัง URL ดังกล่าว

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

ขั้นตอนที่ 3: Google แจ้งให้ผู้ใช้ให้ความยินยอม

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

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

ข้อผิดพลาด

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

admin_policy_enforced

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

disallowed_useragent

ปลายทางการให้สิทธิ์จะแสดงภายใน User Agent ที่ฝังไว้ซึ่งไม่ได้รับอนุญาตตามนโยบาย OAuth 2.0 ของ Google

นักพัฒนาแอป iOS และ macOS อาจพบข้อผิดพลาดนี้เมื่อเปิดคำขอการให้สิทธิ์ใน WKWebView นักพัฒนาแอปควรใช้ไลบรารี iOS เช่น Google Sign-In สำหรับ iOS หรือ AppAuth สำหรับ iOS ของ OpenID Foundation แทน

นักพัฒนาเว็บอาจพบข้อผิดพลาดนี้เมื่อแอป iOS หรือ macOS เปิดเว็บลิงก์ทั่วไปใน User Agent ที่ฝังไว้ และผู้ใช้ไปยังปลายทางการให้สิทธิ์ OAuth 2.0 ของ Google จากเว็บไซต์ของคุณ นักพัฒนาแอปควรอนุญาตให้ลิงก์ทั่วไปเปิดในเครื่องมือจัดการลิงก์เริ่มต้นของระบบปฏิบัติการ ซึ่งรวมถึงทั้งเครื่องมือจัดการ Universal Link หรือแอปเบราว์เซอร์เริ่มต้น ไลบรารี SFSafariViewController ก็เป็นตัวเลือกที่รองรับเช่นกัน

org_internal

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

invalid_client

รหัสลับไคลเอ็นต์ OAuth ไม่ถูกต้อง ตรวจสอบการกำหนดค่าไคลเอ็นต์ OAuth รวมถึงรหัสไคลเอ็นต์และรหัสลับที่ใช้สำหรับคำขอนี้

deleted_client

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

invalid_grant

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

redirect_uri_mismatch

redirect_uri ที่ส่งในคำขอการให้สิทธิ์ไม่ตรงกับ URI การเปลี่ยนเส้นทางที่ได้รับอนุญาตสำหรับรหัสไคลเอ็นต์ OAuth ตรวจสอบ URI การเปลี่ยนเส้นทางที่ได้รับอนุญาตในหน้าไคลเอ็นต์ของคอนโซล Google Cloud

พารามิเตอร์ redirect_uri อาจหมายถึงขั้นตอน OAuth นอกแบนด์ (OOB) ที่เลิกใช้งานแล้วและไม่รองรับอีกต่อไป ดูคำแนะนำในการย้ายข้อมูลเพื่ออัปเดตการผสานรวม

invalid_request

เกิดข้อผิดพลาดกับคำขอของคุณ ซึ่งอาจเกิดจากสาเหตุหลายประการดังนี้

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

ขั้นตอนที่ 4: จัดการการตอบกลับของเซิร์ฟเวอร์ OAuth 2.0

เซิร์ฟเวอร์ OAuth 2.0 จะตอบสนองต่อคำขอเข้าถึงของแอปพลิเคชันโดยใช้ URL ที่ระบุในคำขอ

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

การตอบกลับข้อผิดพลาด:

https://oauth2.example.com/auth?error=access_denied

การตอบกลับรหัสการให้สิทธิ์:

https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7

ตัวอย่างการตอบกลับของเซิร์ฟเวอร์ OAuth 2.0

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

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 access_type=offline&
 include_granted_scopes=true&
 response_type=code&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//developers.google.com/oauthplayground&
 client_id=client_id

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

ขั้นตอนที่ 5: แลกเปลี่ยนรหัสการให้สิทธิ์เป็นโทเค็นการรีเฟรชและโทเค็นเพื่อการเข้าถึง

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

PHP

หากต้องการแลกรหัสการให้สิทธิ์เป็นโทเค็นเพื่อการเข้าถึง ให้ใช้เมธอด fetchAccessTokenWithAuthCode ดังนี้

$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);

Python

ในหน้า Callback ให้ใช้ไลบรารี google-auth เพื่อยืนยันการตอบกลับของเซิร์ฟเวอร์การให้สิทธิ์ จากนั้นใช้เมธอด flow.fetch_token เพื่อแลกรหัสการให้สิทธิ์ในการตอบกลับนั้นเป็นโทเค็นเพื่อการเข้าถึง

state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'],
    state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)

# Store the credentials in browser session storage, but for security: client_id, client_secret,
# and token_uri are instead stored only on the backend server.
credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'granted_scopes': credentials.granted_scopes}

Ruby

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

  target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
  redirect target_url

Node.js

หากต้องการแลกรหัสการให้สิทธิ์เป็นโทเค็นเพื่อการเข้าถึง ให้ใช้เมธอด getToken ดังนี้

const url = require('url');

// Receive the callback from Google's OAuth 2.0 server.
app.get('/oauth2callback', async (req, res) => {
  let q = url.parse(req.url, true).query;

  if (q.error) { // An error response e.g. error=access_denied
    console.log('Error:' + q.error);
  } else if (q.state !== req.session.state) { //check state value
    console.log('State mismatch. Possible CSRF attack');
    res.end('State mismatch. Possible CSRF attack');
  } else { // Get access and refresh tokens (if access_type is offline)

    let { tokens } = await oauth2Client.getToken(q.code);
    oauth2Client.setCredentials(tokens);
});

HTTP/REST

หากต้องการแลกรหัสการให้สิทธิ์เป็นโทเค็นเพื่อการเข้าถึง ให้เรียกใช้ปลายทาง https://oauth2.googleapis.com/token และตั้งค่าพารามิเตอร์ต่อไปนี้

ช่อง
client_id รหัสไคลเอ็นต์ที่ได้จากหน้าไคลเอ็นต์ของ Cloud Console
client_secret ไม่บังคับ

รหัสลับไคลเอ็นต์ที่ได้จากหน้าไคลเอ็นต์ของ Cloud Console

code รหัสการให้สิทธิ์ที่แสดงผลจากคำขอเริ่มต้น
grant_type ตามที่กำหนดไว้ในข้อกำหนด OAuth 2.0 ค่าของช่องนี้ต้องตั้งค่าเป็น authorization_code
redirect_uri URI การเปลี่ยนเส้นทางรายการใดรายการหนึ่งที่แสดงสำหรับโปรเจ็กต์ของคุณในหน้าไคลเอ็นต์ของ Cloud Console สำหรับ client_id ที่ระบุ

แม้ว่าการใช้ DPoP จะเป็นตัวเลือก แต่เราขอแนะนำให้ใช้เพื่อเพิ่มความปลอดภัย ความปลอดภัยของ DPoP ขึ้นอยู่กับการจำกัดคีย์ส่วนตัวไว้ในอุปกรณ์เดียว เราขอแนะนำให้จัดเก็บคีย์ในลักษณะที่ไม่สามารถคัดลอกออกจากอุปกรณ์ได้ เช่น โดยใช้ TPM, Secure Enclave หรือคีย์สโตร์อื่นๆ ที่สนับสนุนโดยฮาร์ดแวร์ หากต้องการใช้ DPoP แอปพลิเคชันของคุณต้องสร้าง JWT หลักฐาน DPoP ใหม่ที่ไม่ซ้ำกันสำหรับคำขอแต่ละรายการไปยังปลายทางโทเค็น และเพิ่มเป็นส่วนหัวของคำขอ HTTP

ส่วนหัว ต้องระบุ คำอธิบาย
DPoP ไม่บังคับ หลักฐาน DPoP คือ JWT ที่พิสูจน์การครอบครองคีย์ส่วนตัว นี่คือส่วนหัว ไม่ใช่พารามิเตอร์ หากระบุไว้ โทเค็นที่แสดงผลจะเชื่อมโยงกับคีย์นี้ ต้องสร้างหลักฐานใหม่ที่ไม่ซ้ำกันสำหรับคำขอแต่ละรายการ และต้องรวมการอ้างสิทธิ์ htm (เมธอด HTTP) และ htu (HTTP URI) ที่ตรงกับคำขอ

ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่างคำขอ

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\
 VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\
 nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\
 QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\
 oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\
 WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\
 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
redirect_uri=https%3A//developers.google.com/oauthplayground&
grant_type=authorization_code

สร้างหลักฐาน DPoP

ขั้นตอนต่อไปนี้แสดงวิธีสร้างหลักฐาน DPoP โดยใช้ OpenSSL จากบรรทัดคำสั่ง

  1. สร้างคู่คีย์ EC P-256
    openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem
    openssl ec -in dpop_private.pem -pubout -out dpop_public.pem
  2. สร้างส่วนหัว DPoP

    ส่วนหัวต้องมีคำกล่าวอ้าง typ, alg และ jwk (คีย์สาธารณะ) ค่า x และ y คือพิกัดที่เข้ารหัส Base64Url ของคีย์สาธารณะ EC เข้ารหัส Base64Url สำหรับ JSON นี้

    {
      "typ":"dpop+jwt",
      "alg":"ES256",
      "jwk": {
        "kty":"EC",
        "x":"YOUR_PUBLIC_KEY_X",
        "y":"YOUR_PUBLIC_KEY_Y",
        "crv":"P-256"
      }
    }
  3. สร้างเพย์โหลด DPoP

    เพย์โหลดต้องมี jti (ตัวระบุที่ไม่ซ้ำกันสำหรับคำขอ), htm (เมธอด HTTP เช่น POST), htu (HTTP URI เช่น https://oauth2.googleapis.com/token) และ iat (เวลาที่ออก) หากคุณได้รับ Nonce จากเซิร์ฟเวอร์ในส่วนหัว DPoP-Nonce ในการตอบกลับคำขอก่อนหน้า คุณต้องใส่ค่า Nonce นั้นในการอ้างสิทธิ์ nonce การอ้างสิทธิ์ Nonce เป็นตัวเลือกสำหรับการแลกเปลี่ยนรหัสการให้สิทธิ์ และจะใช้เมื่อได้รับส่วนหัว DPoP-Nonce ก่อนหน้านี้เท่านั้น เข้ารหัส Base64Url สำหรับ JSON นี้

    {
      "jti":"JTI_VALUE",
      "htm":"POST",
      "htu":"https://oauth2.googleapis.com/token",
      "iat":YOUR_JWT_ISSUED_TIME,
      "nonce":"SERVER_PROVIDED_NONCE"
    }

    ค่า jti จะขึ้นอยู่กับประเภทของ Exchange ดังนี้

    • สำหรับการแลกเปลี่ยนรหัสการให้สิทธิ์ jti ต้องเป็นแฮช SHA256 ที่เข้ารหัส Base64Url ของรหัสการให้สิทธิ์: "jti":"BASE64URL(SHA256(AUTHORIZATION_CODE))"
    • สำหรับการแลกเปลี่ยนโทเค็นการรีเฟรช jti ต้องเป็นตัวระบุที่ไม่ซ้ำกันต่อคำขอ: "jti":"YOUR_UNIQUE_PER_REQUEST_IDENTIFIER"
  4. ลงนามในหลักฐาน:

    เชื่อมโยงส่วนหัวและเพย์โหลดที่เข้ารหัสด้วยจุด (.) จากนั้นลงนามผลลัพธ์ด้วยคีย์ส่วนตัวโดยใช้ ES256 โปรดทราบว่า JWS กำหนดให้ลายเซ็นต้องอยู่ในรูปแบบ R | S ที่เชื่อมโยงแบบข้อมูลดิบ (64 ไบต์สำหรับ P-256) หากใช้ OpenSSL โดยตรง คุณต้องแปลงลายเซ็นที่เข้ารหัส ASN.1 DER เริ่มต้นเป็นรูปแบบข้อมูลดิบนี้

การแลกเปลี่ยนที่สำเร็จจะแสดงด้วยการตอบกลับ 200 OK ที่มีโทเค็น หากใช้หลักฐาน DPoP ที่ถูกต้องในระหว่างการแลกเปลี่ยน โทเค็นการรีเฟรชที่ Google แสดงผลจะเชื่อมโยงกับคีย์ของคุณด้วย DPoP แต่โทเค็นเพื่อการเข้าถึงจะไม่เชื่อมโยงกับ DPoP โทเค็นเพื่อการเข้าถึงจะยังคงมี token_type ของ Bearer แทนที่จะเป็น DPoP นอกจากนี้ Google จะแสดงผลส่วนหัว HTTP DPoP-Nonce ในการตอบกลับด้วย ไคลเอ็นต์ต้องแคช Nonce นี้และรวมไว้ในการอ้างสิทธิ์ nonce ของหลักฐาน DPoP ในคำขอต่อๆ ไป (เช่น เมื่อแลกเปลี่ยนโทเค็นการรีเฟรชเป็นโทเค็นเพื่อการเข้าถึงใหม่ หรือเมื่อเรียกใช้ API ที่ได้รับการปกป้องด้วย DPoP) การใช้ Nonce ที่ออกก่อนเวลานี้จะช่วยหลีกเลี่ยงความล้มเหลวในการส่งไปกลับเพิ่มเติม (use_dpop_nonce) ในคำขอถัดไปได้

ต้องระบุหลักฐาน DPoP สำหรับคำขอแลกเปลี่ยนโทเค็นที่ส่งด้วยโทเค็นการรีเฟรชที่เชื่อมโยงกับ DPoP

การแลกเปลี่ยนที่ล้มเหลวจะเกิดขึ้นหากไม่มีส่วนหัว DPoP เมื่อคาดไว้ ไม่ถูกต้อง หรือหากหลักฐานใช้คีย์อื่นที่ไม่ใช่คีย์ที่เชื่อมโยงกับโทเค็น ในกรณีเหล่านี้ เซิร์ฟเวอร์จะแสดงข้อผิดพลาด 400 Bad Request หากหลักฐาน DPoP มีการอ้างสิทธิ์ htm หรือ htu ที่ไม่ตรงกัน, iat ที่หมดอายุ, jti ที่ใช้ซ้ำ หรือลายเซ็นที่ไม่ถูกต้อง Google จะแสดงรหัสข้อผิดพลาด invalid_dpop_proof หากจำเป็นต้องใช้ Nonce ของ DPoP เช่น ในระหว่างการแลกเปลี่ยนโทเค็นการรีเฟรช และการพิสูจน์ DPoP ไม่มีคำกล่าวอ้าง nonce หรือค่า Nonce ไม่เป็นที่ยอมรับสำหรับเซิร์ฟเวอร์ (เช่น หมดอายุ มีการใช้งานแล้ว หรือไม่ถูกต้อง) Google จะแสดงรหัสข้อผิดพลาด use_dpop_nonce พร้อมกับส่วนหัว HTTP DPoP-Nonce ที่มี Nonce ใหม่ซึ่งคุณสามารถใช้ในคำขอต่อๆ ไปได้ ข้อผิดพลาดอื่นๆ อาจแสดงผลเป็น invalid_grant

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

การตอบกลับจะมีฟิลด์ต่อไปนี้

ช่อง
access_token โทเค็นที่แอปพลิเคชันของคุณส่งเพื่อให้สิทธิ์คำขอ Google API
expires_in อายุการใช้งานที่เหลือของโทเค็นเพื่อการเข้าถึงเป็นวินาที
refresh_token โทเค็นที่คุณใช้เพื่อรับโทเค็นเพื่อการเข้าถึงใหม่ได้ โทเค็นการรีเฟรชจะใช้ได้จนกว่าผู้ใช้จะเพิกถอนสิทธิ์เข้าถึงหรือโทเค็นการรีเฟรชหมดอายุ หากใช้ DPoP โทเค็นการรีเฟรชจะเชื่อมโยงกับคีย์ส่วนตัวที่ใช้ในการลงนามหลักฐาน DPoP ขอย้ำอีกครั้งว่าช่องนี้จะปรากฏในการตอบกลับนี้ก็ต่อเมื่อคุณตั้งค่าพารามิเตอร์ access_type เป็น offline ในคำขอเริ่มต้นไปยังเซิร์ฟเวอร์การให้สิทธิ์ของ Google
refresh_token_expires_in อายุการใช้งานที่เหลือของโทเค็นการรีเฟรชเป็นวินาที ระบบจะตั้งค่านี้ก็ต่อเมื่อผู้ใช้ให้สิทธิ์เข้าถึงตามเวลา
scope ขอบเขตของสิทธิ์เข้าถึงที่ access_token มอบให้จะแสดงเป็นรายการสตริงที่คั่นด้วยช่องว่างและคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
token_type ประเภทของโทเค็นที่แสดงผล ค่านี้จะเป็น Bearer เสมอ แม้ว่าจะใช้ DPoP ก็ตาม

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

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI

{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "token_type": "Bearer",
  "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
  "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}

ข้อผิดพลาด

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

invalid_grant

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

ขั้นตอนที่ 6: ตรวจสอบขอบเขตที่ผู้ใช้ให้สิทธิ์

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

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

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

PHP

หากต้องการตรวจสอบขอบเขตที่ผู้ใช้ให้สิทธิ์ ให้ใช้เมธอด getGrantedScope() ดังนี้

// Space-separated string of granted scopes if it exists, otherwise null.
$granted_scopes = $client->getOAuth2Service()->getGrantedScope();

// Determine which scopes user granted and build a dictionary
$granted_scopes_dict = [
  'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY),
  'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY)
];

Python

ออบเจ็กต์ credentials ที่แสดงผลมีพร็อพเพอร์ตี้ granted_scopes ซึ่งเป็นรายการขอบเขตที่ผู้ใช้ให้สิทธิ์แก่แอปของคุณ

credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'granted_scopes': credentials.granted_scopes}

ฟังก์ชันต่อไปนี้จะตรวจสอบขอบเขตที่ผู้ใช้ให้สิทธิ์แก่แอปของคุณ

def check_granted_scopes(credentials):
  features = {}
  if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']:
    features['drive'] = True
  else:
    features['drive'] = False

  if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']:
    features['calendar'] = True
  else:
    features['calendar'] = False

  return features

Ruby

เมื่อขอขอบเขตหลายรายการพร้อมกัน ให้ตรวจสอบว่าขอบเขตใดได้รับสิทธิ์ผ่านพร็อพเพอร์ตี้ scope ของออบเจ็กต์ credentials

# User authorized the request. Now, check which scopes were granted.
if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY)
  # User authorized read-only Drive activity permission.
  # Calling the APIs, etc
else
  # User didn't authorize read-only Drive activity permission.
  # Update UX and application accordingly
end

# Check if user authorized Calendar read permission.
if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY)
  # User authorized Calendar read permission.
  # Calling the APIs, etc.
else
  # User didn't authorize Calendar read permission.
  # Update UX and application accordingly
end

Node.js

เมื่อขอขอบเขตหลายรายการพร้อมกัน ให้ตรวจสอบว่าขอบเขตใดได้รับสิทธิ์ผ่านพร็อพเพอร์ตี้ scope ของออบเจ็กต์ tokens

// User authorized the request. Now, check which scopes were granted.
if (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly'))
{
  // User authorized read-only Drive activity permission.
  // Calling the APIs, etc.
}
else
{
  // User didn't authorize read-only Drive activity permission.
  // Update UX and application accordingly
}

// Check if user authorized Calendar read permission.
if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))
{
  // User authorized Calendar read permission.
  // Calling the APIs, etc.
}
else
{
  // User didn't authorize Calendar read permission.
  // Update UX and application accordingly
}

HTTP/REST

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

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

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

เรียกใช้ Google API

PHP

ใช้โทเค็นเพื่อการเข้าถึงเพื่อเรียกใช้ Google APIs โดยทำตามขั้นตอนต่อไปนี้

  1. หากต้องการใช้โทเค็นเพื่อการเข้าถึงกับออบเจ็กต์ Google\Client ใหม่ เช่น หากคุณจัดเก็บโทเค็นเพื่อการเข้าถึงไว้ในเซสชันของผู้ใช้ ให้ใช้เมธอด setAccessToken ดังนี้
    $client->setAccessToken($access_token);
  2. สร้างออบเจ็กต์บริการสำหรับ API ที่ต้องการเรียกใช้ คุณสร้างออบเจ็กต์บริการโดยระบุออบเจ็กต์ Google\Client ที่ได้รับอนุญาตให้กับเครื่องมือสร้างสำหรับ API ที่ต้องการเรียกใช้ เช่น หากต้องการเรียกใช้ Drive API ให้ทำดังนี้
    $drive = new Google\Service\Drive($client);
  3. ส่งคำขอไปยังบริการ API โดยใช้ อินเทอร์เฟซที่ออบเจ็กต์บริการมีให้ เช่น หากต้องการแสดงรายการไฟล์ใน Google ไดรฟ์ของผู้ใช้ที่ตรวจสอบสิทธิ์แล้ว ให้ทำดังนี้
    $files = $drive->files->listFiles(array());

Python

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

  1. สร้างออบเจ็กต์บริการสำหรับ API ที่ต้องการเรียกใช้ คุณสร้างออบเจ็กต์บริการโดยเรียกใช้เมธอด build ของไลบรารี googleapiclient.discovery ด้วยชื่อและเวอร์ชันของ API และข้อมูลเข้าสู่ระบบของผู้ใช้ ดังนี้ ตัวอย่างเช่น หากต้องการเรียกใช้ Drive API เวอร์ชัน 3 ให้ทำดังนี้
    from googleapiclient.discovery import build
    
    drive = build('drive', 'v2', credentials=credentials)
  2. ส่งคำขอไปยังบริการ API โดยใช้อินเทอร์เฟซที่ออบเจ็กต์บริการมีให้ เช่น หากต้องการแสดงรายการไฟล์ใน Google ไดรฟ์ของผู้ใช้ที่ตรวจสอบสิทธิ์แล้ว ให้ทำดังนี้
    files = drive.files().list().execute()

Ruby

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

  1. สร้างออบเจ็กต์บริการสำหรับ API ที่ต้องการเรียกใช้ เช่น หากต้องการเรียกใช้ Drive API เวอร์ชัน 3 ให้ทำดังนี้
    drive = Google::Apis::DriveV3::DriveService.new
  2. ตั้งค่าข้อมูลเข้าสู่ระบบในบริการโดยทำดังนี้
    drive.authorization = credentials
  3. ส่งคำขอไปยังบริการ API โดยใช้อินเทอร์เฟซที่ออบเจ็กต์บริการมีให้ เช่น หากต้องการแสดงรายการไฟล์ใน Google ไดรฟ์ของผู้ใช้ที่ตรวจสอบสิทธิ์แล้ว ให้ทำดังนี้
    files = drive.list_files

หรือจะให้สิทธิ์เป็นรายเมธอดก็ได้โดยระบุพารามิเตอร์ options ให้กับเมธอด ดังนี้

files = drive.list_files(options: { authorization: credentials })

Node.js

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

const { google } = require('googleapis');

// Example of using Google Drive API to list filenames in user's Drive.
const drive = google.drive('v3');
drive.files.list({
  auth: oauth2Client,
  pageSize: 10,
  fields: 'nextPageToken, files(id, name)',
}, (err1, res1) => {
  if (err1) return console.log('The API returned an error: ' + err1);
  const files = res1.data.files;
  if (files.length) {
    console.log('Files:');
    files.map((file) => {
      console.log(`${file.name} (${file.id})`);
    });
  } else {
    console.log('No files found.');
  }
});

HTTP/REST

หลังจากที่แอปพลิเคชันได้รับโทเค็นเพื่อการเข้าถึงแล้ว คุณสามารถใช้โทเค็นดังกล่าวเพื่อเรียกใช้ Google API ในนามของบัญชีผู้ใช้ที่ระบุได้ หากได้รับสิทธิ์เข้าถึงตามขอบเขตที่ API กำหนด โดยใส่โทเค็นเพื่อการเข้าถึงในคำขอไปยัง API โดยใส่พารามิเตอร์การค้นหา access_token หรือค่าส่วนหัว HTTP Bearer Authorization หากเป็นไปได้ เราขอแนะนำให้ใช้ส่วนหัว HTTP เนื่องจากสตริงการค้นหามักจะปรากฏในบันทึกของเซิร์ฟเวอร์ ในกรณีส่วนใหญ่ คุณสามารถใช้ไลบรารีของไคลเอ็นต์เพื่อตั้งค่าการเรียกใช้ Google API (เช่น เมื่อเรียกใช้ Drive Files API)

คุณลองใช้ Google APIs ทั้งหมดและดูขอบเขตได้ที่ OAuth 2.0 Playground

ตัวอย่าง HTTP GET

การเรียกไปยังปลายทาง drive.files (Drive Files API) โดยใช้ส่วนหัว HTTP Authorization: Bearer อาจมีลักษณะดังต่อไปนี้ โปรดทราบว่าคุณต้องระบุโทเค็นเพื่อการเข้าถึงของคุณเอง ดังนี้

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

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

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

ตัวอย่างของ curl

คุณทดสอบคำสั่งเหล่านี้ได้ด้วยแอปพลิเคชันบรรทัดคำสั่ง curl ต่อไปนี้คือตัวอย่างที่ใช้ตัวเลือกส่วนหัว HTTP (แนะนำ)

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

หรือตัวเลือกพารามิเตอร์สตริงการค้นหา

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

ตัวอย่างที่เสร็จสมบูรณ์

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

PHP

วิธีเรียกใช้ตัวอย่างนี้

  1. ในคอนโซล API ให้เพิ่ม URL ของเครื่องในพื้นที่ลงในรายการ URL เปลี่ยนเส้นทาง เช่น เพิ่ม http://localhost:8080
  2. สร้างไดเรกทอรีใหม่และเปลี่ยนไปใช้ไดเรกทอรีนั้น เช่น
    mkdir ~/php-oauth2-example
    cd ~/php-oauth2-example
  3. ติดตั้งไลบรารีของไคลเอ็นต์ Google API สำหรับ PHP โดยใช้ Composer ดังนี้
    composer require google/apiclient:^2.15.0
  4. สร้างไฟล์ index.php และ oauth2callback.php โดยมีเนื้อหาต่อไปนี้
  5. เรียกใช้ตัวอย่างด้วยเว็บเซิร์ฟเวอร์ทดสอบในตัวของ PHP โดยทำดังนี้
    php -S localhost:8080 ~/php-oauth2-example

index.php

<?php
require_once __DIR__.'/vendor/autoload.php';

session_start();

$client = new Google\Client();
$client->setAuthConfig('client_secret.json');

// User granted permission as an access token is in the session.
if (isset($_SESSION['access_token']) && $_SESSION['access_token'])
{
  $client->setAccessToken($_SESSION['access_token']);
  
  // Check if user granted Drive permission
  if ($_SESSION['granted_scopes_dict']['Drive']) {
    echo "Drive feature is enabled.";
    echo "</br>";
    $drive = new Drive($client);
    $files = array();
    $response = $drive->files->listFiles(array());
    foreach ($response->files as $file) {
        echo "File: " . $file->name . " (" . $file->id . ")";
        echo "</br>";
    }
  } else {
    echo "Drive feature is NOT enabled.";
    echo "</br>";
  }

   // Check if user granted Calendar permission
  if ($_SESSION['granted_scopes_dict']['Calendar']) {
    echo "Calendar feature is enabled.";
    echo "</br>";
  } else {
    echo "Calendar feature is NOT enabled.";
    echo "</br>";
  }
}
else
{
  // Redirect users to outh2call.php which redirects users to Google OAuth 2.0
  $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
  header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>

oauth2callback.php

<?php
require_once __DIR__.'/vendor/autoload.php';

session_start();

$client = new Google\Client();

// Required, call the setAuthConfig function to load authorization credentials from
// client_secret.json file.
$client->setAuthConfigFile('client_secret.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']);

// Required, to set the scope value, call the addScope function.
$client->addScope([Google\Service\Drive::DRIVE_METADATA_READONLY, Google\Service\Calendar::CALENDAR_READONLY]);

// Enable incremental authorization. Recommended as a best practice.
$client->setIncludeGrantedScopes(true);

// Recommended, offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType("offline");

// Generate a URL for authorization as it doesn't contain code and error
if (!isset($_GET['code']) && !isset($_GET['error']))
{
  // Generate and set state value
  $state = bin2hex(random_bytes(16));
  $client->setState($state);
  $_SESSION['state'] = $state;

  // Generate a url that asks permissions.
  $auth_url = $client->createAuthUrl();
  header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
}

// User authorized the request and authorization code is returned to exchange access and
// refresh tokens.
if (isset($_GET['code']))
{
  // Check the state value
  if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) {
    die('State mismatch. Possible CSRF attack.');
  }

  // Get access and refresh tokens (if access_type is offline)
  $token = $client->fetchAccessTokenWithAuthCode($_GET['code']);

  /** Save access and refresh token to the session variables.
    * ACTION ITEM: In a production app, you likely want to save the
    *              refresh token in a secure persistent storage instead. */
  $_SESSION['access_token'] = $token;
  $_SESSION['refresh_token'] = $client->getRefreshToken();
  
  // Space-separated string of granted scopes if it exists, otherwise null.
  $granted_scopes = $client->getOAuth2Service()->getGrantedScope();

  // Determine which scopes user granted and build a dictionary
  $granted_scopes_dict = [
    'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY),
    'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY)
  ];
  $_SESSION['granted_scopes_dict'] = $granted_scopes_dict;
  
  $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/';
  header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}

// An error response e.g. error=access_denied
if (isset($_GET['error']))
{
  echo "Error: ". $_GET['error'];
}
?>

Python

ตัวอย่างนี้ใช้เฟรมเวิร์ก Flask โดยจะเรียกใช้เว็บแอปพลิเคชันที่ http://localhost:8080 ซึ่งช่วยให้คุณทดสอบขั้นตอน OAuth 2.0 ได้ หากไปที่ URL ดังกล่าว คุณควรเห็นลิงก์ 5 รายการต่อไปนี้

  • เรียกใช้ Drive API: ลิงก์นี้ชี้ไปยังหน้าที่พยายามดำเนินการตามคำขอ API ตัวอย่างหากผู้ใช้ให้สิทธิ์ และจะเริ่มขั้นตอนการให้สิทธิ์หากจำเป็น หากทำสำเร็จ หน้าเว็บจะแสดงการตอบกลับจาก API
  • หน้าจำลองเพื่อเรียกใช้ Calendar API: ลิงก์นี้ชี้ไปยังหน้าจำลองที่พยายามดำเนินการตามคำขอ API ตัวอย่างหากผู้ใช้ให้สิทธิ์ หากจำเป็น ระบบจะเริ่มขั้นตอนการให้สิทธิ์ หากทำสำเร็จ หน้าเว็บจะแสดงการตอบกลับจาก API
  • ทดสอบขั้นตอนการตรวจสอบสิทธิ์โดยตรง: ลิงก์นี้ชี้ไปยังหน้าที่พยายามส่งผู้ใช้ผ่านขั้นตอนการให้สิทธิ์ แอปจะขอสิทธิ์ส่งคำขอ API ที่ได้รับอนุญาตในนามของผู้ใช้
  • เพิกถอนข้อมูลเข้าสู่ระบบปัจจุบัน: ลิงก์นี้จะนำไปยังหน้าที่ เพิกถอนสิทธิ์ที่ผู้ใช้ให้ไว้กับแอปพลิเคชันแล้ว
  • ล้างข้อมูลเข้าสู่ระบบของเซสชัน Flask: ลิงก์นี้จะล้างข้อมูลเข้าสู่ระบบการให้สิทธิ์ที่จัดเก็บไว้ในเซสชัน Flask ซึ่งจะช่วยให้คุณเห็นสิ่งที่จะเกิดขึ้นหากผู้ใช้ที่ให้สิทธิ์แก่แอปของคุณแล้วพยายามเรียกใช้คำขอ API ในเซสชันใหม่ นอกจากนี้ยังช่วยให้คุณเห็นการตอบกลับจาก API ที่แอปจะได้รับหากผู้ใช้เพิกถอนสิทธิ์ที่ให้ไว้กับแอป และแอปยังคงพยายามให้สิทธิ์คำขอด้วยโทเค็นเพื่อการเข้าถึงที่เพิกถอนไปแล้ว
# -*- coding: utf-8 -*-

import os
import flask
import json
import requests

import google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery

# This variable specifies the name of a file that contains the OAuth 2.0
# information for this application, including its client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"

# The OAuth 2.0 access scope allows for access to the
# authenticated user's account and requires requests to use an SSL connection.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly',
          'https://www.googleapis.com/auth/calendar.readonly']
API_SERVICE_NAME = 'drive'
API_VERSION = 'v2'

app = flask.Flask(__name__)
# Note: A secret key is included in the sample so that it works.
# If you use this code in your application, replace this with a truly secret
# key. See https://flask.palletsprojects.com/quickstart/#sessions.
app.secret_key = 'REPLACE ME - this value is here as a placeholder.'

@app.route('/')
def index():
  return print_index_table()

@app.route('/drive')
def drive_api_request():
  if 'credentials' not in flask.session:
    return flask.redirect('authorize')

  features = flask.session['features']

  if features['drive']:
    # Load client secrets from the server-side file.
    with open(CLIENT_SECRETS_FILE, 'r') as f:
        client_config = json.load(f)['web']

    # Load user-specific credentials from browser session storage.
    session_credentials = flask.session['credentials']

    # Reconstruct the credentials object.
    credentials = google.oauth2.credentials.Credentials(
        refresh_token=session_credentials.get('refresh_token'),
        scopes=session_credentials.get('granted_scopes'),
        token=session_credentials.get('token'),
        client_id=client_config.get('client_id'),
        client_secret=client_config.get('client_secret'),
        token_uri=client_config.get('token_uri'))

    drive = googleapiclient.discovery.build(
        API_SERVICE_NAME, API_VERSION, credentials=credentials)

    files = drive.files().list().execute()

    # Save credentials back to session in case access token was refreshed.
    flask.session['credentials'] = credentials_to_dict(credentials)

    return flask.jsonify(**files)
  else:
    # User didn't authorize read-only Drive activity permission.
    return '<p>Drive feature is not enabled.</p>'

@app.route('/calendar')
def calendar_api_request():
  if 'credentials' not in flask.session:
    return flask.redirect('authorize')

  features = flask.session['features']

  if features['calendar']:
    # User authorized Calendar read permission.
    # Calling the APIs, etc.
    return ('<p>User granted the Google Calendar read permission. '+
            'This sample code does not include code to call Calendar</p>')
  else:
    # User didn't authorize Calendar read permission.
    # Update UX and application accordingly
    return '<p>Calendar feature is not enabled.</p>'

@app.route('/authorize')
def authorize():
  # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
  flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
      CLIENT_SECRETS_FILE, scopes=SCOPES)

  # The URI created here must exactly match one of the authorized redirect URIs
  # for the OAuth 2.0 client, which you configured in the API Console. If this
  # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'
  # error.
  flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

  authorization_url, state = flow.authorization_url(
      # Enable offline access so that you can refresh an access token without
      # re-prompting the user for permission. Recommended for web server apps.
      access_type='offline',
      # Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes='true')

  # Store the state so the callback can verify the auth server response.
  flask.session['state'] = state

  return flask.redirect(authorization_url)

@app.route('/oauth2callback')
def oauth2callback():
  # Specify the state when creating the flow in the callback so that it can
  # verified in the authorization server response.
  state = flask.session['state']

  flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
      CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
  flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

  # Use the authorization server's response to fetch the OAuth 2.0 tokens.
  authorization_response = flask.request.url
  flow.fetch_token(authorization_response=authorization_response)

  # Store credentials in the session.
  # ACTION ITEM: In a production app, you likely want to save these
  #              credentials in a persistent database instead.
  credentials = flow.credentials
  
  credentials = credentials_to_dict(credentials)
  flask.session['credentials'] = credentials

  # Check which scopes user granted
  features = check_granted_scopes(credentials)
  flask.session['features'] = features
  return flask.redirect('/')
  
@app.route('/revoke')
def revoke():
  if 'credentials' not in flask.session:
    return ('You need to <a href="/authorize">authorize</a> before ' +
            'testing the code to revoke credentials.')

  # Load client secrets from the server-side file.
  with open(CLIENT_SECRETS_FILE, 'r') as f:
      client_config = json.load(f)['web']

  # Load user-specific credentials from the session.
  session_credentials = flask.session['credentials']

  # Reconstruct the credentials object.
  credentials = google.oauth2.credentials.Credentials(
      refresh_token=session_credentials.get('refresh_token'),
      scopes=session_credentials.get('granted_scopes'),
      token=session_credentials.get('token'),
      client_id=client_config.get('client_id'),
      client_secret=client_config.get('client_secret'),
      token_uri=client_config.get('token_uri'))

  revoke = requests.post('https://oauth2.googleapis.com/revoke',
      params={'token': credentials.token},
      headers = {'content-type': 'application/x-www-form-urlencoded'})

  status_code = getattr(revoke, 'status_code')
  if status_code == 200:
    # Clear the user's session credentials after successful revocation
    if 'credentials' in flask.session:
        del flask.session['credentials']
        del flask.session['features']
    return('Credentials successfully revoked.' + print_index_table())
  else:
    return('An error occurred.' + print_index_table())

@app.route('/clear')
def clear_credentials():
  if 'credentials' in flask.session:
    del flask.session['credentials']
  return ('Credentials have been cleared.<br><br>' +
          print_index_table())

def credentials_to_dict(credentials):
  return {'token': credentials.token,
          'refresh_token': credentials.refresh_token,
          'granted_scopes': credentials.granted_scopes}

def check_granted_scopes(credentials):
  features = {}
  if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']:
    features['drive'] = True
  else:
    features['drive'] = False

  if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']:
    features['calendar'] = True
  else:
    features['calendar'] = False

  return features

def print_index_table():
  return ('<table>' + 
          '<tr><td><a href="/authorize">Test the auth flow directly</a></td>' +
          '<td>Go directly to the authorization flow. If there are stored ' +
          '    credentials, you still might not be prompted to reauthorize ' +
          '    the application.</td></tr>' +
          '<tr><td><a href="/drive">Call Drive API directly</a></td>' +
          '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' +
          '    the application.</td></tr>' +
          '<tr><td><a href="/calendar">Call Calendar API directly</a></td>' +
          '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' +
          '    the application.</td></tr>' + 
          '<tr><td><a href="/revoke">Revoke current credentials</a></td>' +
          '<td>Revoke the access token associated with the current user ' +
          '    session. After revoking credentials, if you go to the test ' +
          '    page, you should see an <code>invalid_grant</code> error.' +
          '</td></tr>' +
          '<tr><td><a href="/clear">Clear Flask session credentials</a></td>' +
          '<td>Clear the access token currently stored in the user session. ' +
          '    After clearing the token, if you <a href="/authorize">authorize</a> ' +
          '    again, you should go back to the auth flow.' +
          '</td></tr></table>')

if __name__ == '__main__':
  # When running locally, disable OAuthlib's HTTPs verification.
  # ACTION ITEM for developers:
  #     When running in production *do not* leave this option enabled.
  os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

  # This disables the requested scopes and granted scopes check.
  # If users only grant partial request, the warning would not be thrown.
  os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'

  # Specify a hostname and port that are set as a valid redirect URI
  # for your API project in the Google API Console.
  app.run('localhost', 8080, debug=True)

Ruby

ตัวอย่างนี้ใช้เฟรมเวิร์ก Sinatra

require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'

require 'google/apis/drive_v3'
require 'google/apis/calendar_v3'

require 'sinatra'

configure do
  enable :sessions

  # Required, call the from_file method to retrieve the client ID from a
  # client_secret.json file.
  set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json')

  # Required, scope value
  # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
  scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY',
           'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY']

  # Required, Authorizers require a storage instance to manage long term persistence of
  # access and refresh tokens.
  set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)

  # Required, indicate where the API server will redirect the user after the user completes
  # the authorization flow. The redirect URI is required. The value must exactly
  # match one of the authorized redirect URIs for the OAuth 2.0 client, which you
  # configured in the API Console. If this value doesn't match an authorized URI,
  # you will get a 'redirect_uri_mismatch' error.
  set :callback_uri, '/oauth2callback'

  # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
  # from the client_secret.json file. To get these credentials for your application, visit
  # https://console.cloud.google.com/apis/credentials.
  set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope,
                          settings.token_store, callback_uri: settings.callback_uri)
end

get '/' do
  # NOTE: Assumes the user is already authenticated to the app
  user_id = request.session['user_id']

  # Fetch stored credentials for the user from the given request session.
  # nil if none present
  credentials = settings.authorizer.get_credentials(user_id, request)

  if credentials.nil?
    # Generate a url that asks the user to authorize requested scope(s).
    # Then, redirect user to the url.
    redirect settings.authorizer.get_authorization_url(request: request)
  end
  
  # User authorized the request. Now, check which scopes were granted.
  if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY)
    # User authorized read-only Drive activity permission.
    # Example of using Google Drive API to list filenames in user's Drive.
    drive = Google::Apis::DriveV3::DriveService.new
    files = drive.list_files(options: { authorization: credentials })
    "<pre>#{JSON.pretty_generate(files.to_h)}</pre>"
  else
    # User didn't authorize read-only Drive activity permission.
    # Update UX and application accordingly
  end

  # Check if user authorized Calendar read permission.
  if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY)
    # User authorized Calendar read permission.
    # Calling the APIs, etc.
  else
    # User didn't authorize Calendar read permission.
    # Update UX and application accordingly
  end
end

# Receive the callback from Google's OAuth 2.0 server.
get '/oauth2callback' do
  # Handle the result of the oauth callback. Defers the exchange of the code by
  # temporarily stashing the results in the user's session.
  target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
  redirect target_url
end

Node.js

วิธีเรียกใช้ตัวอย่างนี้

  1. ในคอนโซล API ให้เพิ่ม URL ของเครื่องในพื้นที่ลงในรายการ URL เปลี่ยนเส้นทาง เช่น เพิ่ม http://localhost
  2. ตรวจสอบว่าคุณได้ติดตั้ง LTS สำหรับการบำรุงรักษา, LTS ที่ใช้งานอยู่ หรือ Node.js เวอร์ชันปัจจุบันแล้ว
  3. สร้างไดเรกทอรีใหม่และเปลี่ยนไปใช้ไดเรกทอรีนั้น เช่น
    mkdir ~/nodejs-oauth2-example
    cd ~/nodejs-oauth2-example
  4. ติดตั้งไลบรารีของไคลเอ็นต์ Google API สำหรับ Node.js โดยใช้ npm ดังนี้
    npm install googleapis
  5. สร้างไฟล์ main.js ที่มีเนื้อหาต่อไปนี้
  6. เรียกใช้ตัวอย่าง
    node .\main.js

main.js

const http = require('http');
const https = require('https');
const url = require('url');
const { google } = require('googleapis');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');

/**
 * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI.
 * To get these credentials for your application, visit
 * https://console.cloud.google.com/apis/credentials.
 */
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
const scopes = [
  'https://www.googleapis.com/auth/drive.metadata.readonly',
  'https://www.googleapis.com/auth/calendar.readonly'
];

/* Global variable that stores user credential in this code example.
 * ACTION ITEM for developers:
 *   Store user's refresh token in your data store if
 *   incorporating this code into your real app.
 *   For more information on handling refresh tokens,
 *   see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens
 */
let userCredential = null;

async function main() {
  const app = express();

  app.use(session({
    secret: 'your_secure_secret_key', // Replace with a strong secret
    resave: false,
    saveUninitialized: false,
  }));

  // Example on redirecting user to Google's OAuth 2.0 server.
  app.get('/', async (req, res) => {
    // Generate a secure random state value.
    const state = crypto.randomBytes(32).toString('hex');
    // Store state in the session
    req.session.state = state;

    // Generate a url that asks permissions for the Drive activity and Google Calendar scope
    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true,
      // Include the state parameter to reduce the risk of CSRF attacks.
      state: state
    });

    res.redirect(authorizationUrl);
  });

  // Receive the callback from Google's OAuth 2.0 server.
  app.get('/oauth2callback', async (req, res) => {
    // Handle the OAuth 2.0 server response
    let q = url.parse(req.url, true).query;

    if (q.error) { // An error response e.g. error=access_denied
      console.log('Error:' + q.error);
    } else if (q.state !== req.session.state) { //check state value
      console.log('State mismatch. Possible CSRF attack');
      res.end('State mismatch. Possible CSRF attack');
    } else { // Get access and refresh tokens (if access_type is offline)
      let { tokens } = await oauth2Client.getToken(q.code);
      oauth2Client.setCredentials(tokens);

      /** Save credential to the global variable in case access token was refreshed.
        * ACTION ITEM: In a production app, you likely want to save the refresh token
        *              in a secure persistent database instead. */
      userCredential = tokens;
      
      // User authorized the request. Now, check which scopes were granted.
      if (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly'))
      {
        // User authorized read-only Drive activity permission.
        // Example of using Google Drive API to list filenames in user's Drive.
        const drive = google.drive('v3');
        drive.files.list({
          auth: oauth2Client,
          pageSize: 10,
          fields: 'nextPageToken, files(id, name)',
        }, (err1, res1) => {
          if (err1) return console.log('The API returned an error: ' + err1);
          const files = res1.data.files;
          if (files.length) {
            console.log('Files:');
            files.map((file) => {
              console.log(`${file.name} (${file.id})`);
            });
          } else {
            console.log('No files found.');
          }
        });
      }
      else
      {
        // User didn't authorize read-only Drive activity permission.
        // Update UX and application accordingly
      }

      // Check if user authorized Calendar read permission.
      if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))
      {
        // User authorized Calendar read permission.
        // Calling the APIs, etc.
      }
      else
      {
        // User didn't authorize Calendar read permission.
        // Update UX and application accordingly
      }
    }
  });

  // Example on revoking a token
  app.get('/revoke', async (req, res) => {
    // Build the string for the POST request
    let postData = "token=" + userCredential.access_token;

    // Options for POST request to Google's OAuth 2.0 server to revoke a token
    let postOptions = {
      host: 'oauth2.googleapis.com',
      port: '443',
      path: '/revoke',
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
      }
    };

    // Set up the request
    const postReq = https.request(postOptions, function (res) {
      res.setEncoding('utf8');
      res.on('data', d => {
        console.log('Response: ' + d);
      });
    });

    postReq.on('error', error => {
      console.log(error)
    });

    // Post the request with data
    postReq.write(postData);
    postReq.end();
  });


  const server = http.createServer(app);
  server.listen(8080);
}
main().catch(console.error);

HTTP/REST

ตัวอย่าง Python นี้ใช้เฟรมเวิร์ก Flask และไลบรารี Requests เพื่อสาธิตขั้นตอนการทำงานของเว็บ OAuth 2.0 เราขอแนะนำให้ใช้ไลบรารีของไคลเอ็นต์ Google API สำหรับ Python ในขั้นตอนนี้ (ตัวอย่างในแท็บ Python ใช้ไลบรารีของไคลเอ็นต์)

import json
import flask
import requests

app = flask.Flask(__name__)

# To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit
# https://console.cloud.google.com/apis/credentials.
CLIENT_ID = '123456789.apps.googleusercontent.com'
CLIENT_SECRET = 'abc123'  # Read from a file or environmental variable in a real app

# Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly'

# Indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
REDIRECT_URI = 'http://example.com/oauth2callback'

@app.route('/')
def index():
  if 'credentials' not in flask.session:
    return flask.redirect(flask.url_for('oauth2callback'))

  credentials = json.loads(flask.session['credentials'])

  if credentials['expires_in'] <= 0:
    return flask.redirect(flask.url_for('oauth2callback'))
  else: 
    # User authorized the request. Now, check which scopes were granted.
    if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['scope']:
      # User authorized read-only Drive activity permission.
      # Example of using Google Drive API to list filenames in user's Drive.
      headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}
      req_uri = 'https://www.googleapis.com/drive/v2/files'
      r = requests.get(req_uri, headers=headers).text
    else:
      # User didn't authorize read-only Drive activity permission.
      # Update UX and application accordingly
      r = 'User did not authorize Drive permission.'

    # Check if user authorized Calendar read permission.
    if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['scope']:
      # User authorized Calendar read permission.
      # Calling the APIs, etc.
      r += 'User authorized Calendar permission.'
    else:
      # User didn't authorize Calendar read permission.
      # Update UX and application accordingly
      r += 'User did not authorize Calendar permission.'

  return r

@app.route('/oauth2callback')
def oauth2callback():
  if 'code' not in flask.request.args:
    state = str(uuid.uuid4())
    flask.session['state'] = state
    # Generate a url that asks permissions for the Drive activity
    # and Google Calendar scope. Then, redirect user to the url.
    auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'
                '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI,
                                                                          SCOPE, state)
    return flask.redirect(auth_uri)
  else:
    if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']:
      return 'State mismatch. Possible CSRF attack.', 400

    auth_code = flask.request.args.get('code')
    data = {'code': auth_code,
            'client_id': CLIENT_ID,
            'client_secret': CLIENT_SECRET,
            'redirect_uri': REDIRECT_URI,
            'grant_type': 'authorization_code'}

    # Exchange authorization code for access and refresh tokens (if access_type is offline)
    r = requests.post('https://oauth2.googleapis.com/token', data=data)
    flask.session['credentials'] = r.text
    return flask.redirect(flask.url_for('index'))

if __name__ == '__main__':
  import uuid
  app.secret_key = str(uuid.uuid4())
  app.debug = False
  app.run()

กฎการตรวจสอบ URI การเปลี่ยนเส้นทาง

Google ใช้กฎการตรวจสอบต่อไปนี้กับ URI การเปลี่ยนเส้นทางเพื่อช่วยนักพัฒนาแอปดูแลให้แอปพลิเคชันปลอดภัย URI เปลี่ยนเส้นทางต้องเป็นไปตามกฎเหล่านี้ ดูคำจำกัดความของโดเมน โฮสต์ เส้นทาง การค้นหา รูปแบบ และข้อมูลผู้ใช้ที่ใช้ในกฎเหล่านี้ได้ที่ RFC 3986 ส่วนที่ 3

กฎการตรวจสอบความถูกต้อง
รูปแบบ

URI เปลี่ยนเส้นทางต้องใช้รูปแบบ HTTPS ไม่ใช่ HTTP ธรรมดา URI ของ Localhost (รวมถึง URI ของที่อยู่ IP ของ Localhost) จะได้รับการยกเว้นจากกฎนี้

ผู้จัด

โฮสต์ต้องไม่ใช่ที่อยู่ IP ดิบ ที่อยู่ IP ของ Localhost ได้รับการยกเว้นจากกฎนี้

โดเมน
  • TLD ของโฮสต์ (โดเมนระดับบนสุด) ต้องอยู่ในรายการคำต่อท้ายสาธารณะ
  • โดเมนที่ฝากบริการต้องไม่ใช่ “googleusercontent.com”
  • URI เปลี่ยนเส้นทางต้องไม่มีโดเมนเครื่องมือการย่อ URL (เช่น goo.gl) เว้นแต่ว่าแอปจะเป็นเจ้าของโดเมน นอกจากนี้ หากแอปที่เป็นเจ้าของโดเมนเครื่องมือย่อ URL เลือกที่จะเปลี่ยนเส้นทางไปยังโดเมนนั้น URI การเปลี่ยนเส้นทางดังกล่าวต้องมี “/google-callback/” ในเส้นทางหรือลงท้ายด้วย “/google-callback”
  • Userinfo

    URI เปลี่ยนเส้นทางต้องไม่มีองค์ประกอบย่อย userinfo

    เส้นทาง

    URI การเปลี่ยนเส้นทางต้องไม่มี Path Traversal (หรือที่เรียกว่า Directory Backtracking) ซึ่งแสดงด้วย “/..” หรือ “\..” หรือการเข้ารหัส URL

    การค้นหา

    URI การเปลี่ยนเส้นทางต้องไม่มีการเปลี่ยนเส้นทางแบบเปิด

    Fragment

    URI การเปลี่ยนเส้นทางต้องไม่มีคอมโพเนนต์ Fragment

    อักขระ URI การเปลี่ยนเส้นทางต้องไม่มีอักขระบางตัว ซึ่งรวมถึง
    • อักขระไวลด์การ์ด ('*')
    • อักขระ ASCII ที่พิมพ์ไม่ได้
    • การเข้ารหัสเปอร์เซ็นต์ไม่ถูกต้อง (การเข้ารหัสเปอร์เซ็นต์ใดๆ ที่ไม่เป็นไปตามรูปแบบการเข้ารหัส URL ของเครื่องหมายเปอร์เซ็นต์ตามด้วยเลขฐาน 16 จำนวน 2 หลัก)
    • อักขระ Null (อักขระ NULL ที่เข้ารหัส เช่น %00, %C0%80)

    การให้สิทธิ์แบบเพิ่มทีละส่วน

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

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

    ในกรณีนี้ เมื่อลงชื่อเข้าใช้ แอปอาจขอขอบเขต openid และ profile เพื่อลงชื่อเข้าใช้ขั้นพื้นฐาน จากนั้นจึงขอขอบเขต https://www.googleapis.com/auth/drive.file ในภายหลังเมื่อมีการส่งคำขอแรกเพื่อบันทึกมิกซ์

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

    กฎต่อไปนี้ใช้กับโทเค็นเพื่อการเข้าถึงที่ได้จากการให้สิทธิ์แบบเพิ่มขึ้น

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

    ตัวอย่างโค้ดเฉพาะภาษาในขั้นตอนที่ 1: ตั้งค่าการให้สิทธิ์เปลี่ยนเส้นทางไปยังเซิร์ฟเวอร์ OAuth 2.0 ของ Google ทั้งหมดใช้การให้สิทธิ์แบบเพิ่มทีละส่วน ตัวอย่างโค้ดต่อไปนี้ยังแสดงโค้ดที่คุณต้องเพิ่มเพื่อใช้การให้สิทธิ์แบบเพิ่มส่วนด้วย

    PHP

    $client->setIncludeGrantedScopes(true);

    Python

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

    authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')

    Ruby

    auth_client.update!(
      :additional_parameters => {"include_granted_scopes" => "true"}
    )

    Node.js

    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true
    });

    HTTP/REST

    GET https://accounts.google.com/o/oauth2/v2/auth?
      client_id=your_client_id&
      response_type=code&
      state=state_parameter_passthrough_value&
      scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
      redirect_uri=https%3A//developers.google.com/oauthplayground&
      prompt=consent&
      include_granted_scopes=true

    รีเฟรชโทเค็นเพื่อการเข้าถึง (การเข้าถึงแบบออฟไลน์)

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

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

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

    เว็บแอปพลิเคชันฝั่งเซิร์ฟเวอร์ แอปพลิเคชันที่ติดตั้ง และอุปกรณ์ทั้งหมดจะได้รับโทเค็นการรีเฟรชในระหว่างกระบวนการให้สิทธิ์ โดยทั่วไปแล้ว โทเค็นการรีเฟรชจะไม่ใช้ในเว็บแอปพลิเคชันฝั่งไคลเอ็นต์ (JavaScript)

    PHP

    หากแอปพลิเคชันของคุณต้องการสิทธิ์เข้าถึง Google API แบบออฟไลน์ ให้ตั้งค่าประเภทการเข้าถึงของไคลเอ็นต์ API เป็น offline ดังนี้

    $client->setAccessType("offline");

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

    Python

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

    authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')

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

    Ruby

    หากแอปพลิเคชันของคุณต้องการสิทธิ์เข้าถึง Google API แบบออฟไลน์ ให้ตั้งค่าประเภทการเข้าถึงของไคลเอ็นต์ API เป็น offline ดังนี้

    auth_client.update!(
      :additional_parameters => {"access_type" => "offline"}
    )

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

    Node.js

    หากแอปพลิเคชันของคุณต้องการสิทธิ์เข้าถึง Google API แบบออฟไลน์ ให้ตั้งค่าประเภทการเข้าถึงของไคลเอ็นต์ API เป็น offline ดังนี้

    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true
    });

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

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

    oauth2Client.on('tokens', (tokens) => {
      if (tokens.refresh_token) {
        // store the refresh_token in your secure persistent database
        console.log(tokens.refresh_token);
      }
      console.log(tokens.access_token);
    });

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

    หากต้องการตั้งค่า refresh_token ในภายหลัง คุณสามารถใช้วิธี setCredentials ได้ ดังนี้

    oauth2Client.setCredentials({
      refresh_token: `STORED_REFRESH_TOKEN`
    });

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

    HTTP/REST

    หากต้องการรีเฟรชโทเค็นเพื่อการเข้าถึง แอปพลิเคชันของคุณจะส่งคำขอ HTTPS POST ไปยังเซิร์ฟเวอร์การให้สิทธิ์ของ Google (https://oauth2.googleapis.com/token) ซึ่งมีพารามิเตอร์ต่อไปนี้ในเนื้อหาคำขอ

    ชื่อ ค่า
    client_id รหัสไคลเอ็นต์ที่ได้จากคอนโซล API
    client_secret ไม่บังคับ

    รหัสลับไคลเอ็นต์ที่ได้จากคอนโซล API

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

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

    ส่วนหัว ต้องระบุ คำอธิบาย
    DPoP ไม่บังคับ หลักฐาน DPoP คือ JWT ที่พิสูจน์การครอบครองคีย์ส่วนตัว นี่คือส่วนหัว ไม่ใช่พารามิเตอร์ หากระบุไว้ โทเค็นที่แสดงผลจะเชื่อมโยงกับคีย์นี้ ต้องสร้างหลักฐานใหม่ที่ไม่ซ้ำกันสำหรับคำขอแต่ละรายการ และต้องรวมการอ้างสิทธิ์ htm (เมธอด HTTP) และ htu (HTTP URI) ที่ตรงกับคำขอ

    ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่างคำขอ

    POST /token HTTP/1.1
    Host: oauth2.googleapis.com
    Content-Type: application/x-www-form-urlencoded
    DPoP: DPOP_PROOF_JWT
    
    client_id=your_client_id&
    refresh_token=refresh_token&
    grant_type=refresh_token

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

    การแลกเปลี่ยนที่สำเร็จจะแสดงด้วยการตอบกลับ 200 OK ที่มีโทเค็นเพื่อการเข้าถึงใหม่ เมื่อใช้ DPoP token_type จะเป็น Bearer การตอบกลับที่สำเร็จจะยืนยันว่าระบบยอมรับการพิสูจน์ DPoP สำหรับโทเค็นการรีเฟรช นอกจากนี้ Google อาจแสดงส่วนหัว HTTP DPoP-Nonce ใหม่ในการตอบกลับด้วย หากแสดง ไคลเอ็นต์ของคุณต้องแคช Nonce นี้และรวมไว้ในการอ้างสิทธิ์ nonce ของหลักฐาน DPoP ในคำขอต่อๆ ไป

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

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

    HTTP/1.1 200 OK
    Content-Type: application/json; charset=utf-8
    DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
    
    {
      "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
      "expires_in": 3920,
      "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
      "token_type": "Bearer"
    }

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

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

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

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

    PHP

    หากต้องการเพิกถอนโทเค็นแบบเป็นโปรแกรม ให้เรียกใช้ revokeToken() ดังนี้

    $client->revokeToken();

    Python

    หากต้องการเพิกถอนโทเค็นแบบเป็นโปรแกรม ให้ส่งคำขอไปยัง https://oauth2.googleapis.com/revoke ที่มีโทเค็นเป็นพารามิเตอร์และตั้งค่าส่วนหัว Content-Type ดังนี้

    requests.post('https://oauth2.googleapis.com/revoke',
        params={'token': credentials.token},
        headers = {'content-type': 'application/x-www-form-urlencoded'})

    Ruby

    หากต้องการเพิกถอนโทเค็นแบบเป็นโปรแกรม ให้ส่งคำขอ HTTP ไปยังปลายทาง oauth2.revoke ดังนี้

    uri = URI('https://oauth2.googleapis.com/revoke')
    response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)

    โทเค็นอาจเป็นโทเค็นเพื่อการเข้าถึงหรือโทเค็นการรีเฟรชก็ได้ หากโทเค็นเป็นโทเค็นเพื่อการเข้าถึงและมีโทเค็นการรีเฟรชที่เกี่ยวข้อง ระบบจะเพิกถอนโทเค็นการรีเฟรชด้วย

    หากการเพิกถอนได้รับการประมวลผลเรียบร้อยแล้ว รหัสสถานะของการตอบกลับจะเป็น 200 สำหรับเงื่อนไขข้อผิดพลาด ระบบจะแสดงรหัสสถานะ 400 พร้อมกับรหัสข้อผิดพลาด

    Node.js

    หากต้องการเพิกถอนโทเค็นแบบเป็นโปรแกรม ให้ส่งคำขอ HTTPS POST ไปยังปลายทาง /revoke ดังนี้

    const https = require('https');
    
    // Build the string for the POST request
    let postData = "token=" + userCredential.access_token;
    
    // Options for POST request to Google's OAuth 2.0 server to revoke a token
    let postOptions = {
      host: 'oauth2.googleapis.com',
      port: '443',
      path: '/revoke',
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
      }
    };
    
    // Set up the request
    const postReq = https.request(postOptions, function (res) {
      res.setEncoding('utf8');
      res.on('data', d => {
        console.log('Response: ' + d);
      });
    });
    
    postReq.on('error', error => {
      console.log(error)
    });
    
    // Post the request with data
    postReq.write(postData);
    postReq.end();

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

    หากการเพิกถอนได้รับการประมวลผลเรียบร้อยแล้ว รหัสสถานะของการตอบกลับจะเป็น 200 สำหรับเงื่อนไขข้อผิดพลาด ระบบจะแสดงรหัสสถานะ 400 พร้อมกับรหัสข้อผิดพลาด

    HTTP/REST

    หากต้องการเพิกถอนโทเค็นแบบเป็นโปรแกรม แอปพลิเคชันของคุณจะส่งคำขอไปยัง https://oauth2.googleapis.com/revoke และรวมโทเค็นเป็นพารามิเตอร์ ดังนี้

    curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
            https://oauth2.googleapis.com/revoke?token={token}

    โทเค็นอาจเป็นโทเค็นเพื่อการเข้าถึงหรือโทเค็นการรีเฟรชก็ได้ หากโทเค็นเป็นโทเค็นเพื่อการเข้าถึงและมีโทเค็นการรีเฟรชที่เกี่ยวข้อง ระบบจะเพิกถอนโทเค็นการรีเฟรชด้วย

    หากการเพิกถอนได้รับการประมวลผลเรียบร้อยแล้ว รหัสสถานะ HTTP ของการตอบกลับจะเป็น 200 สำหรับเงื่อนไขข้อผิดพลาด ระบบจะแสดงรหัสสถานะ HTTP 400 พร้อมกับรหัสข้อผิดพลาด

    การเข้าถึงตามเวลา

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

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

    ใช้การป้องกันแบบครอบคลุมหลายบริการ

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

    ตัวอย่างประเภทเหตุการณ์ที่บริการการป้องกันแบบครอบคลุมหลายบริการของ Google ส่งไปยังแอปของคุณมีดังนี้

    • https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
    • https://schemas.openid.net/secevent/oauth/event-type/token-revoked
    • https://schemas.openid.net/secevent/risc/event-type/account-disabled

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