Collegare il componente aggiuntivo di Google Workspace a un servizio di terze parti

Una scheda di autorizzazione personalizzata da un'anteprima del link che include il logo dell'azienda, una descrizione e un pulsante di accesso.
Un'interfaccia di scheda di accesso per un componente aggiuntivo che visualizza l'anteprima dei link di un servizio di terze parti.

Se il componente aggiuntivo Google Workspace si connette a un servizio o a un'API di terze parti che richiede l'autorizzazione, il componente aggiuntivo può chiedere agli utenti di accedere e autorizzare l'accesso.

Questa pagina spiega come autenticare gli utenti utilizzando un flusso di autorizzazione (ad esempio OAuth), che include i seguenti passaggi:

  1. Rileva quando è necessaria l'autorizzazione.
  2. Restituisce un'interfaccia della scheda che chiede agli utenti di accedere al servizio.
  3. Aggiorna il componente aggiuntivo in modo che gli utenti possano accedere al servizio o alla risorsa protetta.

Se il tuo componente aggiuntivo richiede solo l'identità dell'utente, puoi autenticare direttamente gli utenti utilizzando il loro ID o indirizzo email Google Workspace. Per utilizzare l'indirizzo email per l'autenticazione, vedi Convalida delle richieste JSON. Se hai creato il componente aggiuntivo utilizzando Google Apps Script, puoi semplificare questo processo utilizzando la libreria OAuth2 per Google Apps Script (esiste anche una versione OAuth1).

Rilevare che è necessaria l'autorizzazione

Quando utilizzano il tuo componente aggiuntivo, gli utenti potrebbero non essere autorizzati ad accedere a una risorsa protetta per vari motivi, ad esempio i seguenti:

  • Non è ancora stato generato un token di accesso per connettersi al servizio di terze parti o è scaduto.
  • Il token di accesso non copre la risorsa richiesta.
  • Il token di accesso non copre gli ambiti richiesti della richiesta.

Il componente aggiuntivo deve rilevare questi casi in modo che gli utenti possano accedere al tuo servizio.

Se crei in Apps Script, la funzione hasAccess() della libreria OAuth può indicarti se hai 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 genera un'eccezione in caso di errore e ti consente di esaminare il codice di risposta e i contenuti della richiesta nell'oggetto HttpResponse restituito.

Chiedere agli utenti di accedere al tuo servizio

Quando il componente aggiuntivo rileva che è necessaria l'autorizzazione, deve restituire un'interfaccia scheda per chiedere agli utenti di accedere al servizio. La scheda di accesso deve reindirizzare gli utenti per completare la procedura di autenticazione e autorizzazione di terze parti sulla tua infrastruttura.

Quando crei il componente aggiuntivo utilizzando gli endpoint HTTP, ti consigliamo di proteggere l'app di destinazione con Accedi con Google e di ottenere l'ID utente utilizzando il token ID emesso durante l'accesso. La rivendicazione contiene l'ID univoco dell'utente e può essere correlata all'ID del tuo componente aggiuntivo.

Creare e restituire una scheda di accesso

Per la scheda di accesso del tuo servizio, puoi utilizzare la scheda di autorizzazione di base di Google oppure puoi personalizzare una scheda per visualizzare informazioni aggiuntive, come il logo della tua organizzazione. Se pubblichi il tuo componente aggiuntivo pubblicamente, devi utilizzare una scheda personalizzata.

Carta di autorizzazione di base

La seguente immagine mostra un esempio della scheda di autorizzazione di base di Google:

Prompt di autorizzazione di base per l'account di esempio. Il
    prompt indica che il componente aggiuntivo vorrebbe mostrare
    ulteriori informazioni, ma necessita dell'approvazione dell'utente per
    accedere all'account.

Per richiedere agli utenti una scheda di autorizzazione di base, devi restituire l'oggetto AuthorizationError. Il seguente codice mostra un esempio di oggetto AuthorizationError:

Apps Script

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

JSON

Restituisci la seguente risposta JSON:

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

Sostituisci quanto segue:

  • AUTHORIZATION_URL: l'URL dell'app web che gestisce l'autorizzazione.
  • RESOURCE_DISPLAY_NAME: il nome visualizzato per la risorsa o il servizio protetto. Questo nome viene visualizzato all'utente nel prompt di autorizzazione. Ad esempio, se il tuo RESOURCE_DISPLAY_NAME è Example Account, il prompt indica "Questo componente aggiuntivo vorrebbe mostrare ulteriori informazioni, ma necessita di approvazione per accedere al tuo Account esempio".

Una volta completata l'autorizzazione, all'utente viene chiesto di aggiornare il componente aggiuntivo per accedere alla risorsa protetta.

Restituire le schede di autorizzazione in Google Chat

Se il componente aggiuntivo estende Google Chat e l'utente lo esegue all'interno di Google Chat, può completare la procedura di autorizzazione senza un aggiornamento manuale. Google Chat supporta il nuovo tentativo automatico dell'esecuzione precedente se il trigger è Messaggio, Aggiunta allo spazio o Comando app. Per questi trigger, il componente aggiuntivo riceve completeRedirectUri nel payload dell'evento. Devi codificare completeRedirectUri nell'URL di configurazione per attivare il nuovo tentativo automatico. Il reindirizzamento a questo URL indica a Google Chat che la richiesta di configurazione è stata soddisfatta e consente a Google Chat di riprovare l'esecuzione precedente.

