2016-01-14 11 views
7

मैं AngularJS और ASP.Net वेबएपीआई (जो काफी अच्छा है) के साथ सामाजिक लॉगिन पर इस लेख अनुसरण कर रही हूं:AngularJS और ASP.Net वेबएपीआई सामाजिक लॉगिन एक मोबाइल ब्राउज़र पर

ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app

सुंदर ज्यादा, जब आप डेस्कटॉप ब्राउज़र (यानी क्रोम, एफएफ, आईई, एज) के माध्यम से सामाजिक लॉगिन चला रहे हैं तो कोड ठीक काम करता है। सामाजिक लॉगिन एक नई विंडो (टैब नहीं) में खुलता है और आप या तो अपने Google या फेसबुक खाते का उपयोग कर सकते हैं और एक बार जब आप इनमें से किसी के माध्यम से लॉग इन हो जाते हैं, तो आपको कॉलबैक पेज (authComplete.html) पर रीडायरेक्ट किया जाता है, और कॉलबैक पेज में एक जेएस फ़ाइल परिभाषित है (authComplete.js) जो विंडो बंद कर देगी और पैरेंट विंडो पर कमांड निष्पादित करेगी।

AngularJS नियंत्रक जो बाहरी प्रवेश URL कॉल करता है और डेस्कटॉप ब्राउज़र पर एक पॉपअप विंडो (नहीं टैब) को खोलता है:

loginController.js

'use strict'; 
app.controller('loginController', ['$scope', '$location', 'authService', 'ngAuthSettings', function ($scope, $location, authService, ngAuthSettings) { 

    $scope.loginData = { 
     userName: "", 
     password: "", 
     useRefreshTokens: false 
    }; 

    $scope.message = ""; 

    $scope.login = function() { 

     authService.login($scope.loginData).then(function (response) { 

      $location.path('/orders'); 

     }, 
     function (err) { 
      $scope.message = err.error_description; 
     }); 
    }; 

    $scope.authExternalProvider = function (provider) { 

     var redirectUri = location.protocol + '//' + location.host + '/authcomplete.html'; 

     var externalProviderUrl = ngAuthSettings.apiServiceBaseUri + "api/Account/ExternalLogin?provider=" + provider 
                    + "&response_type=token&client_id=" + ngAuthSettings.clientId 
                    + "&redirect_uri=" + redirectUri; 
     window.$windowScope = $scope; 

     var oauthWindow = window.open(externalProviderUrl, "Authenticate Account", "location=0,status=0,width=600,height=750"); 
    }; 

    $scope.authCompletedCB = function (fragment) { 

     $scope.$apply(function() { 

      if (fragment.haslocalaccount == 'False') { 

       authService.logOut(); 

       authService.externalAuthData = { 
        provider: fragment.provider, 
        userName: fragment.external_user_name, 
        externalAccessToken: fragment.external_access_token 
       }; 

       $location.path('/associate'); 

      } 
      else { 
       //Obtain access token and redirect to orders 
       var externalData = { provider: fragment.provider, externalAccessToken: fragment.external_access_token }; 
       authService.obtainAccessToken(externalData).then(function (response) { 

        $location.path('/orders'); 

       }, 
      function (err) { 
       $scope.message = err.error_description; 
      }); 
      } 

     }); 
    } 
}]); 

authComplete.html

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
    <title></title> 

</head> 
<body> 
    <script src="scripts/authComplete.js"></script> 
</body> 
</html> 

authComplete.js

window.common = (function() { 
    var common = {}; 

    common.getFragment = function getFragment() { 
     if (window.location.hash.indexOf("#") === 0) { 
      return parseQueryString(window.location.hash.substr(1)); 
     } else { 
      return {}; 
     } 
    }; 

    function parseQueryString(queryString) { 
     var data = {}, 
      pairs, pair, separatorIndex, escapedKey, escapedValue, key, value; 

     if (queryString === null) { 
      return data; 
     } 

     pairs = queryString.split("&"); 

     for (var i = 0; i < pairs.length; i++) { 
      pair = pairs[i]; 
      separatorIndex = pair.indexOf("="); 

      if (separatorIndex === -1) { 
       escapedKey = pair; 
       escapedValue = null; 
      } else { 
       escapedKey = pair.substr(0, separatorIndex); 
       escapedValue = pair.substr(separatorIndex + 1); 
      } 

      key = decodeURIComponent(escapedKey); 
      value = decodeURIComponent(escapedValue); 

      data[key] = value; 
     } 

     return data; 
    } 

    return common; 
})(); 

var fragment = common.getFragment(); 
window.location.hash = fragment.state || ''; 
window.opener.$windowScope.authCompletedCB(fragment); 
window.close(); 

