إدارة أذونات OAuth الدقيقة لتطبيقات Google Chat

يجب أن تتوافق تطبيقات المحادثات التي تستخدم مصادقة المستخدم مع أذونات OAuth الدقيقة للسماح للمستخدمين بمنح مجموعة فرعية من النطاقات المطلوبة. على سبيل المثال، قد يمنح المستخدم إذن الوصول إلى اسمه ولكن يرفض منح إذن الوصول إلى تقويمه.

تعتمد طريقة التعامل مع أذونات OAuth الدقيقة على طريقة إنشاء تطبيق Chat:

برمجة التطبيقات

إذا أنشأت تطبيق Chat باستخدام برمجة تطبيقات Google، ستتعامل "برمجة تطبيقات Google" مع أذونات OAuth الدقيقة تلقائيًا. ومع ذلك، تأكَّد من أنّ الرمز البرمجي يعالج الحالات التي لا يمنح فيها المستخدم جميع النطاقات المطلوبة. اتّبِع التعليمات الواردة في مقالة التعامل مع أذونات OAuth الدقيقة في "برمجة تطبيقات Google".

نقاط نهاية 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']
            }
        })
    

    جافا

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

    جافا

    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 في "برمجة التطبيقات" التي ليست إضافات

إذا أنشأت تطبيقًا في Chat ليس إضافة باستخدام "برمجة تطبيقات Google"، ستعمل التعليمات الواردة في التعامل مع أذونات OAuth الدقيقة في "برمجة تطبيقات Google" مع مراعاة ما يلي:

ScriptApp.requireScopes يوقف تنفيذ البرنامج النصي إذا لم يتم منح النطاقات المحدّدة، ولكن تظهر للمستخدم بطاقة إعدادات في Chat بدلاً من شاشة طلب الموافقة في OAuth. تطلب بطاقة الإعدادات دائمًا من المستخدم منح جميع النطاقات المطلوبة بدلاً من النطاقات التي لم يتم منحها فقط.

لتقديم عمليات تحقّق فردية على مستوى نطاق التفويض، استخدِم ScriptApp.getAuthorizationInfo للتحقّق من التفويض، وإذا لزم الأمر، اطلب التفويض باستخدام رسالة خاصة.

يوضّح المثال التالي كيفية التحقّق من توفّر إذن معيّن (مثل إذن الوصول إلى التقويم)، وفي حال عدم توفّره، يتم عرض رسالة خاصة تتضمّن عنوان URL الخاص بالتفويض المطلوب.

برمجة التطبيقات

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

تطبيقات Chat التي تستخدم بروتوكول HTTP وليست إضافات

إذا كان تطبيق 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'
)

جافا

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