2016-01-14 16 views
7

tôi sau bài viết này trên Logins xã hội với AngularJS và ASP.Net WebAPI (đó là khá tốt):AngularJS và ASP.Net WebAPI Đăng nhập xã hội trên một trình duyệt di động

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

Khá nhiều, mã hoạt động tốt khi bạn đang chạy đăng nhập xã hội thông qua trình duyệt trên máy tính để bàn (ví dụ: Chrome, FF, IE, Edge). Đăng nhập xã hội mở trong cửa sổ mới (không phải tab) và bạn có thể sử dụng tài khoản Google hoặc Facebook của mình và sau khi đăng nhập qua bất kỳ tài khoản nào, bạn được chuyển hướng đến trang gọi lại (authComplete.html) và trang gọi lại có tệp JS được định nghĩa (authComplete.js) sẽ đóng cửa sổ và thực hiện lệnh trên cửa sổ chính.

bộ điều khiển angularJS trong đó kêu gọi url đăng nhập bên ngoài và mở ra một cửa sổ popup (không tab) trên các trình duyệt máy tính để bàn:

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(); 

Vấn đề tôi đang gặp là khi tôi chạy các ứng dụng trên thiết bị di động (Safari, Chrome for Mobile), cửa sổ đăng nhập xã hội sẽ mở trong một tab mới và chức năng JS mà được dự định để vượt qua trở lại mảnh vỡ đến cửa sổ ứng dụng chính không thực hiện nad tab mới không đóng.

Bạn thực sự có thể thử hành vi này trên cả hai máy tính để bàn và trình duyệt di động thông qua ứng dụng:

http://ngauthenticationapi.azurewebsites.net/

Những gì tôi đã cố gắng cho đến nay trong bối cảnh này là trong bộ điều khiển đăng nhập, tôi sửa đổi chức năng để rằng url đăng nhập bên ngoài mở ra trong cùng một cửa sổ:

$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; 
}; 

Và sửa đổi chức năng authComplete.js common.getFragment để trở về trang đăng nhập, bằng cách thêm các thẻ truy cập được cung cấp bởi các xã hội đăng nhập như là chuỗi truy vấn:

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 {}; 
     } 
}; 

Và trong bộ điều khiển đăng nhập, tôi đã thêm một chức năng để phân tích các chuỗi truy vấn và cố gắng gọi $ scope.authCompletedCB (fragment) chức năng như:

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); 
     } 
} 

Nhưng rõ ràng này đem lại cho tôi một lỗi liên quan đến góc (mà tôi không có đầu mối tại thời điểm này):

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

vì vậy, khá nhiều đó là một ngõ cụt đối với tôi ở giai đoạn này.

Bất kỳ ý tưởng hoặc đầu vào nào sẽ được đánh giá cao.

Gracias!

Cập nhật: Tôi đã quản lý để giải quyết lỗi Góc về rootscope bị ném, nhưng thật đáng buồn, giải quyết vấn đề đó không khắc phục được vấn đề chính. Nếu tôi cố mở thông tin đăng nhập xã hội trên cùng một tab trình duyệt nơi ứng dụng của tôi, Google có thể đăng nhập và quay lại ứng dụng và chuyển mã thông báo bắt buộc. Đó là một câu chuyện khác cho Facebook, nơi trong bảng điều khiển công cụ của nhà phát triển, có một cảnh báo dường như ngăn Facebook hiển thị trang đăng nhập.

Khá nhiều, phương pháp ban đầu mà cửa sổ mới (hoặc tab) được mở là con đường phía trước nhưng việc sửa lỗi tương tự cho trình duyệt di động dường như đang trở nên khó khăn hơn.

Trả lời

4

Trên máy tính để bàn, khi cửa sổ xác thực bật lên (không phải tab) nó có thuộc tính opener được đặt thành cửa sổ mở cửa sổ bật lên này, trên thiết bị di động, như bạn đã nói, không phải cửa sổ bật lên mà là tab mới . khi một tab mới được mở ra trong trình duyệt, các opener tài sản là null nên thực sự bạn có một ngoại lệ ở đây:

window.opener.$windowScope.authCompletedCB

bởi vì bạn không có thể tham khảo các $windowScope tài sản giá trị null (window.opener) như vậy mỗi dòng mã sau khi mã này sẽ không được thực hiện - đó là lý do tại sao cửa sổ không đóng trên thiết bị di động.

Một giải pháp

Trong file authComplete.js của bạn, thay vì cố gắng gọi window.opener.$windowScope.authCompletedCB và vượt qua đoạn của người sử dụng, lưu đoạn trong localStorage hoặc trong một cookie (sau khi tất cả trang tại authComplete.html là có cùng nguồn gốc với ứng dụng của bạn) sử dụng JSON.stringify() và chỉ cần đóng cửa sổ bằng cách sử dụng window.close().

Trong loginController.js, thực hiện một $interval cho một cái gì đó giống như 100ms để kiểm tra một giá trị trong localStorage hoặc trong một cookie (đừng quên để xóa khoảng cách khi $scope$destroy), nếu afragment tồn tại, bạn có thể phân tích cú pháp của nó giá trị sử dụng JSON.parse từ bộ nhớ, xóa bộ nhớ khỏi bộ nhớ và gọi $scope.authCompletedCB với giá trị được phân tích cú pháp.

CẬP NHẬT - Thêm mẫu mã

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

tôi sẽ dành thời gian để kiểm tra này ra sau đó vào ngày . – Batuta

+0

Bạn không cần, 'localStorage' là một phần của HTML5 API trong trình duyệt, trên thực tế là trên cửa sổ', bạn có thể truy cập nó từ bất cứ nơi nào bằng cách sử dụng 'window.localStorage' – udidu

+0

Chỉ cần thử điều này ngay bây giờ và tôi đang nhận được một số lỗi rootscope Lỗi: $ rootScope: inprog Hành động đã được tiến hành – Batuta

Các vấn đề liên quan