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

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

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

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

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

إضافات Google Workspace التي توسّع نطاق Chat

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

تطبيقات Chat المستقلة التي تم إنشاؤها باستخدام "برمجة تطبيقات Google"

إذا أنشأت تطبيق 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.
  // ...
}

نقاط نهاية HTTP

إذا أنشأت تطبيق Chat باستخدام نقاط نهاية HTTP، يجب أن يتيح تطبيق Chat أذونات OAuth الدقيقة.

إضافات Google Workspace التي توسّع نطاق Chat

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

  1. في ملف بيان الإضافة، حدِّد نطاقات التفويض المطلوبة في الحقل 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();
    }
    

    لطلب جميع النطاقات المرتبطة بالإضافة، اضبط 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 المستقلة التي تم إنشاؤها باستخدام HTTP

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

جافا

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 الدقيقة.