Google Workspace-Add-on mit einem Drittanbieterdienst verknüpfen

Eine benutzerdefinierte Autorisierungskarte aus einer Linkvorschau mit dem Logo des Unternehmens, einer Beschreibung und einer Anmeldeschaltfläche.

Wenn Ihr Google Workspace-Add-on eine Verbindung zu einem Drittanbieterdienst oder einer Drittanbieter-API herstellt, für die eine Autorisierung erforderlich ist, werden Nutzer möglicherweise aufgefordert, sich anzumelden und den Zugriff zu autorisieren.

Auf dieser Seite wird beschrieben, wie Sie Nutzer mithilfe eines Autorisierungsablaufs (z. B. OAuth) authentifizieren. Dazu sind die folgenden Schritte erforderlich:

  1. Erkennen, wann eine Autorisierung erforderlich ist.
  2. Eine Kartenoberfläche zurückgeben, auf der Nutzer aufgefordert werden, sich beim Dienst anzumelden.
  3. Aktualisieren Sie das Add-on, damit Nutzer auf den Dienst oder die geschützte Ressource zugreifen können.

Wenn für Ihr Add-on nur die Nutzeridentität erforderlich ist, können Sie Nutzer direkt mit ihrer Google Workspace-ID oder E-Mail-Adresse authentifizieren. Weitere Informationen zur Verwendung der E-Mail-Adresse für die Authentifizierung finden Sie unter JSON-Anfragen validieren. Wenn Sie Ihr Add-on mit Google Apps Script erstellt haben, können Sie diesen Vorgang mithilfe der OAuth2-Bibliothek für Google Apps Script vereinfachen. Es gibt auch eine OAuth1-Version.

Prüfen, ob eine Autorisierung erforderlich ist

Wenn Nutzer Ihr Add-on verwenden, sind sie aus verschiedenen Gründen möglicherweise nicht berechtigt, auf eine geschützte Ressource zuzugreifen. Das kann z. B. folgende Gründe haben:

  • Ein Zugriffstoken für die Verbindung zum Drittanbieterdienst wurde noch nicht generiert oder ist abgelaufen.
  • Das Zugriffstoken deckt die angeforderte Ressource nicht ab.
  • Das Zugriffstoken deckt nicht die erforderlichen Bereiche der Anfrage ab.

Ihr Add-on sollte diese Fälle erkennen, damit sich Nutzer anmelden und auf Ihren Dienst zugreifen können.

Wenn Sie Apps Script verwenden, können Sie mit der Funktion hasAccess() der OAuth-Bibliothek prüfen, ob Sie Zugriff auf einen Dienst haben. Alternativ können Sie bei UrlFetchApp fetch()-Anfragen den Parameter muteHttpExceptions auf true festlegen. Dadurch wird verhindert, dass bei einem Fehler eine Ausnahme ausgelöst wird, und du kannst den Antwortcode und den Inhalt der Anfrage im zurückgegebenen HttpResponse-Objekt prüfen.

Nutzer zum Anmelden in Ihrem Dienst auffordern

Wenn Ihr Add-on erkennt, dass eine Autorisierung erforderlich ist, muss es eine Kartenoberfläche zurückgeben, über die Nutzer aufgefordert werden, sich beim Dienst anzumelden. Die Anmeldekarte muss Nutzer zur Authentifizierung und Autorisierung durch den Drittanbieter in Ihrer Infrastruktur weiterleiten.

Wenn Sie Ihr Add-on mit HTTP-Endpunkten erstellen, empfehlen wir Ihnen, die Ziel-App mit Google Log-in zu schützen und die Nutzer-ID mithilfe des Identitätstokens abzurufen, das bei der Anmeldung ausgestellt wurde. Die untergeordnete Anspruchsklasse enthält die eindeutige ID des Nutzers und kann mit der ID aus Ihrem Add-on abgeglichen werden.

Anmeldekarte erstellen und zurückgeben

Für die Anmeldekarte Ihres Dienstes können Sie die Karte für die grundlegende Autorisierung von Google verwenden oder eine Karte anpassen, um zusätzliche Informationen wie das Logo Ihrer Organisation anzuzeigen. Wenn Sie Ihr Add-on öffentlich veröffentlichen, müssen Sie eine benutzerdefinierte Karte verwenden.

Karte für die grundlegende Autorisierung

Das folgende Bild zeigt ein Beispiel für die grundlegende Autorisierungskarte von Google:

Aufforderung zur grundlegenden Autorisierung für Beispielkonto Darin wird angegeben, dass das Add-on zusätzliche Informationen anzeigen möchte, aber die Zustimmung des Nutzers zum Zugriff auf das Konto benötigt.

