Google Workspace 外掛程式專案可以透過 Apps Script 內建和進階服務直接連線至許多 Google 產品。
此外,您也可以存取非 Google 的 API 和服務。如果服務不需要授權,您通常只需提出適當的 UrlFetch
要求,然後讓外掛程式解讀回應。
不過,如果非 Google 服務確實需要授權,您就必須為該服務設定 OAuth。您可以使用 Apps Script 的 OAuth2 程式庫來簡化這項程序 (另提供 OAuth1 版本)。
使用 OAuth 服務
使用 OAuth 服務物件連線至非 Google 服務時,Google Workspace 外掛程式必須偵測何時需要授權,以及何時會叫用授權流程。
授權流程包括:
- 通知使用者需要驗證,並提供啟動程序的連結。
- 向非 Google 服務取得授權。
- 重新整理外掛程式即可重新存取受保護的資源。
當需要非 Google 授權時,Google Workspace 外掛程式基礎架構會處理這些詳細資料。您的外掛程式只需要偵測需要授權的時機,並在必要時叫用授權流程。
偵測需要授權
要求可能因為各種原因而無權存取受保護的資源,例如:
- 尚未產生存取權杖或存取權杖已過期。
- 存取權杖不涵蓋要求的資源。
- 存取權杖不包含要求所需的範圍。
外掛程式程式碼應能偵測到這類情況。OAuth 程式庫 hasAccess()
函式可指出您目前是否有權存取服務。或者,使用 UrlFetchApp fetch()
要求時,您可以將 muteHttpExceptions
參數設為 true
。這麼做可避免要求在要求失敗時擲回例外狀況,並讓您檢查傳回的 HttpResponse
物件中的要求回應代碼和內容。
當外掛程式偵測到需要授權時,應該觸發授權流程。
叫用授權流程
如要叫用授權流程,請使用卡片服務建立 AuthorizationException
物件、設定其屬性,然後呼叫 throwException()
函式。擲回例外狀況之前,您必須提供下列項目:
- 必要。授權網址。這個位置是由非 Google 服務指定,也是授權流程開始時使用者前往的位置。請使用
setAuthorizationUrl()
函式設定這個網址。 - 必要。資源顯示名稱字串。在提出授權要求時,向使用者識別資源。您可以使用
setResourceDisplayName()
函式設定此名稱。 - 建立自訂授權提示的回呼函式名稱。這個回呼會傳回建構的
Card
物件陣列,組合 UI 來處理授權。此為選用步驟;如果未設定,系統會使用預設的授權資訊卡。請使用setCustomUiCallback()
函式設定回呼函式。
非 Google OAuth 設定範例
本程式碼範例說明如何設定外掛程式,以便使用需要 OAuth 的非 Google API。並使用 Apps Script 的 OAuth2 建構用於存取 API 的服務。
/**
* Attempts to access a non-Google API using a constructed service
* object.
*
* If your add-on needs access to non-Google APIs that require OAuth,
* you need to implement this method. You can use the OAuth1 and
* OAuth2 Apps Script libraries to help implement it.
*
* @param {String} url The URL to access.
* @param {String} method_opt The HTTP method. Defaults to GET.
* @param {Object} headers_opt The HTTP headers. Defaults to an empty
* object. The Authorization field is added
* to the headers in this method.
* @return {HttpResponse} the result from the UrlFetchApp.fetch() call.
*/
function accessProtectedResource(url, method_opt, headers_opt) {
var service = getOAuthService();
var maybeAuthorized = service.hasAccess();
if (maybeAuthorized) {
// A token is present, but it may be expired or invalid. Make a
// request and check the response code to be sure.
// Make the UrlFetch request and return the result.
var accessToken = service.getAccessToken();
var method = method_opt || 'get';
var headers = headers_opt || {};
headers['Authorization'] =
Utilities.formatString('Bearer %s', accessToken);
var resp = UrlFetchApp.fetch(url, {
'headers': headers,
'method' : method,
'muteHttpExceptions': true, // Prevents thrown HTTP exceptions.
});
var code = resp.getResponseCode();
if (code >= 200 && code < 300) {
return resp.getContentText("utf-8"); // Success
} else if (code == 401 || code == 403) {
// Not fully authorized for this action.
maybeAuthorized = false;
} else {
// Handle other response codes by logging them and throwing an
// exception.
console.error("Backend server error (%s): %s", code.toString(),
resp.getContentText("utf-8"));
throw ("Backend server error: " + code);
}
}
if (!maybeAuthorized) {
// Invoke the authorization flow using the default authorization
// prompt card.
CardService.newAuthorizationException()
.setAuthorizationUrl(service.getAuthorizationUrl())
.setResourceDisplayName("Display name to show to the user")
.throwException();
}
}
/**
* Create a new OAuth service to facilitate accessing an API.
* This example assumes there is a single service that the add-on needs to
* access. Its name is used when persisting the authorized token, so ensure
* it is unique within the scope of the property store. You must set the
* client secret and client ID, which are obtained when registering your
* add-on with the API.
*
* See the Apps Script OAuth2 Library documentation for more
* information:
* https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
*
* @return A configured OAuth2 service object.
*/
function getOAuthService() {
return OAuth2.createService('SERVICE_NAME')
.setAuthorizationBaseUrl('SERVICE_AUTH_URL')
.setTokenUrl('SERVICE_AUTH_TOKEN_URL')
.setClientId('CLIENT_ID')
.setClientSecret('CLIENT_SECRET')
.setScope('SERVICE_SCOPE_REQUESTS')
.setCallbackFunction('authCallback')
.setCache(CacheService.getUserCache())
.setPropertyStore(PropertiesService.getUserProperties());
}
/**
* Boilerplate code to determine if a request is authorized and returns
* a corresponding HTML message. When the user completes the OAuth2 flow
* on the service provider's website, this function is invoked from the
* service. In order for authorization to succeed you must make sure that
* the service knows how to call this function by setting the correct
* redirect URL.
*
* The redirect URL to enter is:
* https://script.google.com/macros/d/<Apps Script ID>/usercallback
*
* See the Apps Script OAuth2 Library documentation for more
* information:
* https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
*
* @param {Object} callbackRequest The request data received from the
* callback function. Pass it to the service's
* handleCallback() method to complete the
* authorization process.
* @return {HtmlOutput} a success or denied HTML message to display to
* the user. Also sets a timer to close the window
* automatically.
*/
function authCallback(callbackRequest) {
var authorized = getOAuthService().handleCallback(callbackRequest);
if (authorized) {
return HtmlService.createHtmlOutput(
'Success! <script>setTimeout(function() { top.window.close() }, 1);</script>');
} else {
return HtmlService.createHtmlOutput('Denied');
}
}
/**
* Unauthorizes the non-Google service. This is useful for OAuth
* development/testing. Run this method (Run > resetOAuth in the script
* editor) to reset OAuth to re-prompt the user for OAuth.
*/
function resetOAuth() {
getOAuthService().reset();
}
建立自訂授權提示
根據預設,授權提示沒有任何品牌宣傳內容,而且只會使用顯示名稱字串來指出外掛程式嘗試存取的資源。不過,外掛程式可以定義用途相同的自訂授權資訊卡,可包含其他資訊和品牌宣傳內容。
您可以實作自訂 UI 回呼函式來定義自訂提示,這個函式會傳回已建構的 Card
物件陣列。這個陣列只能包含一張資訊卡。如果提供更多資訊,這些標頭會顯示在清單中,可能會讓使用者感到困惑。
退回的卡片必須符合以下條件:
- 請向使用者清楚說明外掛程式要求的是代為存取非 Google 服務的權限。
- 清楚說明外掛程式取得授權後可執行的操作。
- 包含按鈕或類似的小工具,將使用者導向服務的授權網址。請確保使用者能清楚看見這個小工具的功能。
- 上述小工具必須在其
OpenLink
物件中使用OnClose.RELOAD_ADD_ON
設定,以確保外掛程式在收到授權後會重新載入。 - 從授權提示開啟的所有連結都必須使用 HTTPS。
您可以藉由呼叫 AuthorizationException
物件上的 setCustomUiCallback()
函式,引導授權流程使用卡片。
下例顯示自訂授權提示回呼函式:
/**
* Returns an array of cards that comprise the customized authorization
* prompt. Includes a button that opens the proper authorization link
* for a non-Google service.
*
* When creating the text button, using the
* setOnClose(CardService.OnClose.RELOAD_ADD_ON) function forces the add-on
* to refresh once the authorization flow completes.
*
* @return {Card[]} The card representing the custom authorization prompt.
*/
function create3PAuthorizationUi() {
var service = getOAuthService();
var authUrl = service.getAuthorizationUrl();
var authButton = CardService.newTextButton()
.setText('Begin Authorization')
.setAuthorizationAction(CardService.newAuthorizationAction()
.setAuthorizationUrl(authUrl));
var promptText =
'To show you information from your 3P account that is relevant' +
' to the recipients of the email, this add-on needs authorization' +
' to: <ul><li>Read recipients of the email</li>' +
' <li>Read contact information from 3P account</li></ul>.';
var card = CardService.newCardBuilder()
.setHeader(CardService.newCardHeader()
.setTitle('Authorization Required'))
.addSection(CardService.newCardSection()
.setHeader('This add-on needs access to your 3P account.')
.addWidget(CardService.newTextParagraph()
.setText(promptText))
.addWidget(CardService.newButtonSet()
.addButton(authButton)))
.build();
return [card];
}
/**
* When connecting to the non-Google service, pass the name of the
* custom UI callback function to the AuthorizationException object
*/
function accessProtectedResource(url, method_opt, headers_opt) {
var service = getOAuthService();
if (service.hasAccess()) {
// Make the UrlFetch request and return the result.
// ...
} else {
// Invoke the authorization flow using a custom authorization
// prompt card.
CardService.newAuthorizationException()
.setAuthorizationUrl(service.getAuthorizationUrl())
.setResourceDisplayName("Display name to show to the user")
.setCustomUiCallback('create3PAuthorizationUi')
.throwException();
}
}
管理 Google Workspace 應用程式的第三方登入程序
Google Workspace 外掛程式的常見應用程式之一,就是提供從 Google Workspace 代管應用程式中與第三方系統互動的介面。Apps Script 的 OAuth2 程式庫可協助您建立及管理第三方服務的連線。
第三方系統通常會要求使用者以使用者 ID、密碼或其他憑證登入。當使用者在使用某個 Google Workspace 主機登入第三方服務時,您必須確保他們切換至其他 Google Workspace 主機時,不須再次登入。為避免重複登入要求,請使用使用者屬性或 ID 權杖。我們會在以下各節中說明。
使用者屬性
您可以將使用者的登入資料儲存在 Apps Script 的使用者屬性中。舉例來說,您可以從他們的登入服務建立自己的 JWT 並記錄在使用者屬性中,或是記錄該服務的使用者名稱和密碼。
使用者屬性設有限制,確保使用者只能在外掛程式指令碼中存取這些屬性。其他使用者和其他指令碼無法存取這些屬性。詳情請參閱 PropertiesService
。
ID 權杖
您可以使用 Google ID 權杖做為服務的登入憑證。這是執行單一登入的方式。由於使用者是 Google 代管應用程式,因此使用者已登入 Google。