ניהול הרשאות OAuth גרנולריות לאפליקציות ב-Google Chat

אפליקציות ל-Chat שמשתמשות באימות משתמשים צריכות לתמוך בהרשאות OAuth גרנולריות כדי לאפשר למשתמשים להעניק קבוצת משנה של היקפי הרשאות מבוקשים. לדוגמה, משתמש יכול להעניק גישה לשם שלו אבל לדחות גישה ליומן שלו.

הטיפול בהרשאות OAuth מפורטות תלוי באופן שבו אתם בונים את אפליקציית Chat:

Apps Script

אם אתם יוצרים את האפליקציה ל-Chat באמצעות Apps Script, מערכת Apps Script מטפלת אוטומטית בהרשאות OAuth גרנולריות. עם זאת, חשוב לוודא שהקוד מטפל במקרים שבהם משתמש לא מעניק את כל ההיקפים המבוקשים. פועלים לפי ההוראות במאמר טיפול בהרשאות OAuth גרנולריות ב-Apps Script.

נקודות הקצה ב-HTTP

אם אתם יוצרים את אפליקציית Chat באמצעות נקודות קצה של HTTP, אתם צריכים להגדיר את הקוד כך שיטפל בהרשאות OAuth מפורטות. בודקים אילו היקפי הרשאות המשתמש העניק, ואם צריך, מבקשים הרשאה להיקפי הרשאות חסרים או לכל היקפי ההרשאות.

  1. בקובץ המניפסט של אפליקציית Chat, מציינים את היקפי ההרשאות הנדרשים בשדה 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();
    }
    

    כדי לבקש את כל ההיקפים שמשויכים לאפליקציית Chat, מגדירים את 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 שהן לא תוספים: ניהול הרשאות OAuth מפורטות לאפליקציות ל-Google Chat

אם אתם מנהלים אפליקציה ל-Chat שהיא לא תוסף ל-Google Workspace, אתם יכולים להשתמש בהוראות הבאות כדי לנהל הרשאות OAuth גרנולריות.

אפליקציות ל-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 שלא מוגדרות כתוספים

אם אפליקציית Chat שלכם היא שירות HTTP ולא תוסף, אתם צריכים לנהל בעצמכם את תהליך ההרשאה באמצעות 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();