Управляйте детальными разрешениями OAuth для приложений Google Chat.

Приложения для чата, использующие аутентификацию пользователей, должны поддерживать детализированные разрешения OAuth , позволяющие пользователям предоставлять подмножество запрашиваемых областей доступа. Например, пользователь может предоставить доступ к своему имени, но отказать в доступе к своему календарю.

Обработка детальных разрешений OAuth зависит от того, как вы разрабатываете свое приложение для чата:

Apps Script

Если вы создаете приложение для чата с помощью Apps Script , Apps Script автоматически обрабатывает детализированные разрешения OAuth. Однако убедитесь, что ваш код обрабатывает случаи, когда пользователь не предоставляет все запрошенные области действия. Метод зависит от того, является ли ваш Apps Script надстройкой Google Workspace, расширяющей возможности Google Chat с помощью Apps Script, или автономным приложением для чата, созданным с помощью Apps Script и событий взаимодействия.

Дополнения Google Workspace, расширяющие возможности чата.

Если вы создаете свое приложение для чата как надстройку Google Workspace, расширяющую функциональность Google Chat с помощью Apps Script , следуйте инструкциям в разделе «Обработка детализированных разрешений OAuth в Apps Script» .

Автономные приложения Скриптовые приложения для чата

Если вы создаете приложение для чата с помощью Apps Script и событий взаимодействия , инструкции в разделе «Обработка детализированных разрешений OAuth в Apps Script» работают с одним исключением:

ScriptApp.requireScopes останавливает выполнение скрипта, если указанные области действия не предоставлены, но пользователь видит в чате карточку конфигурации вместо экрана согласия 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-конечные точки

Если вы создаете свое приложение для чата с использованием HTTP-конечных точек , оно должно поддерживать детальную настройку разрешений OAuth.

Дополнения Google Workspace, расширяющие возможности чата.

Если вы разрабатываете приложение для чата как надстройку для Google Workspace (например, если оно расширяет функциональность других приложений Google Workspace, таких как Google Drive или Gmail), настройте файл манифеста и код для обработки детализированных разрешений OAuth:

  1. В файле манифеста вашего дополнения установите для поля granularOauthPermissionSupport значение OPT_IN . Подробнее о поле granularOauthPermissionSupport см. в разделе «Переход к детализированному потоку разрешений 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. Чтобы узнать, какие права доступа предоставил пользователь, проверьте в своем коде поле 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().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();
    }
    

    Чтобы запросить все области действия, связанные с дополнением, установите 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» .

Автономные HTTP-приложения для чата

Если ваше приложение для чата представляет собой автономный 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» .