The problem
We (me and a colleague) have some code that fits the use case of a webapp nicely, except for authentication of users outside of our domain. If not for this problem, we would have liked to use the following configuration (at the top level in our manifest file).
"webapp": {
"access": "ANYONE",
"executeAs": "USER_DEPLOYING"}
For users inside our domain, we can use Session.getActiveUser(). We can then compare this with our database and show sensitive data that should only be visible to this account.
For users outside our domain however, Session.getActiveUser()
gives the empty string ''
, so we cannot use this to query information in our database, so that we need something different. What we came up with is the following setup.
The alternative setup
We deploy the webapp with "executeAs": "USER_ACCESSING"
and "oauthScopes": ["https://www.googleapis.com/auth/userinfo.email"]
. We deploy another script as an API executable (and follow the instructions here), with a privileged account. The main Code.gs file of the webapp looks like this.
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index.html');
}
function getUserBackendData(){
//works for users outside our domain
var activeUserEmail = Session.getActiveUser();
var apiKey = 'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc'
var scriptId = 'ababababababababababababababababababababababab';
var token = 'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd'; //very secret
var url = 'https://script.googleapis.com/v1/scripts/'+ scriptId +':run?key='+ apiKey;
var functionCallData =
{
"function": 'retrieveData',
"parameters": [activeUserEmail],
"devMode": false};
var options = {
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(functionCallData),
'headers': {'Authorization': 'Bearer '+ token}
};
return UrlFetchApp.fetch(url, options).getContentText();
}
The idea is that this allows us to execute the function retrieveData(activeUserEmail)
in our API executable as a privileged user, while using the webapp to figure out the email address of the user accessing the app. The reason Session.getActiveUser()
now works for users outside our domain is that we deploy as "executeAs": "USER_ACCESSING"
.
Question(s)
Our concerns are that 1) there is an easier/better way to do this and 2) the user accessing the webapp can somehow retrieve the token used to call the function in the api executable, which we fear could be used to steal private data.
This Q&A touches on concern 2. Unfortunately the answers do not cite any official documentation/resources (and don't all agree). Moreover, there, the secret library key is not present in the source code of the webapp (it is in the manifest file), whereas in our case the secret token is in the source code.
Question: Is this alternative setup secure and reasonable?
Update: I just found this answer, written by a user that seems credible. The answer says that the user of the web app will not be able to see the code of the web app, even when the app is deployed as "Anyone, even anonymous". This claim is hedged with "That I know of" though.