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

Aplikacje Google Chat, które korzystają z uwierzytelniania użytkowników, muszą obsługiwać szczegółowe uprawnienia OAuth aby użytkownicy mogli przyznawać podzbiór żądanych zakresów. Użytkownik może na przykład przyznać dostęp do swojej nazwy, ale odmówić dostępu do kalendarza.

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

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 Twój kod obsługuje przypadki, w których użytkownik nie przyznaje wszystkich żądanych zakresów. Metoda zależy od tego, czy Apps Script jest dodatkiem do Google Workspace, który rozszerza Google Chat za pomocą Apps Script, czy samodzielną aplikacją Google Chat utworzoną za pomocą Apps Script i zdarzeń interakcji.

Dodatki do Google Workspace, które rozszerzają 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 utworzone za pomocą 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 przyznane.

Aby przeprowadzić indywidualne sprawdzanie zakresu autoryzacji, użyj ScriptApp.getAuthorizationInfo , aby sprawdzić autoryzację, a w razie potrzeby poprosić o autoryzację za pomocą wiadomości prywatnej.

Poniższy przykład pokazuje, jak sprawdzić konkretne uprawnienie (np. dostęp do kalendarza) i w razie jego braku zwrócić wiadomość prywatną z wymaganym adresem URL autoryzacji.

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ę Google Chat za pomocą punktów końcowych HTTP, Twoja aplikacja Google Chat powinna obsługiwać szczegółowe uprawnienia OAuth.

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

Jeśli tworzysz aplikację Google Chat jako dodatek do Google Workspace, skonfiguruj kod tak, aby obsługiwał szczegółowe uprawnienia OAuth. Sprawdź, które zakresy autoryzacji przyznał użytkownik, a w razie potrzeby poproś o autoryzację brakujących lub wszystkich zakresów.

  1. W pliku manifestu dodatku określ wymagane zakresy autoryzacji w polu oauthScopes. To pole jest częścią zasobu projects.deployments.

    Poniższy przykład wymaga zakresów autoryzacji chat.messages i 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. Aby sprawdzić, które zakresy przyznał użytkownik, sprawdź pole authorizationEventObject.authorizedScopes. Jeśli brakuje wymaganego zakresu, zwróć działanie requesting_google_scopes , aby poprosić użytkownika o brakujące zakresy.

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

    Aby poprosić o wszystkie zakresy powiązane z dodatkiem, ustaw all_scopes na 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 utworzonych za pomocą HTTP.

Samodzielne aplikacje Google Chat utworzone za pomocą HTTP

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

Gdy pobierasz zapisany token lub wymieniasz kod autoryzacji, sprawdź, które 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.