Connessione a servizi non Google da un componente aggiuntivo di Google Workspace

Il tuo progetto di componente aggiuntivo di Google Workspace può connettersi direttamente a molti prodotti Google con i servizi integrati e avanzati di Apps Script.

Puoi anche accedere ad API e servizi non Google. Se il servizio non richiede l'autorizzazione, in genere puoi semplicemente effettuare una richiesta UrlFetch appropriata e poi chiedere al componente aggiuntivo di interpretare la risposta.

Tuttavia, se il servizio non Google richiede l'autorizzazione, devi configurare OAuth per quel servizio. Puoi semplificare questo processo utilizzando la libreria OAuth2 per Apps Script (è disponibile anche una versione OAuth1).

Utilizzo di un servizio OAuth

Quando utilizzi un oggetto di servizio OAuth per connetterti a un servizio non Google, il componente aggiuntivo Google Workspace deve rilevare quando è richiesta l'autorizzazione e, quando è necessaria, richiamare il flusso di autorizzazione.

Il flusso di autorizzazione è costituito da:

  1. Avvisare l'utente che è necessaria l'autenticazione e fornendo un link per avviare il processo.
  2. Acquisizione dell'autorizzazione dal servizio non Google.
  3. Aggiornamento del componente aggiuntivo per riprovare ad accedere alla risorsa protetta.

Quando è necessaria un'autorizzazione diversa da Google, l'infrastruttura del componente aggiuntivo di Google Workspace gestisce questi dettagli. Il componente aggiuntivo deve rilevare solo quando è necessaria l'autorizzazione e richiamare il flusso di autorizzazione quando necessario.

Rilevamento dell'autorizzazione richiesta

Una richiesta potrebbe non avere l'autorizzazione ad accedere a una risorsa protetta per una serie di motivi, ad esempio:

  • Il token di accesso non è stato ancora generato o è scaduto.
  • Il token di accesso non copre la risorsa richiesta.
  • Il token di accesso non copre gli ambiti richiesti della richiesta.

Il codice del componente aggiuntivo dovrebbe rilevare questi casi. La funzione hasAccess() della libreria OAuth può indicare se hai attualmente accesso a un servizio. In alternativa, quando utilizzi le richieste UrlFetchApp fetch(), puoi impostare il parametro muteHttpExceptions su true. In questo modo, la richiesta non può generare un'eccezione in caso di errore della richiesta e puoi esaminare il codice di risposta alla richiesta e i contenuti nell'oggetto HttpResponse restituito.

Quando il componente aggiuntivo rileva che è richiesta l'autorizzazione, dovrebbe attivare il flusso di autorizzazione.

Chiamata del flusso di autorizzazione

Puoi richiamare il flusso di autorizzazione utilizzando il servizio carta per creare un oggetto AuthorizationException, impostandone le proprietà, quindi richiamando la funzione throwException(). Prima di lanciare l'eccezione, fornisci quanto segue:

  1. Obbligatorio. Un URL di autorizzazione. Specificato dal servizio non Google ed è la posizione a cui viene reindirizzato l'utente quando viene avviato il flusso di autorizzazione. Puoi impostare questo URL utilizzando la funzione setAuthorizationUrl().
  2. Obbligatorio. Una stringa del nome visualizzato della risorsa. Identifica la risorsa per l'utente quando viene richiesta l'autorizzazione. Puoi impostare questo nome utilizzando la funzione setResourceDisplayName().
  3. Il nome di una funzione di callback che crea una richiesta di autorizzazione personalizzata. Questo callback restituisce un array di oggetti Card creati che compongono una UI per la gestione dell'autorizzazione. Questa operazione è facoltativa; se non impostata, viene utilizzata la carta di autorizzazione predefinita. Puoi impostare la funzione di callback utilizzando la funzione setCustomUiCallback().

Esempio di configurazione OAuth non Google

Questo esempio di codice mostra come configurare un componente aggiuntivo per utilizzare un'API non Google che richiede OAuth. Utilizza il protocollo OAuth2 per Apps Script per costruire un servizio per l'accesso all'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();
}

Creazione di una richiesta di autorizzazione personalizzata

carta di autorizzazione dei servizi non Google

Per impostazione predefinita, un prompt di autorizzazione non ha alcun branding e utilizza solo la stringa del nome visualizzato per indicare la risorsa a cui il componente aggiuntivo sta tentando di accedere. Il componente aggiuntivo può, tuttavia, definire una scheda di autorizzazione personalizzata che ha lo stesso scopo e può includere informazioni aggiuntive e branding.

Puoi definire un prompt personalizzato implementando una funzione di callback dell'interfaccia utente personalizzata che restituisce un array di oggetti Card creati. Questo array deve contenere una sola scheda. Se ne vengono forniti altri, le intestazioni vengono visualizzate sotto forma di elenco, il che può risultare confuso per l'esperienza utente.

La carta restituita deve:

  • Comunica chiaramente all'utente che il componente aggiuntivo richiede l'autorizzazione per accedere a un servizio non Google per suo conto.
  • Indica chiaramente cosa può fare il componente aggiuntivo se autorizzato.
  • Contengono un pulsante o un widget simile che indirizza l'utente all'URL di autorizzazione del servizio. Assicurati che la funzione di questo widget sia evidente per l'utente.
  • Il widget riportato sopra deve utilizzare l'impostazione OnClose.RELOAD_ADD_ON nel relativo oggetto OpenLink per garantire che il componente aggiuntivo venga ricaricato dopo la ricezione dell'autorizzazione.
  • Tutti i link aperti dalla richiesta di autorizzazione devono utilizzare HTTPS.

Puoi indirizzare il flusso di autorizzazione all'utilizzo della carta chiamando la funzione setCustomUiCallback() sul tuo oggetto AuthorizationException.

L'esempio seguente mostra una funzione di callback della richiesta di autorizzazione personalizzata:

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

Gestione degli accessi di terze parti nelle app Google Workspace

Un'applicazione comune per i componenti aggiuntivi di Google Workspace consiste nel fornire un'interfaccia per interagire con un sistema di terze parti all'interno di un'applicazione host di Google Workspace. La libreria OAuth2 per Apps Script può aiutarti a creare e gestire le connessioni a servizi di terze parti.

I sistemi di terze parti spesso richiedono che l'utente acceda utilizzando un ID utente, una password o altre credenziali. Quando un utente accede a un servizio di terze parti mentre utilizza un host di Google Workspace, devi assicurarti che non debba accedere di nuovo quando passa a un altro host di Google Workspace. Per evitare richieste di accesso ripetute, utilizza le proprietà utente o i token ID. Questi passaggi sono spiegati nelle sezioni seguenti.

Proprietà utente

Puoi archiviare i dati di accesso di un utente nelle proprietà utente di Apps Script. Ad esempio, puoi creare il tuo JWT dal servizio di accesso e registrarlo in una proprietà utente oppure registrare il nome utente e la password del servizio.

Le proprietà utente hanno un ambito in modo che siano accessibili solo a quell'utente all'interno dello script del componente aggiuntivo. Gli altri utenti e altri script non possono accedere a queste proprietà. Per ulteriori dettagli, visita PropertiesService.

Token ID

Puoi utilizzare un token ID Google come credenziale di accesso al servizio. Questo è un modo per ottenere il Single Sign-On. Gli utenti hanno già eseguito l'accesso a Google perché si trovano in un'app host di Google.