I'm writing a Google Hangouts Chat bot with Google Apps Script. The bot authenticates with a third party service with the apps script OAuth2 library. As documented in this How-To, when a message is received by the bot but authentication with the third party service is required, the bot sends a special REQUEST_CONFIG
reply to Chat containing the configCompleteRedirectUrl
.
var scriptProperties = PropertiesService.getScriptProperties();
function onMessage(event) {
var service = getThirdPartyService();
if (!service.hasAccess()) {
return requestThirdPartyAuth(service, event);
}
Logger.log('execution passed authentication');
return { text: 'Original message ' + event.message.argumentText };
}
function getThirdPartyService() {
var clientId = scriptProperties.getProperty('CLIENT_ID');
var clientSecret = scriptProperties.getProperty('CLIENT_SECRET');
return OAuth2.createService('ThirdPartyApp')
// Set the endpoint URLs.
.setAuthorizationBaseUrl('https://...')
.setTokenUrl('https://.../oauth/token')
// Set the client ID and secret.
.setClientId(clientId)
.setClientSecret(clientSecret)
// Set the name of the callback function that should be invoked to
// complete the OAuth flow.
.setCallbackFunction('authThirdPartyCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
.setCache(CacheService.getUserCache())
.setLock(LockService.getUserLock())
// Set the scope and other parameters.
.setScope('...')
.setParam('audience', '...')
.setParam('prompt', 'consent');
}
function requestThirdPartyAuth(service, event) {
Logger.log('Authorization requested');
return { "actionResponse": {
"type": "REQUEST_CONFIG",
"url": service.getAuthorizationUrl({
configCompleteRedirectUrl: event.configCompleteRedirectUrl
})
}};
/**
* Handles the OAuth callback.
*/
function authThirdPartyCallback(request) {
var service = getThirdPartyService();
var authorized = service.handleCallback(request);
if (authorized) {
Logger.log("user authorized");
//https://stackoverflow.com/a/48030297/9660
return HtmlService.createHtmlOutput("<script>window.top.location.href='" + request.parameter.configCompleteRedirectUrl + "';</script>");
} else {
Logger.log("user denied access");
return HtmlService.createHtmlOutput('Denied');
}
}
The service defines a callback authentication function, which in turn sends the browser to the configCompleteRedirectUrl
. After this URL has been reached, the original message is supposed to be sent or re-dispatched a second time (see How-To step 7.3).
The authentication callback is successful because the last page displayed in the browser OAuth flow is the one specified in event.configCompleteRedirectUrl
. Within the Chat window, the configuration prompt is erased, and the original message is changed to public. However, the original message is not dispatched again. The last log displayed in the apps script console is from the authentication callback event.
Is there something I have done incorrectly that prevents the original message from being dispatched again?
After much back and forth with a Google support team member, it turns out that there is a bug in the Hangouts Chat implementation when run against the V8 Apps Script runtime.
My appsscript.json
file had "runtimeVersion": "V8"
set. The re-dispatch does not work in this scenario. After I reverted to "runtimeVersion": "STABLE"
in appsscript.json
and re-deployed my scripts, the re-dispatch started working.