Si tu complemento de Google Workspace se conecta a un servicio o API de terceros que requiere autorización, el complemento puede solicitar a los usuarios que accedan y autoricen el acceso.
En esta página, se explica cómo autenticar a los usuarios con un flujo de autorización (como OAuth), que incluye los siguientes pasos:
- Detecta cuándo se requiere autorización.
- Muestra una interfaz de tarjeta que les solicita a los usuarios que accedan al servicio.
- Actualiza el complemento para que los usuarios puedan acceder al servicio o al recurso protegido.
Si tu complemento solo requiere la identidad del usuario, puedes autenticarlo directamente con su ID de Google Workspace o dirección de correo electrónico. Para usar la dirección de correo electrónico para la autenticación, consulta Cómo validar solicitudes JSON. Si compilaste tu complemento con Google Apps Script, puedes facilitar este proceso con la biblioteca de OAuth2 para Google Apps Script (también hay una versión de OAuth1).
Detecta que se requiere autorización
Cuando usan tu complemento, es posible que los usuarios no tengan autorización para acceder a un recurso protegido por varios motivos, como los siguientes:
- Aún no se genera un token de acceso para conectarse al servicio de terceros o este expiró.
- El token de acceso no cubre el recurso solicitado.
- El token de acceso no cubre los permisos necesarios de la solicitud.
Tu complemento debe detectar estos casos para que los usuarios puedan acceder a tu servicio.
Si compilas en Apps Script, la función hasAccess()
de la biblioteca de OAuth puede indicarte si tienes acceso a un servicio.
Como alternativa, cuando uses solicitudes UrlFetchApp fetch()
, puedes configurar el parámetro muteHttpExceptions
como true
. Esto evita que la solicitud genere una excepción en caso de falla y te permite examinar el código y el contenido de la respuesta de la solicitud en el objeto HttpResponse
que se muestra.
Solicita a los usuarios que accedan a tu servicio
Cuando el complemento detecta que se requiere la autorización, debe mostrar una interfaz de tarjeta para solicitar a los usuarios que accedan al servicio. La tarjeta de acceso debe redireccionar a los usuarios para que completen el proceso de autenticación y autorización de terceros en tu infraestructura.
Cuando compilas tu complemento con extremos HTTP, te recomendamos que protejas la app de destino con el Acceso con Google y que obtengas el ID del usuario con el token de identidad que se emite durante el acceso. El subtítulo contiene el ID único del usuario y se puede correlacionar con el ID de tu complemento.
Cómo compilar y mostrar una tarjeta de acceso
Para la tarjeta de acceso de tu servicio, puedes usar la tarjeta de autorización básica de Google o personalizar una tarjeta para mostrar información adicional, como el logotipo de tu organización. Si publicas tu complemento de forma pública, debes usar una tarjeta personalizada.
Tarjeta de autorización básica
En la siguiente imagen, se muestra un ejemplo de la tarjeta de autorización básica de Google:
En el siguiente código, se muestra un ejemplo del uso de la tarjeta de autorización básica de Google:
Apps Script
CardService.newAuthorizationException() .setAuthorizationUrl('AUTHORIZATION_URL') .setResourceDisplayName('RESOURCE_DISPLAY_NAME') .throwException();
JSON
Muestra la siguiente respuesta JSON:
{
"basic_authorization_prompt": {
"authorization_url": "AUTHORIZATION_URL",
"resource": "RESOURCE_DISPLAY_NAME"
}
}
Reemplaza lo siguiente:
AUTHORIZATION_URL
: Es la URL de la app web que controla la autorización.RESOURCE_DISPLAY_NAME
: Es el nombre visible del servicio o recurso protegido. Este nombre se le muestra al usuario en el mensaje de autorización. Por ejemplo, si tuRESOURCE_DISPLAY_NAME
esExample Account
, el mensaje dice "Este complemento desea mostrar información adicional, pero necesita aprobación para acceder a tu cuenta de ejemplo".
Después de completar la autorización, se le solicita al usuario que actualice el complemento para acceder al recurso protegido.
Tarjeta de autorización personalizada
Para modificar el mensaje de autorización, puedes crear una tarjeta personalizada para la experiencia de acceso de tu servicio.
Si publicas tu complemento públicamente, debes usar una tarjeta de autorización personalizada. Para obtener más información sobre los requisitos de publicación de Google Workspace Marketplace, consulta Acerca de la revisión de apps.
La tarjeta devuelta debe cumplir con los siguientes requisitos:
- Deja en claro al usuario que el complemento solicita permiso para acceder a un servicio que no es de Google en su nombre.
- Deja en claro lo que el complemento puede hacer si se le autoriza.
- Contiene un botón o un widget similar que dirige al usuario a la URL de autorización del servicio. Asegúrate de que la función de este widget sea evidente para el usuario.
- El widget anterior debe usar la configuración
OnClose.RELOAD
en su objetoOpenLink
para garantizar que el complemento se vuelva a cargar después de recibir la autorización. - Todos los vínculos que se abran desde el mensaje de autorización deben usar HTTPS.
En la siguiente imagen, se muestra un ejemplo de tarjeta de autorización personalizada para la página principal de un complemento. La tarjeta incluye un logotipo, una descripción y un botón de acceso:
En el siguiente código, se muestra cómo usar este ejemplo de tarjeta personalizada:
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
Muestra la siguiente respuesta 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"
}
}
]
}
]
}
}
]
}
}
}
Reemplaza lo siguiente:
LOGO_URL
: Es la URL de un logotipo o una imagen. Debe ser una URL pública.LOGO_ALT_TEXT
: Es el texto alternativo del logotipo o la imagen, comoCymbal Labs Logo
.DESCRIPTION
: Un llamado a la acción para que los usuarios accedan, comoSign in to get started
.- Para actualizar el botón de acceso, haz lo siguiente:
AUTHORIZATION_URL
: Es la URL de la app web que controla la autorización.- Opcional: Para cambiar el color del botón, actualiza los valores de punto flotante RGBA del campo
color
. En el caso de Apps Script, actualiza el métodosetBackgroundColor()
con valores hexadecimales.
TEXT_SIGN_UP
: Es un texto que les solicita a los usuarios que creen una cuenta si no tienen una. Por ejemplo,New to Cymbal Labs? <a href=\"https://www.example.com/signup\">Sign up</a> here
Administra los accesos de terceros en las apps de Google Workspace
Una aplicación común para los complementos de Google Workspace es proporcionar una interfaz para interactuar con un sistema de terceros desde una aplicación host de Google Workspace.
Los sistemas de terceros suelen requerir que el usuario acceda con un ID de usuario, una contraseña o alguna otra credencial. Cuando un usuario accede a tu servicio de terceros mientras usa un host de Google Workspace, debes asegurarte de que no tenga que volver a acceder cuando cambie a otro host de Google Workspace.
Si compilas en Apps Script, puedes evitar solicitudes de acceso repetidas con propiedades del usuario o tokens de ID. Estos se explican en las siguientes secciones.
Propiedades del usuario
Puedes almacenar los datos de acceso de un usuario en las propiedades del usuario de Apps Script. Por ejemplo, puedes crear tu propio token web JSON (JWT) desde su servicio de acceso y registrarlo en una propiedad del usuario, o bien registrar el nombre de usuario y la contraseña de su servicio.
Las propiedades del usuario tienen un alcance de modo que solo ese usuario puede acceder a ellas dentro de la secuencia de comandos de tu complemento. Otros usuarios y otras secuencias de comandos no pueden acceder a estas propiedades. Consulta PropertiesService
para obtener más detalles.
Tokens de ID
Puedes usar un token de ID de Google como credencial de acceso para tu servicio. Esta es una forma de lograr el inicio de sesión único. Los usuarios ya accedieron a Google porque están en una app de host de Google.
Ejemplo de configuración de OAuth que no es de Google
En la siguiente muestra de código de Apps Script, se muestra cómo configurar un complemento para usar una API que no sea de Google que requiera OAuth. En este ejemplo, se usa la biblioteca OAuth2 para Apps Script para crear un servicio que permita acceder a la 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. 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();
}