Se connecter à des services autres que Google depuis un module complémentaire Google Workspace

Votre projet de module complémentaire Google Workspace peut se connecter directement à de nombreux produits Google grâce aux services intégrés et avancés d'Apps Script.

Vous pouvez également accéder aux API et services tiers. Si le service ne nécessite pas d'autorisation, vous pouvez généralement effectuer une requête UrlFetch appropriée, puis demander au module complémentaire d'interpréter la réponse.

Toutefois, si le service autre que Google nécessite une autorisation, vous devez configurer OAuth pour ce service. Vous pouvez faciliter ce processus en utilisant la bibliothèque OAuth2 pour Apps Script (il existe également une version OAuth1).

Utiliser un service OAuth

Lorsque vous utilisez un objet de service OAuth pour vous connecter à un service autre que Google, votre module complémentaire Google Workspace doit détecter quand une autorisation est requise et, le cas échéant, appeler le flux d'autorisation.

Le flux d'autorisation comprend les éléments suivants:

  1. Avertir l'utilisateur que l'authentification est nécessaire et fournir un lien pour lancer le processus
  2. Obtenir l'autorisation auprès du service autre que Google
  3. Actualisation du module complémentaire pour réessayer d'accéder à la ressource protégée.

Lorsqu'une autorisation autre que Google est nécessaire, l'infrastructure du module complémentaire Google Workspace gère ces détails. Votre module complémentaire doit seulement détecter les cas où une autorisation est nécessaire et appeler le flux d'autorisation si nécessaire.

Détecter que l'autorisation est requise

Une requête peut ne pas être autorisée à accéder à une ressource protégée pour diverses raisons, par exemple:

  • Le jeton d'accès n'a pas encore été généré ou a expiré.
  • Le jeton d'accès ne couvre pas la ressource demandée.
  • Le jeton d'accès ne couvre pas les champs d'application requis pour la requête.

Le code de votre module complémentaire devrait détecter ces cas de figure. La fonction hasAccess() de la bibliothèque OAuth peut vous indiquer si vous avez actuellement accès à un service. Lorsque vous utilisez des requêtes UrlFetchApp fetch(), vous pouvez également définir le paramètre muteHttpExceptions sur true. Cela empêche la requête de générer une exception en cas d'échec de la requête, et vous permet d'examiner le code de réponse à la requête et le contenu de l'objet HttpResponse renvoyé.

Lorsque le module complémentaire détecte qu'une autorisation est requise, il doit déclencher le flux d'autorisation.

Appeler le flux d'autorisation

Pour appeler le flux d'autorisation, utilisez le service de cartes pour créer un objet AuthorizationException, définissez ses propriétés, puis appelez la fonction throwException(). Avant de générer l'exception, vous devez fournir les éléments suivants:

  1. Obligatoire. Une URL d'autorisation. Il est spécifié par le service autre que Google et correspond à l'emplacement vers lequel l'utilisateur est redirigé lorsque le flux d'autorisation démarre. Vous définissez cette URL à l'aide de la fonction setAuthorizationUrl().
  2. Obligatoire. Chaîne du nom à afficher de la ressource. Identifie la ressource auprès de l'utilisateur lorsque l'autorisation est demandée. Vous définissez ce nom à l'aide de la fonction setResourceDisplayName().
  3. Nom d'une fonction de rappel qui crée une invite d'autorisation personnalisée. Ce rappel renvoie un tableau d'objets Card créés qui composent une UI pour gérer les autorisations. Ceci est facultatif. Si vous ne la définissez pas, la carte d'autorisation par défaut est utilisée. Vous définissez la fonction de rappel à l'aide de la fonction setCustomUiCallback().

Exemple de configuration OAuth sans Google

Cet exemple de code montre comment configurer un module complémentaire pour utiliser une API non Google nécessitant OAuth. Il utilise le protocole OAuth2 pour Apps Script afin de créer un service permettant d'accéder à l'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();
}

Créer une invite d'autorisation personnalisée

carte d&#39;autorisation pour un service autre que Google

Par défaut, une invite d'autorisation n'est associée à aucun branding et n'utilise que la chaîne du nom à afficher pour indiquer à quelle ressource le module complémentaire tente d'accéder. Votre module complémentaire peut toutefois définir une carte d'autorisation personnalisée qui remplit le même objectif et peut inclure des informations supplémentaires et des éléments de branding.

Pour définir une invite personnalisée, implémentez une fonction de rappel de l'interface utilisateur personnalisée qui renvoie un tableau d'objets Card compilés. Ce tableau ne doit contenir qu'une seule fiche. Si d'autres en sont fournis, leurs en-têtes sont affichés sous forme de liste, ce qui peut perturber l'expérience utilisateur.

La carte renvoyée doit remplir les conditions suivantes:

  • Indiquez clairement à l'utilisateur que le module complémentaire demande l'autorisation d'accéder à un service autre que Google en son nom.
  • Indiquez clairement ce que le module complémentaire peut faire s'il est autorisé.
  • contenir un bouton ou un widget similaire qui redirige l'utilisateur vers l'URL d'autorisation du service ; Assurez-vous que la fonction de ce widget est évidente pour l'utilisateur.
  • Le widget ci-dessus doit utiliser le paramètre OnClose.RELOAD_ADD_ON dans son objet OpenLink pour s'assurer que le module complémentaire s'actualise après la réception de l'autorisation.
  • Tous les liens ouverts à partir de l'invite d'autorisation doivent utiliser HTTPS.

Pour demander au flux d'autorisation d'utiliser votre carte, appelez la fonction setCustomUiCallback() au niveau de l'objet AuthorizationException.

L'exemple suivant illustre une fonction de rappel d'invite d'autorisation personnalisée:

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

Gérer les connexions tierces dans les applications Google Workspace

Une application courante des modules complémentaires Google Workspace consiste à fournir une interface permettant d'interagir avec un système tiers depuis une application hôte Google Workspace. La bibliothèque OAuth2 pour Apps Script peut vous aider à créer et à gérer les connexions à des services tiers.

Les systèmes tiers exigent souvent que l'utilisateur se connecte à l'aide d'un ID utilisateur, d'un mot de passe ou d'autres identifiants. Lorsqu'un utilisateur se connecte à votre service tiers alors qu'il utilise un hôte Google Workspace, vous devez vous assurer qu'il n'a pas besoin de se reconnecter lorsqu'il change d'hôte Google Workspace. Pour éviter les requêtes de connexion répétées, utilisez les propriétés utilisateur ou les jetons d'ID. Ceux-ci sont expliqués dans les sections suivantes.

Propriétés utilisateur

Vous pouvez stocker les données de connexion d'un utilisateur dans les propriétés utilisateur d'Apps Script. Par exemple, vous pouvez créer votre propre JWT à partir de son service de connexion et l'enregistrer dans une propriété utilisateur, ou enregistrer le nom d'utilisateur et le mot de passe de son service.

Les propriétés utilisateur sont limitées de sorte qu'elles ne soient accessibles qu'à cet utilisateur dans le script de votre module complémentaire. Les autres utilisateurs et d'autres scripts ne peuvent pas accéder à ces propriétés. Pour plus d'informations, consultez la section PropertiesService.

Jetons d'ID

Vous pouvez utiliser un jeton d'ID Google comme identifiant de connexion pour votre service. C'est un moyen d'obtenir l'authentification unique. Les utilisateurs sont déjà connectés à Google, car ils se trouvent dans une application hôte Google.