Votre projet de modules complémentaires Google Workspace peut se connecter directement à de nombreux produits Google à l'aide des services intégrés et avancés d'Apps Script.
Vous pouvez également accéder à des API et des services autres que Google. Si le service ne nécessite pas d'autorisation, vous pouvez généralement effectuer une requête UrlFetch
appropriée, puis faire en sorte que votre module complémentaire interprète 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 à quel moment l'autorisation est requise et, le cas échéant, appeler le flux d'autorisation.
Le flux d'autorisation comprend:
- Alerter l'utilisateur que l'authentification est nécessaire et fournir un lien pour démarrer le processus
- Obtenir l'autorisation auprès d'un service autre que Google
- Actualisation du module complémentaire pour réessayer d'accéder à la ressource protégée.
Lorsqu'une autorisation autre que Google est requise, l'infrastructure du module complémentaire Google Workspace gère ces détails. Votre module complémentaire ne doit détecter que les autorisations requises et appeler le flux d'autorisation si nécessaire.
Détecter qu'une 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.
Votre code complémentaire doit détecter ces cas. 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 et le contenu de la réponse à la requête dans l'objet HttpResponse
renvoyé.
Lorsque le module complémentaire détecte une autorisation requise, il doit déclencher le flux d'autorisation.
Appeler le flux d'autorisation
Vous appelez le flux d'autorisation en utilisant le service Card pour créer un objet AuthorizationException
, en définissant ses propriétés, puis en appelant la fonction throwException()
. Avant de générer l'exception, vous devez fournir les éléments suivants:
- Obligatoire. URL d'autorisation. Ceci est spécifié par le service non-Google et est l'emplacement vers lequel l'utilisateur est dirigé au début du flux d'autorisation. Vous définissez cette URL à l'aide de la fonction
setAuthorizationUrl()
. - Obligatoire. Chaîne de nom à afficher pour la ressource. Identifie la ressource pour l'utilisateur lorsque l'autorisation est demandée. Vous pouvez définir ce nom à l'aide de la fonction
setResourceDisplayName()
. - Nom d'une fonction de rappel qui crée une invite d'autorisation personnalisée.
Ce rappel renvoie un tableau d'objets
Card
compilés qui composent une UI pour la gestion des autorisations. Cette option est facultative. Si elle n'est pas configurée, la carte d'autorisation par défaut est utilisée. Vous pouvez définir la fonction de rappel à l'aide de la fonctionsetCustomUiCallback()
.
Exemple de configuration OAuth non Google
Cet exemple de code montre comment configurer un module complémentaire pour utiliser une API non Google nécessitant OAuth. Il utilise 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
Par défaut, une invite d'autorisation ne comporte aucune marque et n'utilise que la chaîne de nom à afficher pour indiquer la ressource à laquelle le module complémentaire tente d'accéder. Votre module complémentaire peut toutefois définir une carte d'autorisation personnalisée ayant la même fonction, et qui peut inclure des informations supplémentaires et des éléments de branding.
Vous définissez une invite personnalisée en implémentant une fonction de rappel d'UI personnalisée qui renvoie un tableau d'objets Card
compilés. Ce tableau ne doit contenir qu'une seule fiche. Si vous en fournissez davantage, leurs en-têtes s'affichent sous forme de liste, ce qui peut perturber l'expérience utilisateur.
La carte renvoyée doit respecter les conditions suivantes:
- Indiquez clairement à l'utilisateur que le module complémentaire demande l'autorisation d'accéder en son nom à un service autre que Google.
- 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 objetOpenLink
pour garantir l'actualisation du module complémentaire après réception de l'autorisation. - Tous les liens ouverts à partir de l'invite d'autorisation doivent utiliser HTTPS.
Vous demandez au flux d'autorisation d'utiliser votre carte en appelant la fonction setCustomUiCallback()
sur votre objet AuthorizationException
.
L'exemple suivant montre 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 à partir d'une application hôte Google Workspace. La bibliothèque OAuth2 pour Apps Script peut vous aider à créer et à gérer des 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 passe à un autre hôte Google Workspace. Pour éviter les requêtes de connexion répétées, utilisez des propriétés utilisateur ou des jetons d'ID. Elles sont expliquées 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 jeton 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 définies de telle sorte qu'elles ne sont accessibles que par cet utilisateur dans le script de votre module complémentaire. Les autres utilisateurs et les 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 utilisent une application hôte Google.