2015-02-06 64 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>