จัดการสิทธิ์ OAuth แบบละเอียดสำหรับแอป Google Chat

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

การจัดการสิทธิ์ OAuth แบบละเอียดจะขึ้นอยู่กับวิธีที่คุณสร้างแอปใน Chat ดังนี้

Apps Script

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

ส่วนเสริม Google Workspace ที่ขยายการทำงานของ Chat

หากคุณสร้างแอป Chat เป็น ส่วนเสริม Google Workspace ที่ขยายการทำงานของ Google Chat โดยใช้ Apps Script, ให้ทำตามวิธีการใน หัวข้อจัดการสิทธิ์ OAuth แบบละเอียดใน Apps Script

แอปใน Chat แบบสแตนด์อโลนของ Apps Script

หากคุณสร้างแอปใน Chat โดยใช้ Apps Script และเหตุการณ์การโต้ตอบ, วิธีการในหัวข้อ จัดการสิทธิ์ OAuth แบบละเอียดใน Apps Script จะใช้ได้โดยมีข้อควรพิจารณาดังนี้

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

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

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

Apps Script

/**
* Responds to a MESSAGE event in Google Chat.
* Checks for required permissions and if missing asks for them.
*
* @param {Object} event the event object from Chat
* @return {Object} JSON response
*/
function onMessage(event) {
  // Check if the script has the necessary permissions.
  // In this example, the script checks for the "calendar.events" scope.
  var requiredScopes = ['https://www.googleapis.com/auth/calendar.events'];
  var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL, requiredScopes);

  // If permissions are missing, return a message with the authorization URL.
  if (authInfo.getAuthorizationStatus() === ScriptApp.AuthorizationStatus.REQUIRED) {
    var authUrl = authInfo.getAuthorizationUrl();
    return {
      "text": "This action requires authorization. Please <" + authUrl + "|click here to authorize>.",
      "privateMessageViewer": {
        "name": event.user.name
      }
    };
  }

  // Permission granted; proceed with the application logic.
  // ...
}

ปลายทาง HTTP

หากคุณสร้างแอปใน Chat โดยใช้ ปลายทาง HTTP แอปใน Chat ของคุณควรจะรองรับสิทธิ์ OAuth แบบละเอียด

ส่วนเสริม Google Workspace ที่ขยายการทำงานของ Chat

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

  1. ในไฟล์ Manifest ของส่วนเสริม ให้ระบุขอบเขตการให้สิทธิ์ที่จำเป็นในช่อง oauthScopes ช่องนี้เป็น ส่วนหนึ่งของทรัพยากรprojects.deployments

    ตัวอย่างต่อไปนี้ต้องใช้ขอบเขตการให้สิทธิ์ chat.messages และ calendar.events

    JSON

    {
      "oauthScopes": [
        "https://www.googleapis.com/auth/chat.messages",
        "https://www.googleapis.com/auth/calendar.events"
      ],
      "addOns": {
        "common": {
          "name": "My Chat App",
          "logoUrl": "https://lh3.googleusercontent.com/..."
        },
        "chat": {},
        "calendar": {},
        "httpOptions": {}
      }
    }
    
  2. หากต้องการดูขอบเขตที่ผู้ใช้ให้สิทธิ์แล้ว ให้ตรวจสอบช่อง authorizationEventObject.authorizedScopes หากไม่มีขอบเขตที่จำเป็น ให้ส่งคืน requesting_google_scopes การดำเนินการเพื่อแจ้งให้ผู้ใช้ให้สิทธิ์ขอบเขตที่ขาดหายไป

    Node.js

    // Check for authorized scopes.
    const authorizedScopes = req.body.authorizationEventObject?.authorizedScopes || [];
    if (!authorizedScopes.includes('https://www.googleapis.com/auth/chat.messages')) {
      // Respond with a request for the missing scope.
      res.send({
        'requesting_google_scopes': {
          'scopes': ['https://www.googleapis.com/auth/chat.messages']
        }
      });
      return;
    }
    

    Python

    from flask import jsonify, request
    
    # Check for authorized scopes.
    event_data = request.get_json()
    authorized_scopes = event_data.get('authorizationEventObject', {}).get('authorizedScopes', [])
    if 'https://www.googleapis.com/auth/chat.messages' not in authorized_scopes:
        # Respond with a request for the missing scope.
        return jsonify({
            'requesting_google_scopes': {
                'scopes': ['https://www.googleapis.com/auth/chat.messages']
            }
        })
    

    Java

    import com.google.gson.JsonArray;
    import com.google.gson.JsonObject;
    import java.util.List;
    
    // Check for authorized scopes.
    List<String> authorizedScopes = event.getAuthorizationEventObject() != null
        ? event.getAuthorizationEventObject().getAuthorizedScopes()
        : null;
    if (authorizedScopes == null || !authorizedScopes.contains("https://www.googleapis.com/auth/chat.messages")) {
      // Respond with a request for the missing scope.
      JsonObject requestingGoogleScopes = new JsonObject();
      JsonArray scopes = new JsonArray();
      scopes.add("https://www.googleapis.com/auth/chat.messages");
      requestingGoogleScopes.add("scopes", scopes);
    
      JsonObject response = new JsonObject();
      response.add("requesting_google_scopes", requestingGoogleScopes);
      return response.toString();
    }
    

    หากต้องการขอขอบเขตทั้งหมดที่เชื่อมโยงกับส่วนเสริม ให้ตั้งค่า all_scopes เป็น true

    Node.js

    res.send({
      'requesting_google_scopes': { 'all_scopes': true }
    });
    

    Python

    from flask import jsonify
    
    return jsonify({
        'requesting_google_scopes': { 'all_scopes': True }
    })
    

    Java

    import com.google.gson.JsonObject;
    
    JsonObject requestingGoogleScopes = new JsonObject();
    requestingGoogleScopes.addProperty("all_scopes", true);
    
    JsonObject response = new JsonObject();
    response.add("requesting_google_scopes", requestingGoogleScopes);
    return response.toString();
    

