Kết nối với các dịch vụ không phải của Google từ tiện ích bổ sung của Google Workspace

Dự án Google Workspace tiện ích bổ sung của bạn có thể kết nối trực tiếp với nhiều sản phẩm của Google bằng các dịch vụ tích hợpnâng cao của Apps Script.

Bạn cũng có thể truy cập vào các API và dịch vụ không phải của Google. Nếu dịch vụ không yêu cầu uỷ quyền, bạn chỉ cần tạo một yêu cầu UrlFetch thích hợp rồi yêu cầu tiện ích bổ sung diễn giải phản hồi đó.

Tuy nhiên, nếu dịch vụ không phải của Google yêu cầu uỷ quyền, bạn phải định cấu hình OAuth cho dịch vụ đó. Bạn có thể làm cho quá trình này dễ dàng hơn bằng cách sử dụng thư viện OAuth2 cho Apps Script (cũng có một phiên bản OAuth1).

Sử dụng dịch vụ OAuth

Khi sử dụng đối tượng dịch vụ OAuth để kết nối với một dịch vụ không phải của Google, tiện ích bổ sungGoogle Workspace của bạn cần phát hiện thời điểm bắt buộc uỷ quyền và khi có thể, hãy gọi quy trình uỷ quyền.

Quy trình uỷ quyền bao gồm:

  1. Thông báo cho người dùng rằng cần thiết phải xác thực và cung cấp đường liên kết để bắt đầu quá trình.
  2. Đang nhận ủy quyền từ dịch vụ không phải của Google.
  3. Làm mới tiện ích bổ sung để thử truy cập lại vào tài nguyên được bảo vệ.

Khi cần sự cho phép của Google, cơ sở hạ tầng tiện ích bổ sung Google Workspace sẽ xử lý các thông tin chi tiết này. Tiện ích bổ sung của bạn chỉ cần phát hiện thời điểm cần ủy quyền và gọi quy trình ủy quyền khi cần.

Việc phát hiện ủy quyền là bắt buộc

Một yêu cầu có thể không có quyền truy cập vào một tài nguyên được bảo vệ vì nhiều lý do, chẳng hạn như:

  • Mã truy cập chưa được tạo hoặc đã hết hạn.
  • Mã truy cập không bao gồm tài nguyên được yêu cầu.
  • Mã truy cập không bao gồm các phạm vi bắt buộc của yêu cầu.

Mã bổ sung của bạn phải phát hiện được các trường hợp này. Hàm hasAccess() của thư viện OAuth có thể cho bạn biết liệu bạn hiện có quyền truy cập vào một dịch vụ hay không. Ngoài ra, khi sử dụng các yêu cầu UrlFetchApp fetch(), bạn có thể đặt tham số muteHttpExceptions thành true. Thao tác này ngăn yêu cầu gửi một ngoại lệ khi không đạt được yêu cầu và cho phép bạn kiểm tra mã và nội dung phản hồi yêu cầu trong đối tượng HttpResponse được trả về.

Khi phát hiện thấy tiện ích bổ sung cần được cho phép, tiện ích bổ sung sẽ kích hoạt quy trình uỷ quyền.

Gọi quy trình ủy quyền

Bạn gọi quy trình ủy quyền bằng cách sử dụng Dịch vụ thẻ để tạo đối tượng AuthorizationException, đặt thuộc tính của đối tượng, sau đó gọi hàm throwException(). Trước khi gửi trường hợp ngoại lệ, bạn cần cung cấp các thông tin sau:

  1. Bắt buộc. URL uỷ quyền. Dịch vụ này không phải dịch vụ của Google và là vị trí người dùng được đưa đến khi luồng uỷ quyền bắt đầu. Bạn đặt URL này bằng cách sử dụng hàm setAuthorizationUrl().
  2. Bắt buộc. Chuỗi tên hiển thị tài nguyên. Xác định tài nguyên cho người dùng khi có yêu cầu uỷ quyền. Bạn đặt tên này bằng cách sử dụng hàm setResourceDisplayName().
  3. Tên của một hàm gọi lại tạo ra lời nhắc cấp phép tuỳ chỉnh. Lệnh gọi lại này trả về một mảng các đối tượng Card được tạo để tạo một giao diện người dùng nhằm xử lý việc uỷ quyền. Trường này không bắt buộc; nếu bạn không đặt thẻ uỷ quyền mặc định, hệ thống sẽ sử dụng. Bạn đặt hàm callback bằng cách sử dụng hàm setCustomUiCallback().

Ví dụ về cấu hình không phải của Google OAuth

