2015-02-06 99 views
3

就网络开发而言,我是一个新手,对于Google应用程序脚本和OAuth2.0,我更是如此。话虽如此,我已经研究了足够多,并尝试了几个技巧,但仍然无法摆脱这个问题。IFRAME沙箱中的Google OAuth2和应用程序脚本

我借样品从这里:

Google Developers - Client API Library

然后用的index.html文件从该网页代码中创建Google Apps脚本项目。我还在开发人员控制台上创建了一个项目,创建了一个客户端ID和API密钥,并打开了所需的API支持。我还对示例进行了必要的更改以反映新的客户端ID和API密钥。

index.html页面由HTML服务提供,SandBox Mode设置为IFRAME。如果我在浏览器窗口中加载URL(例如使用隐身模式)并点击“授权”按钮,它将打开Goog​​le登录窗口。但在签约后,它会打开与消息的两个新选项卡

请关闭此窗口

和原来的浏览器窗口中显示没有变化。

JavaScript控制台显示错误信息等这些:

不安全JavaScript尝试从帧开始导航帧与URL '' 与URL https://accounts.google.com/o/oauth2/postmessageRelay?parent=https%3A%2F%2F…6lxdpyio6iqy-script.googleusercontent.com#rpctoken=288384029&forcesecure=1。 尝试导航的框架是沙盒,因此 不允许导航其祖先。

从消息看来,它似乎是使用IFRAME的一种效果,某种安全功能会阻止将回调传递到原始窗口。如果我重新加载原始窗口,事情工作正常。但是,这不是我想要的。

我该如何解决此问题?它是一个非常简单的项目,如果有帮助,我可以提供源代码。

感谢, 帕

编辑:这是我想要的示例代码。您需要将您的客户ID和API密钥,并在谷歌Console还设置JS起源的东西的工作:

Code.gs

function doGet(e) { 
    return HtmlService.createHtmlOutputFromFile('index').setSandboxMode(HtmlService.SandboxMode.IFRAME); 
} 

的index.html

<!-- 
    Copyright (c) 2011 Google Inc. 

    Licensed under the Apache License, Version 2.0 (the "License"); you may not 
    use this file except in compliance with the License. You may obtain a copy of 
    the License at 

    http://www.apache.org/licenses/LICENSE-2.0 

    Unless required by applicable law or agreed to in writing, software 
    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
    License for the specific language governing permissions and limitations under 
    the License. 

    To run this sample, replace YOUR API KEY with your application's API key. 
    It can be found at https://code.google.com/apis/console/?api=plus under API Access. 
    Activate the Google+ service at https://code.google.com/apis/console/ under Services 
--> 
<!DOCTYPE html> 
<html> 
    <head> 
    <meta charset='utf-8' /> 
    </head> 
    <body> 
    <!--Add a button for the user to click to initiate auth sequence --> 
    <button id="authorize-button" style="visibility: hidden">Authorize</button> 
    <script type="text/javascript"> 
     // Enter a client ID for a web application from the Google Developer Console. 
     // The provided clientId will only work if the sample is run directly from 
     // https://google-api-javascript-client.googlecode.com/hg/samples/authSample.html 
     // In your Developer Console project, add a JavaScript origin that corresponds to the domain 
     // where you will be running the script. 
     var clientId = 'YOUR_CLIENT_ID'; 


     // Enter the API key from the Google Develoepr Console - to handle any unauthenticated 
     // requests in the code. 
     // The provided key works for this sample only when run from 
     // https://google-api-javascript-client.googlecode.com/hg/samples/authSample.html 
     // To use in your own application, replace this API key with your own. 
     var apiKey = 'YOUR API KEY'; 


     // To enter one or more authentication scopes, refer to the documentation for the API. 
     var scopes = 'https://www.googleapis.com/auth/plus.me'; 

     // Use a button to handle authentication the first time. 
     function handleClientLoad() { 
     gapi.client.setApiKey(apiKey); 
     window.setTimeout(checkAuth,1); 
     } 

     function checkAuth() { 
     gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true, response_type: 'token'}, handleAuthResult); 
     } 


     function handleAuthResult(authResult) { 
     var authorizeButton = document.getElementById('authorize-button'); 
     if (authResult && !authResult.error) { 
      authorizeButton.style.visibility = 'hidden'; 
      makeApiCall(); 
     } else { 
      authorizeButton.style.visibility = ''; 
      authorizeButton.onclick = handleAuthClick; 
     } 
     } 

     function handleAuthClick(event) { 
     gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false, response_type: 'token'}, handleAuthResult); 
     return false; 
     } 

     // Load the API and make an API call. Display the results on the screen. 
     function makeApiCall() { 
     gapi.client.load('plus', 'v1', function() { 
      var request = gapi.client.plus.people.get({ 
      'userId': 'me' 
      }); 
      request.execute(function(resp) { 
      var heading = document.createElement('h4'); 
      var image = document.createElement('img'); 
      image.src = resp.image.url; 
      heading.appendChild(image); 
      heading.appendChild(document.createTextNode(resp.displayName)); 
      heading.appendChild(document.createTextNode(resp.emails[0].value)); 

      document.getElementById('content').appendChild(heading); 
      }); 
     }); 
     } 
    </script> 
    <script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script> 
    <div id="content"></div> 
    <p>Retrieves your profile name using the Google Plus API.</p> 
    </body> 
