Mengelola izin OAuth terperinci untuk aplikasi Google Chat

Aplikasi chat yang menggunakan autentikasi pengguna harus mendukung izin OAuth terperinci untuk memungkinkan pengguna memberikan subkumpulan cakupan yang diminta. Misalnya, pengguna dapat memberikan akses ke nama mereka, tetapi menolak memberikan akses ke kalender mereka.

Penanganan izin OAuth terperinci bergantung pada cara Anda membangun aplikasi Chat:

Apps Script

Jika Anda membangun aplikasi Chat menggunakan Apps Script, Apps Script akan menangani izin OAuth terperinci secara otomatis. Namun, pastikan kode Anda menangani kasus saat pengguna tidak memberikan semua cakupan yang diminta. Metodenya bergantung pada apakah Apps Script Anda adalah add-on Google Workspace yang memperluas Google Chat menggunakan Apps Script atau aplikasi Chat mandiri yang dibuat dengan Apps Script dan peristiwa interaksi.

Add-on Google Workspace yang memperluas Chat

Jika Anda membuat aplikasi Chat sebagai add-on Google Workspace yang memperluas Google Chat menggunakan Apps Script, ikuti petunjuk di Menangani izin OAuth terperinci di Apps Script.

Aplikasi Chat Apps Script mandiri

Jika Anda membangun aplikasi Chat menggunakan Apps Script dan peristiwa interaksi, petunjuk di Menangani izin OAuth terperinci di Apps Script dapat digunakan dengan satu pertimbangan:

ScriptApp.requireScopes menghentikan eksekusi skrip jika cakupan yang ditentukan tidak diberikan, tetapi pengguna melihat kartu konfigurasi di Chat bukan Layar Izin OAuth. Kartu konfigurasi selalu meminta pengguna untuk memberikan semua cakupan yang diminta, bukan hanya cakupan yang belum diberikan.

Untuk memberikan pemeriksaan tingkat cakupan otorisasi individual, gunakan ScriptApp.getAuthorizationInfo untuk memeriksa otorisasi dan jika perlu meminta otorisasi menggunakan pesan pribadi.

Contoh berikut menunjukkan cara memeriksa izin tertentu (seperti akses kalender) dan jika tidak ada, menampilkan pesan pribadi dengan URL otorisasi yang diperlukan.

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

Endpoint HTTP

Jika Anda membangun aplikasi Chat menggunakan endpoint HTTP, aplikasi Chat Anda harus mendukung izin OAuth terperinci.

Add-on Google Workspace yang memperluas Chat

Jika Anda membuat aplikasi Chat sebagai add-on Google Workspace (misalnya, jika aplikasi tersebut memperluas aplikasi Google Workspace lainnya seperti Google Drive atau Gmail), konfigurasi file manifes dan kode Anda untuk menangani izin OAuth terperinci:

  1. Di file manifes add-on, tetapkan kolom granularOauthPermissionSupport ke OPT_IN. Untuk mempelajari kolom granularOauthPermissionSupport lebih lanjut, lihat Bermigrasi ke alur izin OAuth terperinci.

    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. Untuk melihat cakupan yang diberikan pengguna, dalam kode Anda, periksa kolom authorizationEventObject.authorizedScopes. Jika cakupan yang diperlukan tidak ada, tampilkan tindakan requesting_google_scopes untuk meminta pengguna memberikan cakupan yang tidak ada.

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

    Untuk meminta semua cakupan yang terkait dengan add-on, tetapkan all_scopes ke 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();
    

Untuk mengetahui petunjuk mendetail, lihat Mengelola izin terperinci untuk add-on Google Workspace HTTP.

Aplikasi Chat HTTP mandiri

Jika aplikasi Chat Anda adalah layanan HTTP mandiri (bukan add-on Google Workspace), Anda mengelola alur OAuth 2.0 sendiri.

Saat Anda mengambil token tersimpan atau menukarkan kode otorisasi, periksa cakupan yang diberikan. Jika cakupan yang diperlukan tidak ada, minta pengguna untuk memberikan otorisasi.

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

Untuk mengetahui informasi selengkapnya, lihat Izin OAuth terperinci.