Прежде всего, вы правы в своем анализе.На странице входа Google (и на самом деле это большой процент размещенного в Google контента) X-Frame-Options отключено, а перенаправление заблокировано для загрузки внутри iframe из-за этого параметра.Если пользователь уже вошел в Google, но не авторизовал приложение, я считаю, что большую часть времени они должны видеть диалог авторизации в iframe без ошибок (о чем Алан Уэллс сообщал).Тем не менее, я не проверил полностью, и это может быть для пользователей с несколькими одновременными входами (например, вошли в несколько Gmail), он выгонит вас на страницу входа и вызовет блок X-Frame-Options.
* 1002В любом случае, после некоторого раскопок, я нашел рабочее решение для этого.Это немного глупо из-за всех различных ограничений, которые Apps Script накладывает на то, что можно использовать.Например, я сначала хотел использовать
postMessage
для передачи сообщения от встроенного iframe на родительскую страницу, и если родитель не получил сообщение в X # секунд, он бы предположил, что iframe не удалось загрузить, и будетперенаправить пользователя для входа / авторизации приложения.Увы,
postMessage
не очень хорошо работает со скриптом приложений из-за того, что они дважды встраивают фреймы.
решение:
Решение, которое я получил, заключалось в использовании подхода JSONP.Это кратко упомянуто Google здесь .Во-первых, поместите оверлей над iframe, который предлагает пользователю аутентифицировать приложение, со ссылкой для этого.Затем вы дважды загружаете скрипт приложения, один раз как iframe, а затем снова как тег <script></script>
.Если тег <script>
загружается успешно, он вызывает функцию обратного вызова, которая скрывает наложение подсказки, так что нижний iframe может стать видимым.
Вот мой код, урезанный, чтобы вы могли увидеть, как это работает:
Emedded HTML:
<style>
.appsWidgetWrapper {
position: fixed;
}
.appsWidget {
width: 100%;
height: 100%;
border: none !important;
}
.loggedOut {
top: 0px;
left: 0px;
position: absolute;
width: 100%;
height: 100%;
background-color: darksalmon;
text-align: center;
}
</style>
<!-- Script loaded as iframe widget with fallback -->
<div class="appsWidgetWrapper">
<iframe class="appsWidget" src="https://script.google.com/macros/s/SCRIPT_ID/exec?embedIframe"></iframe>
<div class="loggedOut">
<div class="loggedOutContent">
<div class="loggedOutText">You need to "authorize" this widget.</div>
<button class="authButton">Log In / Authorize</button>
</div>
</div>
</div>
<!-- Define JSONP callback and authbutton redirect-->
<script>
function authSuccess(email){
console.log(email);
// Hide auth prompt overlay
document.querySelector('.loggedOut').style.display = 'none';
}
document.querySelectorAll('.authButton').forEach(function(elem){
elem.addEventListener('click',function(evt){
var currentUrl = document.location.href;
var authPage = 'https://script.google.com/macros/s/SCRIPT_ID/exec?auth=true&redirect=' + encodeURIComponent(currentUrl);
window.open(authPage,'_blank');
});
});
</script>
<!-- Fetch script as JSONP with callback -->
<script src="https://script.google.com/macros/s/SCRIPT_ID/exec?jsonpCallback=authSuccess"></script>
И Code.gs (Apps Script)
function doGet(e) {
var email = Session.getActiveUser().getEmail();
if (e.queryString && 'jsonpCallback' in e.parameter){
// JSONP callback
// Get the string name of the callback function
var cbFnName = e.parameter['jsonpCallback'];
// Prepare stringified JS that will get evaluated when called from <script></script> tag
var scriptText = "window." + cbFnName + "('" + email + "');";
// Return proper MIME type for JS
return ContentService.createTextOutput(scriptText).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
else if (e.queryString && ('auth' in e.parameter || 'redirect' in e.parameter)){
// Script was opened in order to auth in new tab
var rawHtml = '<p>You have successfully authorized the widget. You can now close this tab and refresh the page you were previously on.</p>';
if ('redirect' in e.parameter){
rawHtml += '<br/><a href="' + e.parameter['redirect'] + '">Previous Page</a>';
}
return HtmlService.createHtmlOutput(rawHtml);
}
else {
// Display HTML in iframe
var rawHtml = "<h1>App Script successfully loaded in iframe!</h1>"
+ "\n"
+ "<h2>User's email used to authorize: <?= authedEmail ?></h2>";
var template = HtmlService.createTemplate(rawHtml);
template.authedEmail = email;
return template.evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
}
В этом примере «authSuccess» - это моя функция обратного вызова JSONP, которая должна получитьвызывается по электронной почте авторизованного пользователя, если сценарий выполнен успешно.В противном случае, если пользователю необходимо войти в систему или авторизоваться, это не произойдет, и наложение останется видимым и заблокирует отображение ошибки iframe для пользователя.
Я также разместил полный код вGithub , где структура может быть немного легче увидеть.