मुद्दा मैं कर रहा हूँ कि जब मैं एक मोबाइल डिवाइस (सफारी, मोबाइल के लिए Chrome) पर आवेदन चलाने के लिए, सामाजिक लॉगिन विंडो एक नया टैब और जे एस समारोह जो पारित करने के लिए इरादा था में खुलती है वापस मुख्य अनुप्रयोग विंडो के टुकड़े नाड निष्पादित नहीं करता है नया टैब बंद नहीं होता है।

आप वास्तव में आवेदन के माध्यम से दोनों एक डेस्कटॉप और मोबाइल ब्राउज़र पर इस व्यवहार की कोशिश कर सकते हैं:

http://ngauthenticationapi.azurewebsites.net/

क्या मैं इस संदर्भ में अब तक की कोशिश की है लॉगिन नियंत्रक में है, मैं इतना समारोह संशोधित कि बाहरी प्रवेश uRL एक ही विंडो में खुलता है:

$scope.authExternalProvider = function (provider) { 
     var redirectUri = location.protocol + '//' + location.host + '/authcomplete.html'; 
     var externalProviderUrl = ngAuthSettings.apiServiceBaseUri + "api/Account/ExternalLogin?provider=" + provider 
                                   + "&response_type=token&client_id=" + ngAuthSettings.clientId 
                                   + "&redirect_uri=" + redirectUri; 
     window.location = externalProviderUrl; 
}; 

और, प्रवेश पृष्ठ पर वापस जाने के लिए सामाजिक द्वारा प्रदान की पहुँच टोकन जोड़कर authComplete.js common.getFragment समारोह संशोधित क्वेरी स्ट्रिंग के रूप में प्रवेश:

var vm = this; 
var fragment = null; 

vm.testFn = function (fragment) { 
     $scope.$apply(function() { 

       if (fragment.haslocalaccount == 'False') { 

         authenticationService.logOut(); 

         authenticationService.externalAuthData = { 
           provider: fragment.provider, 
           userName: fragment.external_user_name, 
           externalAccessToken: fragment.external_access_token 
         }; 

         $location.path('/associate'); 

       } 
       else { 
         //Obtain access token and redirect to orders 
         var externalData = { provider: fragment.provider, externalAccessToken: fragment.external_access_token }; 
         authenticationService.obtainAccessToken(externalData).then(function (response) { 

           $location.path('/home'); 

         }, 
       function (err) { 
         $scope.message = err.error_description; 
       }); 
       } 

     }); 
} 

init(); 

function parseQueryString(queryString) { 
     var data = {}, 
       pairs, pair, separatorIndex, escapedKey, escapedValue, key, value; 

     if (queryString === null) { 
       return data; 
     } 

     pairs = queryString.split("&"); 

     for (var i = 0; i < pairs.length; i++) { 
       pair = pairs[i]; 
       separatorIndex = pair.indexOf("="); 

       if (separatorIndex === -1) { 
         escapedKey = pair; 
         escapedValue = null; 
       } else { 
         escapedKey = pair.substr(0, separatorIndex); 
         escapedValue = pair.substr(separatorIndex + 1); 
       } 

       key = decodeURIComponent(escapedKey); 
       value = decodeURIComponent(escapedValue); 

       data[key] = value; 
     } 

     return data; 
} 

function init() { 
     var idx = window.location.hash.indexOf("ext="); 

     if (window.location.hash.indexOf("#") === 0) { 
       fragment = parseQueryString(window.location.hash.substr(idx)); 
       vm.testFn(fragment); 
     } 
} 

लेकिन स्पष्ट रूप से:

common.getFragment = function getFragment() { 
     if (window.location.hash.indexOf("#") === 0) { 
       var hash = window.location.hash.substr(1); 
       var redirectUrl = location.protocol + '//' + location.host + '/#/login?ext=' + hash; 
       window.location = redirectUrl; 
     } else { 
       return {}; 
     } 
}; 

और लॉगिन नियंत्रक में, मैं क्वेरी स्ट्रिंग पार्स और जैसे $ scope.authCompletedCB (टुकड़ा) फ़ंक्शन को कॉल करने के लिए प्रयास करने के लिए एक समारोह जोड़ा यह मेरे कोणीय (जो मैं पल में कोई सुराग नहीं है) से संबंधित एक त्रुटि दे रहा है:

https://docs.angularjs.org/error/$rootScope/inprog?p0=$digest

तो, काफी यह मेरे लिए एक मृत अंत है इस स्तर पर।

किसी भी विचार या इनपुट की अत्यधिक सराहना की जाएगी।

Gracias!

