ユーザー認証を使用するチャットアプリは、ユーザーがリクエストされたスコープのサブセットを付与できるように、粒度の細かい OAuth 権限をサポートする必要があります。たとえば、ユーザーが名前へのアクセスを許可しても、カレンダーへのアクセスを拒否する場合があります。
きめ細かな OAuth 権限の処理は、Chat 用アプリの構築方法によって異なります。
Apps Script
Apps Script を使用して Chat 用アプリを構築する場合、Apps Script はきめ細かい OAuth 権限を自動的に処理します。ただし、ユーザーがリクエストされたすべてのスコープを付与しないケースをコードで処理するようにしてください。Apps Script でのきめ細かい OAuth 権限の処理の手順に沿って操作します。
HTTP エンドポイント
HTTP エンドポイントを使用して Chat 用アプリを構築する場合は、きめ細かい OAuth 権限を処理するようにコードを構成します。ユーザーが付与した認証スコープを確認し、必要に応じて、不足しているスコープまたはすべてのスコープの認証をリクエストします。
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": {} } }ユーザーが付与したスコープを確認するには、
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() != 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 } })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();
詳細な手順については、HTTP Google Workspace アドオンの詳細な権限を管理するをご覧ください。
関連トピック
- Google Chat の認証と認可の概要については、認証と認可についてをご覧ください。
- ユーザー認証を設定するには、Google Chat ユーザーとして認証と認可を行うをご覧ください。
- Apps Script または HTTP Chat 用アプリで詳細な OAuth 権限を設定する方法については、以下をご覧ください。
- きめ細かい OAuth 権限について詳しくは、きめ細かい OAuth 権限をご覧ください。
アドオンではない Chat 用アプリ: Google Chat 用アプリの詳細な OAuth 権限を管理する
Google Workspace アドオンではない Chat 用アプリを管理している場合は、次の手順に沿ってきめ細かい OAuth 権限を管理します。
アドオンではない Apps Script Chat 用アプリ
Apps Script を使用してアドオンではない Chat 用アプリを作成した場合、Apps Script で粒度の細かい OAuth 権限を処理するの手順は、次の 1 つの考慮事項を除いて機能します。
ScriptApp.requireScopes
は、指定されたスコープが付与されていない場合、スクリプトの実行を停止しますが、ユーザーには OAuth 同意画面ではなく、Chat の設定カードが表示されます。構成カードでは、未付与のスコープだけでなく、リクエストされたすべてのスコープを付与するようユーザーに常に求められます。
個々の認可スコープ レベルのチェックを行うには、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 Chat 用アプリ
アドオンではない 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'
)
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();