ดูวิธีการโดยละเอียดได้ที่หัวข้อ จัดการสิทธิ์แบบละเอียดสำหรับส่วนเสริม Google Workspace ของ HTTP

แอปใน Chat แบบสแตนด์อโลนของ HTTP

หากแอปใน Chat เป็นบริการ HTTP แบบสแตนด์อโลน (ไม่ใช่ส่วนเสริม Google Workspace) คุณจะต้องจัดการขั้นตอน OAuth 2.0 ด้วยตนเอง

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

Node.js

// 1. List authorized scopes.
const fs = require('fs');
const tokens = JSON.parse(fs.readFileSync('token.json'));
const grantedScopes = tokens.scope.split(' ');

// 2. Detect missing scopes.
const requiredScopes = ['https://www.googleapis.com/auth/chat.messages'];
const missingScopes = requiredScopes.filter(scope => !grantedScopes.includes(scope));

if (missingScopes.length > 0) {
  // 3. Request missing scopes.
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: missingScopes,
    include_granted_scopes: true
  });
  res.redirect(authUrl);
}

// To request all scopes instead of just the missing ones:
const allScopesAuthUrl = oauth2Client.generateAuthUrl({
  access_type: 'offline',
  scope: requiredScopes,
  include_granted_scopes: true
});

Python

from flask import redirect
from google.oauth2.credentials import Credentials

# 1. List authorized scopes.
credentials = Credentials.from_authorized_user_file('token.json')
granted_scopes = set(credentials.scopes)

# 2. Detect missing scopes.
required_scopes = {'https://www.googleapis.com/auth/chat.messages'}
missing_scopes = required_scopes - granted_scopes

if missing_scopes:
    # 3. Request missing scopes.
    flow.scope = list(missing_scopes)
    auth_url, _ = flow.authorization_url(
        access_type='offline',
        include_granted_scopes=True
    )
    return redirect(auth_url)

# To request all scopes instead of just the missing ones:
flow.scope = list(required_scopes)
all_scopes_auth_url, _ = flow.authorization_url(
    access_type='offline',
    include_granted_scopes='true'
)

Java

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

// 1. List authorized scopes.
// The "user" string is the user ID for which to load credentials.
Credential credential = flow.loadCredential("user");
Collection<String> grantedScopes = credential.getScopes();

// 2. Detect missing scopes.
// The `requiredScopes` variable contains a list of the OAuth scopes
// that your app requires to function. Define this variable with the
// scopes needed by your application.
List<String> requiredScopes = Arrays.asList("https://www.googleapis.com/auth/chat.messages");
List<String> missingScopes = new ArrayList<>();
for (String scope : requiredScopes) {
  if (!grantedScopes.contains(scope)) {
    missingScopes.add(scope);
  }
}

if (!missingScopes.isEmpty()) {
  // 3. Request missing scopes.
  GoogleAuthorizationCodeRequestUrl urlBuilder = new GoogleAuthorizationCodeRequestUrl(
      clientId, redirectUri, missingScopes)
      .setAccessType("offline")
      .set("include_granted_scopes", "true");
  String authUrl = urlBuilder.build();
  response.sendRedirect(authUrl);
}

// To request all scopes instead of just the missing ones:
GoogleAuthorizationCodeRequestUrl allScopesUrlBuilder = new GoogleAuthorizationCodeRequestUrl(
    clientId, redirectUri, requiredScopes)
    .setAccessType("offline")
    .set("include_granted_scopes", "true");
String allScopesAuthUrl = allScopesUrlBuilder.build();

ดูข้อมูลเพิ่มเติมได้ที่ หัวข้อสิทธิ์ OAuth แบบละเอียด