अद्यतन: मैंने जड़ें फेंकने के बारे में कोणीय त्रुटि को हल करने में कामयाब रहे, लेकिन दुख की बात है कि हल करना मुख्य मुद्दा को ठीक नहीं करता है। यदि मैंने उसी ब्राउज़र टैब पर सोशल लॉगिन खोलने का प्रयास किया जहां मेरा आवेदन है, तो Google लॉगिन कर सकता है और एप्लिकेशन पर वापस जा सकता है और आवश्यक टोकन पास कर सकता है। यह फेसबुक के लिए एक अलग कहानी है, जहां डेवलपर के टूल कंसोल में, एक चेतावनी है जो फेसबुक को लॉगिन पेज प्रदर्शित करने से रोकती है।

बहुत अधिक, मूल विधि जिसके साथ एक नई विंडो (या टैब) खोला गया है, आगे बढ़ने का तरीका है लेकिन मोबाइल ब्राउज़र के लिए इसे ठीक करना अधिक चुनौतीपूर्ण प्रतीत होता है।

उत्तर

4

डेस्कटॉप पर, जब ऑथ विंडो पॉप अप हो जाती है (टैब नहीं) तो opener उस विंडो पर सेट की गई है जो मोबाइल पर इस पॉप अप विंडो को खोला गया है, जैसा कि आपने कहा था, यह पॉप अप विंडो नहीं बल्कि एक नया टैब है । जब एक नया टैब ब्राउज़र में खोला जाता है, opener संपत्ति null तो वास्तव में आप यहाँ एक अपवाद है:

window.opener.$windowScope.authCompletedCB

क्योंकि आप शून्य मान (window.opener) की $windowScope संपत्ति का उल्लेख नहीं कर सकते हैं तो हर कोड के बाद इस लाइन को निष्पादित नहीं किया जाएगा - यही कारण है कि खिड़की मोबाइल पर बंद नहीं है।

एक समाधान

अपने authComplete.js फ़ाइल में window.opener.$windowScope.authCompletedCB फोन और उपयोगकर्ता के टुकड़ा गुजरती हैं, टुकड़ा authComplete.html है सब पर पेज के बाद (localStorage में या एक कुकी में बचाने की कोशिश करने के बजाय आपके आवेदन के समान मूल में) JSON.stringify() का उपयोग करके और window.close() का उपयोग करके विंडो बंद करें।

loginController.js में, localStorage में या एक कुकी में एक मूल्य के लिए जाँच करने के लिए 100ms की तरह कुछ के लिए एक $interval बनाने (अंतराल स्पष्ट करने के लिए मत भूलना जब $scope$destroy है), यदि afragment आप अपने पार्स कर सकते हैं मौजूद स्टोरेज से JSON.parse का उपयोग करके मूल्य, इसे स्टोरेज से हटा दें और पार्स किए गए मान के साथ $scope.authCompletedCB पर कॉल करें।

अद्यतन - जोड़ा कोड नमूने

authComplete.js

... 
var fragment = common.getFragment(); 
// window.location.hash = fragment.state || ''; 
// window.opener.$windowScope.authCompletedCB(fragment); 
localStorage.setItem("auth_fragment", JSON.stringify(fragment)) 
window.close(); 

loginController.js

app.controller('loginController', ['$scope', '$interval', '$location', 'authService', 'ngAuthSettings', 
function ($scope, $interval, $location, authService, ngAuthSettings) { 

    ... 

    // check for fragment every 100ms 
    var _interval = $interval(_checkForFragment, 100); 

    function _checkForFragment() { 
     var fragment = localStorage.getItem("auth_fragment"); 
     if(fragment && (fragment = JSON.parse(fragment))) { 

      // clear the fragment from the storage 
      localStorage.removeItem("auth_fragment"); 

      // continue as usual 
      $scope.authCompletedCB(fragment); 

      // stop looking for fragmet 
      _clearInterval(); 
     } 
    } 

    function _clearInterval() { 
     $interval.cancel(_interval); 
    } 

    $scope.$on("$destroy", function() { 
     // clear the interval when $scope is destroyed 
     _clearInterval(); 
    }); 

}]); 
+0

मैं समय लेने के लिए इस दिन पर बाद में परीक्षण करने के लिए होगा । – Batuta

+0

आपको ब्राउजर में एचटीएमएल 5 एपीआई का हिस्सा नहीं है, वास्तव में यह 'विंडो' पर है, आप इसे 'window.localStorage' – udidu

+0

' का उपयोग करके कहीं से भी एक्सेस कर सकते हैं। कुछ जड़कोप त्रुटि हो रही है त्रुटि: $ rootScope: inprog प्रगति में पहले से ही क्रिया – Batuta

संबंधित मुद्दे