I can restrict the usage of the Web App but I cannot limit the access to the images referred in the <img> tag.
Now I am developing a Web application with Google Apps Script. This is an internal application of the corporation and I got to set the assets' access rights carefully.
For App itself, from Deploy as web app box of the script editor, I set Execute the app as Me (bar@foo.example.com) and Who has access to the app as Anyone within FOO corporation.
(Here let's say I belong to the FOO corporation. My Google account is bar@foo.example.com)
Now, when I login with the Google account of FOO, I could successfully access the Web App. But when I didn't login, I couldn't access it. That's what I want.
But when I set <img> tag to show the jpeg file in the Google Drive, I got to set the Share of the image file as Anyone on the internet with this link can view.
When I set <img> tag in the HTML in the Web App project and set the Share of the jpeg file as FOO Corporation -- Anyone in this group with this link can view, the <img> tag will not work (Error 403 will be returned).
I want to restrict the images' access right as well as the web app. How can I do it?
How to reproduce
put jpeg file
index.html
file and add the code:<html>
<head>
<base target="_top">
</head>
<body>
<img src="https://drive.google.com/uc?id=xxxx_object ID_xxxx" width="30" height="30" alt="error alt text">
</body>
</html>
create Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('index'); }
publish it as Web App
the result -- error
Google (logo) 403. That’s an error. We're sorry, but you do not have access to this page. That’s all we know.
change the access right of the image file
What I want to do?
I want to set the access right of the image restricted as well as the Web app (only the user of FOO corporation can access). How can I do it?
403 Forbidden
The /uc
endpoint, when the file permission is set to "in this group", returns a 403 Forbidden
response even if you are logged in the G Suite account.
Workaround
You can implement a flow of dynamically appending an HTMLImageElement
with src
attribute set to image data (base-64 encoded string from bytes). With this, you can restrict access to both Web App and the image and still be able to load it.
When deploying the Web App, make sure that the deployment has sufficient access to the file, for example, when the file has "Anyone in this group with this link can view" permissions:
Execute the app as: Me
Who has access to the app: Anyone within [org]
Below is a small proof of concept, including a server-side utility and a sample HTML file.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const asyncGAPIv2 = ({
funcName,
onFailure = console.error,
onSuccess,
params = []
}) => {
return new Promise((res, rej) => {
google.script.run
.withSuccessHandler(data => {
typeof onSuccess === "function" && onSuccess(data);
res(data);
})
.withFailureHandler(error => {
typeof onFailure === "function" && onFailure(error);
rej(error);
})
[funcName].apply(null, params);
});
};
const fetchAndAppendImage = async ({ parent = document.body, id }) => {
const data = await asyncGAPIv2({
funcName: "getImageFromDrive",
params: [{ id, token }]
});
const img = document.createElement("img");
img.src = data;
parent.append(img);
};
(async () => await fetchAndAppendImage({ id: "id here" }))();
</script>
</body>
</html>
You can pass the id to a server-side utility, get the file with getFileById
(native authentication flow will ensure the requester will not get access to a file they do not have access to), and form an image data string by doing the following:
File
instance by changing getBlob
to getBytes
.base64Encode
method of the Utilities
service to convert bytes to a base-64 encoded string and prepend data:image/png;base64,
(Data URL scheme). If your images have another MIME-type, amend accordingly./**
* @summary gets an image from Google Drive
*
* @param {{
* id : string
* }}
*
* @returns {string}
*/
const getImageFromDrive = ({ id }) => {
try {
const file = DriveApp.getFileById(id);
const bytes = file.getBlob().getBytes();
const data = `data:image/png;base64,${Utilities.base64Encode(bytes)}`;
return data;
} catch (error) {
console.warn(error);
return "";
}
};
Notes
/uc
endpoint with "in this group" permission while being only logged in to a G Suite account has no authorization issues.