Der folgende Code zeigt ein Beispiel für die Verwendung der einfachen Autorisierungskarte von Google:

Apps Script

CardService.newAuthorizationException()
    .setAuthorizationUrl('AUTHORIZATION_URL')
    .setResourceDisplayName('RESOURCE_DISPLAY_NAME')
    .throwException();

JSON

Gib die folgende JSON-Antwort zurück:

{
  "basic_authorization_prompt": {
    "authorization_url": "AUTHORIZATION_URL",
    "resource": "RESOURCE_DISPLAY_NAME"
  }
}

Ersetzen Sie Folgendes:

  • AUTHORIZATION_URL: Die URL der Webanwendung, die die Autorisierung verarbeitet.
  • RESOURCE_DISPLAY_NAME: Der Anzeigename der geschützten Ressource oder des geschützten Dienstes. Dieser Name wird dem Nutzer in der Autorisierungsanfrage angezeigt. Wenn Ihr RESOURCE_DISPLAY_NAME beispielsweise Example Account lautet, wird die Aufforderung „Über dieses Add-on können zusätzliche Informationen angezeigt werden. Allerdings ist dafür die Genehmigung zum Zugriff auf Ihr Beispielkonto erforderlich.“ angezeigt.

Nach Abschluss der Autorisierung wird der Nutzer aufgefordert, das Add-on zu aktualisieren, um auf die geschützte Ressource zuzugreifen.

Benutzerdefinierte Autorisierungskarte

Wenn Sie die Autorisierungsanfrage ändern möchten, können Sie eine benutzerdefinierte Karte für die Anmeldung in Ihrem Dienst erstellen.

Wenn Sie Ihr Add-on öffentlich veröffentlichen, müssen Sie eine benutzerdefinierte Autorisierungskarte verwenden. Weitere Informationen zu den Veröffentlichungsanforderungen für den Google Workspace Marketplace finden Sie unter App-Überprüfung.

Die zurückgegebene Karte muss Folgendes erfüllen:

  • Informieren Sie den Nutzer darüber, dass das Add-on um Erlaubnis bittet, in seinem Namen auf einen Drittanbieterdienst zuzugreifen.
  • Machen Sie deutlich, was das Add-on bei Autorisierung tun kann.
  • Sie enthalten eine Schaltfläche oder ein ähnliches Widget, über das Nutzer zur Autorisierungs-URL des Dienstes weitergeleitet werden. Die Funktion dieses Widgets muss für den Nutzer klar erkennbar sein.
  • Für das obige Widget muss die Einstellung OnClose.RELOAD im OpenLink-Objekt verwendet werden, damit das Add-on nach Erhalt der Autorisierung neu geladen wird.
  • Alle über die Autorisierungsanfrage geöffneten Links müssen HTTPS verwenden.

Das folgende Bild zeigt ein Beispiel für eine benutzerdefinierte Autorisierungskarte für die Startseite eines Add-ons. Die Karte enthält ein Logo, eine Beschreibung und eine Anmeldeschaltfläche:

Eine benutzerdefinierte Autorisierungskarte für Cymbal Labs mit dem Logo des Unternehmens, einer Beschreibung und einer Anmeldeschaltfläche.

Im folgenden Code wird gezeigt, wie dieses Beispiel für eine benutzerdefinierte Karte verwendet wird:

Apps Script

function customAuthorizationCard() {
    let cardSection1Image1 = CardService.newImage()
        .setImageUrl('LOGO_URL')
        .setAltText('LOGO_ALT_TEXT');

    let cardSection1Divider1 = CardService.newDivider();

    let cardSection1TextParagraph1 = CardService.newTextParagraph()
        .setText('DESCRIPTION');

    let cardSection1ButtonList1Button1 = CardService.newTextButton()
        .setText('Sign in')
        .setBackgroundColor('#0055ff')
        .setTextButtonStyle(CardService.TextButtonStyle.FILLED)
        .setAuthorizationAction(CardService.newAuthorizationAction()
            .setAuthorizationUrl('AUTHORIZATION_URL'));

    let cardSection1ButtonList1 = CardService.newButtonSet()
        .addButton(cardSection1ButtonList1Button1);

    let cardSection1TextParagraph2 = CardService.newTextParagraph()
        .setText('TEXT_SIGN_UP');

    let cardSection1 = CardService.newCardSection()
        .addWidget(cardSection1Image1)
        .addWidget(cardSection1Divider1)
        .addWidget(cardSection1TextParagraph1)
        .addWidget(cardSection1ButtonList1)
        .addWidget(cardSection1TextParagraph2);

    let card = CardService.newCardBuilder()
        .addSection(cardSection1)
        .build();
    return [card];
}