Mã mẫu này cho biết cách định cấu hình tiện ích bổ sung để sử dụng API không phải của Google API yêu cầu OAuth. Công cụ này sử dụng OAuth2 cho Apps Script để tạo dịch vụ để truy cập 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();
}

Tạo lời nhắc ủy quyền tuỳ chỉnh

thẻ uỷ quyền không phải dịch vụ của Google

Theo mặc định, lời nhắc uỷ quyền không có nhãn hiệu nào và chỉ sử dụng chuỗi tên hiển thị để cho biết tài nguyên mà tiện ích bổ sung đang cố gắng truy cập. Tuy nhiên, tiện ích bổ sung có thể xác định một thẻ uỷ quyền tuỳ chỉnh có cùng mục đích và có thể bao gồm các thông tin và thành phần thương hiệu bổ sung.

Bạn xác định lời nhắc tuỳ chỉnh bằng cách triển khai hàm gọi lại trên giao diện người dùng tuỳ chỉnh trả về một loạt các đối tượng Card được tạo. Mảng này chỉ nên chứa một thẻ. Nếu bạn cung cấp nhiều tiêu đề hơn, các tiêu đề của tiêu đề sẽ được hiển thị trong danh sách, có thể dẫn đến trải nghiệm người dùng khó hiểu.

Thẻ được trả về phải thực hiện những việc sau:

  • Cho người dùng biết rõ rằng tiện ích bổ sung đang thay mặt họ yêu cầu quyền truy cập vào một dịch vụ không phải của Google.
  • Nêu rõ những việc tiện ích bổ sung có thể làm nếu được phép.
  • Chứa một nút hoặc tiện ích tương tự đưa người dùng đến URL uỷ quyền của dịch vụ. Đảm bảo rằng người dùng có thể thấy rõ chức năng của tiện ích này.
  • Tiện ích trên phải sử dụng chế độ cài đặt OnClose.RELOAD_ADD_ON trong đối tượng OpenLink để đảm bảo tiện ích bổ sung tải lại sau khi nhận được tiện ích.
  • Tất cả đường liên kết mở từ lời nhắc uỷ quyền phải dùng HTTPS.

Bạn hướng dẫn quy trình uỷ quyền để sử dụng thẻ bằng cách gọi hàm setCustomUiCallback() trên đối tượng AuthorizationException.

Ví dụ sau đây trình bày hàm gọi lại của lệnh uỷ quyền tuỳ chỉnh:

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

Quản lý thông tin đăng nhập bên thứ ba trên Google Workspace ứng dụng

Một ứng dụng phổ biến của Google Workspace tiện ích bổ sung là cung cấp giao diện tương tác với hệ thống của bên thứ ba từ Google Workspaceứng dụng lưu trữ. Thư viện OAuth2 cho Apps Script có thể giúp bạn tạo và quản lý các kết nối của dịch vụ bên thứ ba.

Hệ thống của bên thứ ba thường yêu cầu người dùng đăng nhập bằng mã nhận dạng người dùng, mật khẩu hoặc thông tin xác thực khác. Khi người dùng đăng nhập vào dịch vụ bên thứ ba của bạn trong khi họ đang sử dụng một Google Workspace máy chủ, bạn phải đảm bảo rằng họ không phải đăng nhập lại khi chuyển sang máy chủGoogle Workspace khác. Để ngăn việc yêu cầu đăng nhập lặp lại, hãy sử dụng thuộc tính người dùng hoặc mã thông báo mã nhận dạng. Các thành phần này được giải thích trong các phần sau.

Thuộc tính người dùng

Bạn có thể lưu trữ dữ liệu đăng nhập của người dùng trong các thuộc tính người dùng của Apps Script. Ví dụ: bạn có thể tạo JWT của riêng mình từ dịch vụ đăng nhập của họ và ghi lại dữ liệu đó trong thuộc tính người dùng, hoặc ghi lại tên người dùng và mật khẩu cho dịch vụ của họ.

Thuộc tính người dùng trong phạm vi mà chỉ người dùng đó có thể truy cập được trong tập lệnh tiện ích bổ sung của bạn. Những người dùng khác và các tập lệnh khác không thể truy cập vào các thuộc tính này. Hãy xem PropertiesService để biết thêm thông tin chi tiết.

Mã thông báo mã nhận dạng

Bạn có thể sử dụng mã thông báo Google ID làm thông tin đăng nhập cho dịch vụ của bạn. Đây là cách để đăng nhập một lần. Người dùng đã đăng nhập vào Google do họ đang sử dụng một ứng dụng lưu trữ của Google.