</html> 
+0

如果要强制执行日志与谷歌帐户,你可以将发布权限设置为:**谁有权访问应用程序:任何人**而不是:**任何人,甚至匿名**这样,您甚至不需要oAuth2。 – 2015-02-06 14:58:41

+0

你见过这个文档吗? [Apps脚本 - 使用oauth发送请求](https://developers.google.com/apps-script/guides/services/external#making_requests_to_services_with_oauth) – 2015-02-06 15:13:27

+0

“谁有权访问应用程序:任何人”的问题是,当应用程序被用作谷歌网站上的小工具(这正是我最终想做的),它不显示登录屏幕,而是只显示空白框架,如果用户还没有登录。如果它用作web应用程序,那么会显示登录屏幕,但不会显示小工具。我已经看过这个文档,并且还经历了许多其他文档/教程/示例,但仍不确定如何解决此问题。 – 2015-02-06 18:16:30

回答

1

发现一个解决方案...不好但工程oo:

诀窍是在auth窗口关闭之前删除oauth2relay iframe。窗口关闭后,您必须再次添加框架并立即请求,如果该功能适用​​于用户授权的应用程序。

小心
如果用户同时被注销或令牌已过期,只要Web应用程序窗口打开时,使用同样的道理此脚本不检查。

Code.js:

function doGet(e) { 
    return HtmlService.createTemplateFromFile('Index').evaluate().setTitle(formSettings.title).setSandboxMode(HtmlService.SandboxMode.IFRAME); 
} 

function include(file) { 
    return HtmlService.createHtmlOutputFromFile(file).getContent(); 
} 

function doPost(meta) { 
    if (!meta || !meta.auth) { 
    throw new Error('not authorized'); 
    return; 
    } 
    var auth = JSON.parse(UrlFetchApp.fetch('https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' + meta.auth.access_token, { muteHttpExceptions: true }).getContentText()); 
    if (auth.error || !auth.email) { 
    throw new Error('not authorized'); 
    return; 
    } 

    if (typeof this[meta.method + '_'] == 'function') { 
    return this[meta.method + '_'](auth.email, meta.data); 
    } 
    throw new Error('unknown method'); 
} 

function test_(email, data) { 
    return email; 
} 

的Index.html:

<html> 
    <head> 
    <?!= include('JavaScript'); ?> 
    </head> 
    <body> 
    <div class="content-wrapper"> 

    </div> 
    </body> 
</html> 

Javascript.html:

<script type='text/javascript' src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> 
<script type='text/javascript' src="//apis.google.com/js/client.js?onload=apiLoaded" async></script> 
<script type='text/javascript'> 
    var clientId = '*************-********************************.apps.googleusercontent.com'; 
    var scopes = ['https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/userinfo.email']; 

    var loaded = false; 
    var auth = null; 

    function apiLoaded() { 
     loaded = true; 
     login(); 
    } 

    window._open = window.open; 
    window._windows = []; 
    window.open = function(url) { 
     var w = window._open.apply(window,arguments); 
     window._windows.push(w); 
     return w; 
    } 

    function login(step) { 
     step || (step = 0); 
     if (!loaded) { 
     return; 
     } 
     gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: (step <= 0 || step >= 2) }, function(authResult) { 
     if (authResult) { 
      if (authResult.error) { 
      if (authResult.error == 'immediate_failed' && authResult.error_subtype == 'access_denied' && step <= 0) { 
       var interval = setInterval(function() { 
       var $ifr = $('iframe');//[id^=oauth2relay]'); 
       if (!window._windows.length) { 
        clearInterval(interval); 
        return; 
       } 
       if ($ifr.length) { 
        clearInterval(interval); 
        $ifr.detach(); 
        var w = window._windows.pop(); 
        if (w) { 
        var interval2 = setInterval(function() { 
         if (w.closed) { 
         clearInterval(interval2); 
         $('body').append($ifr); 
         login(2); 
         } 
        }); 
        } else {     
        $('body').append($ifr); 
        } 
       } 
       },500); 
       login(1); 
      } else if (authResult.error == 'immediate_failed' && authResult.error_subtype == 'access_denied' && step >= 2) { 
       //user canceled auth 
      } else { 
       //error 
      } 
      } else { 
      auth = authResult; 
      doPost('test', { some: 'data' }, 'test'); 
      } 
     } else { 
      //error 
     } 
     }); 
    } 

    function test() { 
     console.log(arguments); 
    } 

    //with this method you can do a post request to webapp server 
    function doPost(method, data, callbackName) { 
     data || (data = {}); 
     google.script.run.withSuccessHandler(onSuccess).withFailureHandler(onError).withUserObject({ callback: callbackName }).doPost({ method: method, data: data, auth: auth }); 
    } 

    function onSuccess(data, meta) { 
     if (typeof window[meta.callback] == 'function') { 
     window[meta.callback](null, data); 
     } 
    } 

    function onError(err, meta) { 
     if (typeof window[meta.callback] == 'function') { 
     window[meta.callback](err); 
     } 
    } 
</script> 
相关问题