Łączenie się z usługami innymi niż Google za pomocą dodatku Google Workspace

Twój projekt dotyczący dodatku Google Workspace może łączyć się bezpośrednio z wieloma usługami Google za pomocą wbudowanych i zaawansowanych usług Apps Script.

Masz też dostęp do interfejsów API i usług innych firm. Jeśli usługa nie wymaga autoryzacji, wystarczy, że wyślesz odpowiednie żądanie UrlFetch, a Twój dodatek zinterpretuje odpowiedź.

Jeśli jednak usługa inna niż Google wymaga autoryzacji, musisz dla niej skonfigurować protokół OAuth. Możesz ułatwić sobie ten proces, korzystając z biblioteki OAuth2 for Apps Script (istnieje wersja OAuth1).

Korzystanie z usługi OAuth

Gdy do łączenia się z usługami spoza Google używasz obiektu usługi OAuth, Twój dodatek do Google Workspace musi wykrywać, kiedy wymagana jest autoryzacja, a kiedy tak jest – wywoływać proces autoryzacji.

Proces autoryzacji obejmuje:

  1. Powiadamianie użytkownika o konieczności uwierzytelnienia i udostępnienie linku umożliwiającego rozpoczęcie procesu.
  2. Uzyskiwanie autoryzacji z usługi innej niż Google.
  3. Odświeżam dodatek, aby spróbować ponownie uzyskać dostęp do chronionego zasobu.

Gdy potrzebna jest autoryzacja spoza Google, te informacje są zawarte w infrastrukturze dodatku Google Workspace. Dodatek musi wykrywać, kiedy autoryzacja jest potrzebna, i wywoływać przepływ autoryzacji, gdy jest to konieczne.

Wykrywanie, że autoryzacja jest wymagana

Żądanie może nie mieć uprawnień dostępu do chronionego zasobu z różnych powodów, takich jak:

  • Token dostępu nie został jeszcze wygenerowany lub wygasł.
  • Token dostępu nie obejmuje żądanego zasobu.
  • Token dostępu nie obejmuje wymaganych zakresów żądania.

Kod powinien wykryć te przypadki. Funkcja hasAccess() protokołu OAuth w bibliotece informuje, czy masz obecnie dostęp do usługi. Jeśli używasz żądań UrlFetchApp fetch(), możesz też ustawić parametr muteHttpExceptions na true. Zapobiega to zgłaszaniu przez wyjątek wyjątku w przypadku niepowodzenia żądania oraz sprawdzaniu kodu odpowiedzi i treści w zwróconym obiekcie HttpResponse.

Gdy dodatek wykryje autoryzację, powinien aktywować przepływ autoryzacji.

Wywoływanie procesu autoryzacji

Proces autoryzacji wywołujesz za pomocą usługi Card w celu utworzenia obiektu AuthorizationException, określenia jego właściwości, a następnie wywołania funkcji throwException(). Przed zgłoszeniem wyjątku podaj te informacje:

  1. Wymagany. Adres URL autoryzacji. Określa ona usługę inną niż Google i jest to lokalizacja, do której przechodzi użytkownik po rozpoczęciu procesu autoryzacji. Ustawiasz go za pomocą funkcji setAuthorizationUrl().
  2. Wymagany. Ciąg wyświetlanej nazwy zasobu. Identyfikuje zasób, gdy użytkownik poprosi o autoryzację. Tę nazwę ustawiasz za pomocą funkcji setResourceDisplayName().
  3. Nazwa funkcji wywołania zwrotnego, która tworzy niestandardową prośbę o autoryzację. To wywołanie zwrotne zwraca tablicę wbudowanych obiektów Card, które tworzą interfejs do obsługi autoryzacji. Ta funkcja jest opcjonalna, jeśli nie zostanie użyta, zostanie użyta domyślna karta autoryzacji. Funkcję wywołania zwrotnego ustawiasz za pomocą funkcji setCustomUiCallback().