function startNonGoogleAuth() {
    CardService.newAuthorizationException()
        .setAuthorizationUrl('AUTHORIZATION_URL')
        .setResourceDisplayName('RESOURCE_DISPLAY_NAME')
        .setCustomUiCallback('customAuthorizationCard')
        .throwException();
  }

JSON

Gib die folgende JSON-Antwort zurück:

{
  "custom_authorization_prompt": {
    "action": {
      "navigations": [
        {
          "pushCard": {
            "sections": [
              {
                "widgets": [
                  {
                    "image": {
                      "imageUrl": "LOGO_URL",
                      "altText": "LOGO_ALT_TEXT"
                    }
                  },
                  {
                    "divider": {}
                  },
                  {
                    "textParagraph": {
                      "text": "DESCRIPTION"
                    }
                  },
                  {
                    "buttonList": {
                      "buttons": [
                        {
                          "text": "Sign in",
                          "onClick": {
                            "openLink": {
                              "url": "AUTHORIZATION_URL",
                              "onClose": "RELOAD",
                              "openAs": "OVERLAY"
                            }
                          },
                          "color": {
                            "red": 0,
                            "green": 0,
                            "blue": 1,
                            "alpha": 1,
                          }
                        }
                      ]
                    }
                  },
                  {
                    "textParagraph": {
                      "text": "TEXT_SIGN_UP"
                    }
                  }
                ]
              }
            ]
          }
        }
      ]
    }
  }
}

Ersetzen Sie Folgendes:

  • LOGO_URL: Die URL für ein Logo oder Bild. Muss eine öffentliche URL sein.
  • LOGO_ALT_TEXT: Alt-Text für das Logo oder Bild, z. B. Cymbal Labs Logo.
  • DESCRIPTION: Ein Call-to-Action, der Nutzer zum Anmelden auffordert, z. B. Sign in to get started.
  • So aktualisieren Sie die Anmeldeschaltfläche:
    • AUTHORIZATION_URL: Die URL der Webanwendung, die die Autorisierung verarbeitet.
    • Optional: Wenn Sie die Farbe der Schaltfläche ändern möchten, aktualisieren Sie die RGBA-Floatwerte des Felds color. Aktualisieren Sie in Apps Script die Methode setBackgroundColor() mit Hexadezimalwerten.
  • TEXT_SIGN_UP: Ein Text, der Nutzer auffordert, ein Konto zu erstellen, falls sie noch keines haben. Beispiel: New to Cymbal Labs? <a href=\"https://www.example.com/signup\">Sign up</a> here.

Anmeldungen von Drittanbietern in Google Workspace-Apps verwalten

Eine gängige Anwendung für Google Workspace-Add-ons besteht darin, eine Benutzeroberfläche für die Interaktion mit einem Drittanbietersystem innerhalb einer Google Workspace-Hostanwendung bereitzustellen.

Bei Drittanbietersystemen müssen sich Nutzer häufig mit einer Nutzer-ID, einem Passwort oder anderen Anmeldedaten anmelden. Wenn sich ein Nutzer in Ihrem Drittanbieterdienst anmeldet, während er einen Google Workspace-Host verwendet, muss er sich nicht noch einmal anmelden, wenn er zu einem anderen Google Workspace-Host wechselt.

Wenn Sie Apps Script einbinden, können Sie wiederholte Anmeldeanfragen mit Nutzereigenschaften oder ID-Tokens verhindern. Diese werden in den folgenden Abschnitten erläutert.

Nutzereigenschaften

Sie können die Anmeldedaten eines Nutzers in den Nutzereigenschaften von Apps Script speichern. Sie können beispielsweise ein eigenes JSON Web Token (JWT) über den Anmeldedienst erstellen und in einer Nutzereigenschaft speichern oder den Nutzernamen und das Passwort für den Dienst speichern.

Nutzereigenschaften sind so festgelegt, dass nur der betreffende Nutzer im Script Ihres Add-ons darauf zugreifen kann. Andere Nutzer und andere Scripts können nicht auf diese Properties zugreifen. Unter PropertiesService finden Sie weitere Informationen.

ID-Tokens

Sie können ein Google-ID-Token als Anmeldedaten für Ihren Dienst verwenden. So lässt sich die Einmalanmeldung realisieren. Nutzer sind bereits in Google angemeldet, da sie sich in einer Google-Host-App befinden.

Beispiel für eine OAuth-Konfiguration, die nicht von Google stammt

Im folgenden Apps Script-Codebeispiel wird gezeigt, wie ein Add-on für die Verwendung einer Drittanbieter-API konfiguriert wird, für die OAuth erforderlich ist. In diesem Beispiel wird die OAuth2 for Apps Script-Bibliothek verwendet, um einen Dienst für den Zugriff auf die API zu erstellen.

Apps Script

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