Seu projeto de complemento do Google Workspace pode se conectar diretamente a muitos produtos do Google com os serviços integrados e avançados do Apps Script.
Também é possível acessar APIs e serviços de terceiros. Se o serviço não
exigir autorização, normalmente é possível fazer uma solicitação
UrlFetch
apropriada e fazer com que o
complemento interprete a resposta.
No entanto, se o serviço que não é do Google precisar de autorização, será necessário configurar o OAuth para ele. É possível facilitar esse processo usando a biblioteca OAuth2 para Apps Script. Também há uma versão do OAuth1.
Como usar um serviço OAuth
Ao usar um objeto de serviço OAuth para se conectar a um serviço que não é do Google, seu complemento do Google Workspace precisa detectar quando a autorização é necessária e, quando necessário, invocar o fluxo de autorização.
O fluxo de autorização consiste em:
- Informando ao usuário que a autenticação é necessária e fornecendo um link para iniciar o processo.
- Solicitação de autorização do serviço que não é do Google
- Atualizando o complemento para tentar acessar o recurso protegido novamente.
Quando a autorização que não é do Google é necessária, a infraestrutura dos complementos do Google Workspace processa esses detalhes. Seu complemento só precisa detectar quando a autorização é necessária e invocar o fluxo de autorização quando necessário.
É necessário detectar essa autorização
Uma solicitação pode não ter autorização para acessar um recurso protegido por várias razões, como:
- O token de acesso ainda não foi gerado ou expirou.
- O token de acesso não cobre o recurso solicitado.
- O token de acesso não cobre os escopos obrigatórios da solicitação.
O código do complemento precisa detectar esses casos. A função hasAccess()
da biblioteca OAuth pode informar se você tem acesso a um serviço. Como alternativa,
ao usar solicitações
UrlFetchApp fetch()
, você pode definir o parâmetro muteHttpExceptions
como true
. Isso
impede que a solicitação gere uma exceção em caso de falha, e permite
examinar o código de resposta da solicitação e o conteúdo no objeto
HttpResponse
retornado.
Quando o complemento detecta que a autorização é necessária, ele aciona o fluxo de autorização.
Como invocar o fluxo de autorização
Você invoca o fluxo de autorização usando o serviço de cartão
para criar um objeto
AuthorizationException
,
definir as propriedades e chamar a
função
throwException()
. Antes de gerar a exceção, forneça o seguinte:
- Obrigatório. Um URL de autorização. Ele é especificado pelo serviço que não é do Google e é o local para onde o usuário é levado quando o fluxo de autorização é iniciado. Defina esse URL usando a função
setAuthorizationUrl()
. - Obrigatório. Uma string de nome de exibição do recurso. Identifica o recurso para o usuário quando a autorização é solicitada. Esse nome é definido usando a
função
setResourceDisplayName()
. - O nome de uma função de callback que cria uma
solicitação de autorização personalizada.
Esse callback retorna uma matriz de objetos
Card
criados que compõem uma IU para processar a autorização. Isso é opcional. Se não for definido, será usado o cartão de autorização padrão. Defina a função de callback usando a funçãosetCustomUiCallback()
.
Exemplo de configuração de OAuth que não é do Google
Este exemplo de código mostra como configurar um complemento para usar uma API que não é do Google e exigir o OAuth. Ele usa o OAuth2 para Apps Script para criar um serviço para acessar a 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();
}
Como criar uma solicitação de autorização personalizada
Por padrão, uma solicitação de autorização não tem marca e usa apenas a string do nome de exibição para indicar qual recurso o complemento está tentando acessar. No entanto, seu complemento pode definir um cartão de autorização personalizado com a mesma finalidade e pode incluir outras informações e branding.
Para definir uma solicitação personalizada, implemente uma função de callback de IU personalizada
que retorne uma matriz de objetos Card
criados. Essa matriz deve conter apenas um único cartão. Se mais
forem fornecidos, os cabeçalhos serão exibidos em uma lista, o que pode resultar em
uma experiência confusa para o usuário.
O cartão retornado precisa fazer o seguinte:
- Deixe claro para o usuário que o complemento está pedindo permissão para acessar um serviço que não seja do Google.
- Deixe claro o que o complemento pode fazer se autorizado.
- Conter um botão ou widget semelhante que leva o usuário ao URL de autorização do serviço. Verifique se a função desse widget é óbvia para o usuário.
- O widget acima precisa usar a configuração
OnClose.RELOAD_ADD_ON
no objetoOpenLink
para garantir que o complemento seja recarregado após o recebimento da autorização. - Todos os links abertos na solicitação de autorização precisam usar HTTPS.
Você direciona o fluxo de autorização para usar o cartão chamando
a função setCustomUiCallback()
no objeto
AuthorizationException
.
O exemplo a seguir mostra uma função de callback de solicitação de autorização personalizada:
/**
* 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();
}
}
Gerenciar logins de terceiros nos apps do Google Workspace
Um aplicativo comum para complementos do Google Workspace é a interface de interação com um sistema de terceiros em um aplicativo host do Google Workspace. A biblioteca OAuth2 do Apps Script pode ajudar a criar e gerenciar conexões com serviços de terceiros.
Os sistemas de terceiros geralmente exigem que o usuário faça login usando um ID do usuário, uma senha ou outra credencial. Quando um usuário faz login no seu serviço de terceiros com um host do Google Workspace, você precisa garantir que ele não precise fazer login novamente quando mudar para outro host do Google Workspace. Para evitar solicitações de login repetidas, use propriedades de usuário ou tokens de ID. Eles são explicados nas seções a seguir.
Propriedades do usuário
Você pode armazenar os dados de login de um usuário nas propriedades do usuário do Apps Script. Por exemplo, é possível criar seu próprio JWT com base no serviço de login dele e registrá-lo em uma propriedade do usuário ou registrar o nome de usuário e a senha do serviço.
As propriedades do usuário têm escopo para que só possam ser acessadas por ele
no script do complemento. Outros usuários e outros scripts não podem acessar essas
propriedades. Veja PropertiesService
para mais detalhes.
Tokens de ID
É possível usar um token de ID do Google como a credencial de login do seu serviço. Essa é uma maneira de conectar o Logon único. Os usuários já estão conectados ao Google porque estão em um app host do Google.