Zarządzanie szczegółowymi uprawnieniami OAuth dla aplikacji Google Chat

Aplikacje do czatu, które korzystają z uwierzytelniania użytkowników, muszą obsługiwać szczegółowe uprawnienia OAuth, aby umożliwić użytkownikom przyznawanie podzbioru żądanych zakresów. Na przykład użytkownik może przyznać dostęp do swojego imienia i nazwiska, ale odmówić dostępu do kalendarza.

Obsługa szczegółowych uprawnień OAuth zależy od sposobu tworzenia aplikacji do czatu:

Google Apps Script

Jeśli tworzysz aplikację Google Chat za pomocą Apps Script, Apps Script automatycznie obsługuje szczegółowe uprawnienia OAuth. Upewnij się jednak, że kod obsługuje przypadki, w których użytkownik nie przyznaje wszystkich wymaganych zakresów. Metoda zależy od tego, czy skrypt Apps Script jest dodatkiem do Google Workspace, który rozszerza Google Chat za pomocą Apps Script, czy samodzielną aplikacją do czatu utworzoną za pomocą Apps Script i zdarzeń interakcji.

Dodatki do Google Workspace, które rozszerzają funkcje Google Chat

Jeśli tworzysz aplikację Google Chat jako dodatek do Google Workspace, który rozszerza Google Chat za pomocą Apps Script, postępuj zgodnie z instrukcjami w artykule Obsługa szczegółowych uprawnień OAuth w Apps Script.

Samodzielne aplikacje Google Chat w Apps Script

Jeśli tworzysz aplikację Google Chat za pomocą Apps Script i zdarzeń interakcji, instrukcje w artykule Obsługa szczegółowych uprawnień OAuth w Apps Script działają z jednym zastrzeżeniem:

ScriptApp.requireScopes zatrzymuje wykonywanie skryptu, jeśli określone zakresy nie zostaną przyznane, ale użytkownik widzi w Google Chat kartę konfiguracji zamiast ekranu zgody OAuth. Karta konfiguracji zawsze prosi użytkownika o przyznanie wszystkich żądanych zakresów, a nie tylko tych, które nie zostały jeszcze przyznane.

Aby przeprowadzać indywidualne kontrole autoryzacji na poziomie zakresu, użyj ScriptApp.getAuthorizationInfo, aby sprawdzić autoryzację i w razie potrzeby poprosić o nią za pomocą wiadomości prywatnej.

Poniższy przykład pokazuje, jak sprawdzić, czy użytkownik ma określone uprawnienia (np. dostęp do kalendarza), a jeśli ich nie ma, zwrócić prywatną wiadomość z wymaganym adresem URL autoryzacji.

Google 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.
  // ...
}

Punkty końcowe HTTP

Jeśli tworzysz aplikację do czatu za pomocą punktów końcowych HTTP, powinna ona obsługiwać szczegółowe uprawnienia OAuth.

Dodatki do Google Workspace, które rozszerzają funkcje Google Chat

Jeśli tworzysz aplikację do czatu jako dodatek do Google Workspace (na przykład jeśli rozszerza ona inne aplikacje Google Workspace, takie jak Dysk Google czy Gmail), skonfiguruj plik manifestu i kod, aby obsługiwać szczegółowe uprawnienia OAuth:

  1. W pliku manifestu dodatku ustaw pole granularOauthPermissionSupport na wartość OPT_IN. Więcej informacji o polu granularOauthPermissionSupport znajdziesz w artykule Migracja do szczegółowego przepływu uprawnień OAuth.

    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": {},
        "httpOptions": {
          "granularOauthPermissionSupport": "OPT_IN"
        }
      }
    }
    
  2. Aby sprawdzić, jakie zakresy użytkownik przyznał, w kodzie sprawdź pole authorizationEventObject.authorizedScopes. Jeśli brakuje wymaganego zakresu, zwróć działanie requesting_google_scopes, aby poprosić użytkownika o udzielenie dostępu do brakujących zakresów.

    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().getAuthorizedScopes();
    if (!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();
    }
    

    Aby poprosić o wszystkie zakresy powiązane z dodatkiem, ustaw dla parametru all_scopes wartość 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();
    

Szczegółowe instrukcje znajdziesz w artykule Zarządzanie szczegółowymi uprawnieniami dodatków do Google Workspace HTTP.

Samodzielne aplikacje do czatu HTTP

Jeśli aplikacja Google Chat jest samodzielną usługą HTTP (nie jest dodatkiem do Google Workspace), musisz samodzielnie zarządzać przepływem OAuth 2.0.

Gdy pobierasz zapisany token lub wymieniasz kod autoryzacji, sprawdź, jakie zakresy zostały przyznane. Jeśli brakuje wymaganych zakresów, poproś użytkownika o ich autoryzację.

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();

Więcej informacji znajdziesz w artykule Szczegółowe uprawnienia OAuth.