Przykład konfiguracji protokołu OAuth spoza Google

Ten przykładowy kod pokazuje, jak skonfigurować dodatek pod kątem korzystania z interfejsu API innego niż Google, który wymaga protokołu OAuth. Wykorzystuje protokół OAuth2 dla Apps Script do tworzenia usługi, która pozwala uzyskać dostęp do interfejsu 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();
}

Tworzenie niestandardowego komunikatu autoryzacji

karta autoryzacji usługi innej niż Google

Domyślnie powiadomienie o autoryzacji nie zawiera żadnej marki i wskazuje tylko ciąg nazwy, który ma być użyty do uzyskania dostępu do zasobu. Dodatek może jednak zdefiniować niestandardową kartę autoryzacji, która służy do tego samego celu, co może zawierać dodatkowe informacje i elementy marki.

Aby zdefiniować niestandardowy komunikat, zastosuj niestandardową funkcję wywołania zwrotnego, która zwraca tablicę utworzonych obiektów Card. Ta tablica powinna zawierać tylko jedną kartę. Jeśli podasz ich więcej, ich nagłówki będą wyświetlane na liście, co może być mylące dla użytkowników.

Zwrócona karta musi spełniać te warunki:

  • Wyjaśnij użytkownikowi, że dodatek prosi o pozwolenie na dostęp w imieniu użytkownika do usługi nienależącej do Google.
  • Wyjaśnij, do czego dodatek może uzyskać dostęp, jeśli zostanie on autoryzowany.
  • zawierać przycisk lub podobny widżet, który prowadzi użytkownika do adresu URL autoryzacji usługi. Upewnij się, że funkcja widżetu jest dla użytkownika oczywista.
  • Ten widżet musi używać ustawienia OnClose.RELOAD_ADD_ON w obiekcie OpenLink, aby zapewnić ponowne wczytanie dodatku po otrzymaniu autoryzacji.
  • Wszystkie linki otwierane w oknie autoryzacji muszą używać protokołu HTTPS.

Możesz zezwolić na używanie karty, wywołując funkcję setCustomUiCallback() w obiekcie AuthorizationException.

Poniższy przykład pokazuje funkcję wywołania zwrotnego niestandardowego komunikatu autoryzacji:

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

Zarządzanie loginami innych firm w aplikacjach Google Workspace

Jedną z częstych aplikacji dodatków do Google Workspace jest interfejs pozwalający na interakcję z systemem innej firmy za pomocą aplikacji hosta Google Workspace. Biblioteka OAuth2 dla Apps Script ułatwia tworzenie połączeń z usługami innych firm i zarządzanie nimi.

Systemy innych firm często wymagają, aby użytkownik logował się za pomocą identyfikatora użytkownika, hasła lub innych danych logowania. Gdy użytkownik loguje się w usłudze innej firmy podczas korzystania z jednego hosta Google Workspace, musisz upewnić się, że nie musi on logować się ponownie po przejściu na innego dostawcę usług Google Workspace. Aby uniknąć powtarzających się żądań logowania, użyj właściwości użytkownika lub tokenów tożsamości. Zostało to wyjaśnione w kolejnych sekcjach.

Właściwości użytkownika

Dane logowania użytkownika możesz zapisywać we właściwościach użytkownika Apps Script. Możesz na przykład utworzyć własny token JWT z jego usługi logowania i zarejestrować go we właściwości użytkownika albo zarejestrować nazwę użytkownika i hasło do jego usługi.

Właściwości użytkownika są ograniczone do określonego użytkownika w skrypcie dodatku. Inni użytkownicy i inne skrypty nie mają dostępu do tych właściwości. Więcej informacji: PropertiesService.

Tokeny tożsamości

Możesz użyć tokena tożsamości Google jako danych logowania w swojej usłudze. To sposób na osiągnięcie logowania jednokrotnego. Użytkownicy są już zalogowani w Google, ponieważ korzystają z aplikacji hostującej Google.