Quando un utente viene reindirizzato correttamente al configCompleteRedirectUrl fornito nel messaggio originale, Google Chat esegue i seguenti passaggi:

  1. Cancella il prompt visualizzato dall'utente che ha avviato l'azione.
  2. Invia l'oggetto evento originale allo stesso componente aggiuntivo una seconda volta.

Se non codifichi completeRedirectUri nell'URL di configurazione, l'utente può comunque completare il flusso di autorizzazione. Tuttavia, Google Chat non riprova l'esecuzione precedente e l'utente deve richiamare manualmente il componente aggiuntivo.

Il seguente esempio di codice mostra come un'app Chat può richiedere credenziali OAuth2 offline, memorizzarle in un database e utilizzarle per effettuare chiamate API con autenticazione utente.

Scheda di autorizzazione personalizzata

Per modificare la richiesta di autorizzazione, puoi creare una scheda personalizzata per l'esperienza di accesso del tuo servizio.

Se pubblichi il tuo componente aggiuntivo pubblicamente, devi utilizzare una scheda di autorizzazione personalizzata per tutte le applicazioni host Google Workspace, ad eccezione di Chat. Per scoprire di più sui requisiti di pubblicazione per Google Workspace Marketplace, vedi Informazioni sulla revisione delle app.

La carta restituita deve:

  • Indica chiaramente all'utente che il componente aggiuntivo chiede l'autorizzazione ad accedere a un servizio non Google per suo conto.
  • Indica chiaramente cosa può fare il componente aggiuntivo se autorizzato.
  • Contenere un pulsante o un widget simile che indirizzi l'utente all'URL di autorizzazione del servizio. Assicurati che la funzione di questo widget sia ovvia per l'utente.
  • Il widget precedente deve utilizzare l'impostazione OnClose.RELOAD nel relativo oggetto OpenLink per garantire che il componente aggiuntivo venga ricaricato dopo aver ricevuto l'autorizzazione.
  • Tutti i link aperti dal prompt di autorizzazione devono utilizzare HTTPS.

L'immagine seguente mostra un esempio di scheda di autorizzazione personalizzata per la home page di un componente aggiuntivo. La scheda include un logo, una descrizione e un pulsante di accesso:

Una scheda di autorizzazione personalizzata per Cymbal Labs che include il logo dell'azienda, una descrizione e un pulsante di accesso.

Il seguente codice mostra come utilizzare questo esempio di scheda personalizzata:

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

Restituisci la seguente risposta JSON:

{
  "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"
                    }
                  }
                ]
              }
            ]
          }
        }
      ]
    }
  }
}

Sostituisci quanto segue:

  • LOGO_URL: l'URL di un logo o di un'immagine. Deve essere un URL pubblico.
  • LOGO_ALT_TEXT: testo alternativo per il logo o l'immagine, ad esempio Cymbal Labs Logo.
  • DESCRIPTION: un invito all'azione per gli utenti per accedere, ad esempio Sign in to get started.
  • Per aggiornare il pulsante di accesso:
    • AUTHORIZATION_URL: l'URL dell'app web che gestisce l'autorizzazione.
    • (Facoltativo) Per modificare il colore del pulsante, aggiorna i valori float RGBA del campo color. Per Apps Script, aggiorna il metodo setBackgroundColor() utilizzando valori esadecimali.
  • TEXT_SIGN_UP: un testo che invita gli utenti a creare un account se non ne hanno uno. Ad esempio, New to Cymbal Labs? <a href=\"https://www.example.com/signup\">Sign up</a> here.

Gestire gli accessi di terze parti nelle app Google Workspace

Un'applicazione comune per i componenti aggiuntivi di Google Workspace è fornire un'interfaccia per interagire con un sistema di terze parti dall'interno di un'applicazione host Google Workspace.

I sistemi di terze parti spesso richiedono l'accesso dell'utente tramite un ID utente, una password o altre credenziali. Quando un utente accede al tuo servizio di terze parti mentre utilizza un host Google Workspace, devi assicurarti che non debba accedere di nuovo quando passa a un altro host Google Workspace.

Se crei in Apps Script, puoi impedire richieste di accesso ripetute con proprietà utente o token ID. Questi sono spiegati nelle sezioni seguenti.

Proprietà utente

Puoi memorizzare i dati di accesso di un utente nelle proprietà utente di Apps Script. Ad esempio, potresti creare un tuo JSON Web Token (JWT) dal servizio di accesso e registrarlo in una proprietà utente oppure registrare il nome utente e la password per il servizio.

Le proprietà utente sono delimitate in modo che siano accessibili solo all'utente all'interno dello script del componente aggiuntivo. Altri utenti e altri script non possono accedere a queste proprietà. Per maggiori dettagli, consulta PropertiesService.

Token ID

Puoi utilizzare un token ID Google come credenziale di accesso per il tuo 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 Google.

Esempio di configurazione OAuth non Google

Il seguente esempio di codice Apps Script mostra come configurare un componente aggiuntivo per utilizzare un'API non Google che richiede OAuth. Questo esempio utilizza la libreria OAuth2 per Apps Script per creare un servizio per l'accesso all'API.

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.
*/
function authCallback(callbackRequest) {
  var authorized = getOAuthService().handleCallback(callbackRequest);
  if (authorized) {
    return HtmlService.createHtmlOutput(
      'Success!');
  } 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();
}