javascript – 移动浏览器上的AngularJS和ASP.Net WebAPI社交登录

前端之家收集整理的这篇文章主要介绍了javascript – 移动浏览器上的AngularJS和ASP.Net WebAPI社交登录前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在使用AngularJS和ASP.Net WebAPI关注社交登录这篇文章(非常好):

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

实际上,当您通过桌面浏览器(即Chrome,FF,IE,Edge)运行社交登录时,代码可以正常工作.社交登录在新窗口(不是标签)中打开,您可以使用您的Google或Facebook帐户,一旦您通过其中任何一个登录,您将被重定向到回调页面(authComplete.html),并且回调页面定义了一个JS文件(authComplete.js),该文件关闭窗口并在父窗口上执行命令.

angularJS控制器调用外部登录URL并在桌面浏览器上打开弹出窗口(而不是tab):

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

我遇到的问题是,当我在移动设备(Safari,Chrome for Mobile)上运行应用程序时,社交登录窗口将在新选项卡和JS功能中打开,该功能旨在将片段传回主应用程序窗口不执行nad新选项卡不关闭.

实际上,您可以通过应用程序在桌面和移动浏览器上尝试此行为:

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函数以返回登录页面,方法是将社交登录提供的访问令牌作为查询字符串附加:

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(fragment)函数,如:

var vm = this;
var fragment = null;

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

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

                        authenticationService.logout();

                        authenticationService.externalAuthData = {
                                provider: fragment.provider,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 = {},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);
        }
}

但显然这给了我一个与角度相关的错误(我现在还没有线索):

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

所以,在这个阶段,对我来说几乎是一个死胡同.

任何想法或意见将受到高度赞赏.

格拉西亚斯!

更新:我设法解决了关于rootcope被抛出的Angular错误,但遗憾的是,解决这个问题并不能解决主要问题.如果我尝试在我的应用程序所在的同一浏览器选项卡上打开社交登录,Google可以登录并返回应用程序并传递所需的令牌.对于Facebook而言,这是一个不同的故事,在开发人员的工具控制台中,有一个警告似乎阻止Facebook显示登录页面.

实际上,用于打开新窗口(或标签)的原始方法是前进的方法,但是为移动浏览器修复相同的方法似乎变得更具挑战性.

解决方法

在桌面上,当auth窗口弹出(而不是tab)时,它将opener属性设置为打开此弹出窗口的窗口,在移动设备上,如你所说,它不是弹出窗口而是新选项卡.当在浏览器中打开一个新选项卡时,opener属性为null,所以实际上你有一个例外:

window.opener.$windowScope.authCompletedCB

因为你不能引用null值(window.opener)的$windowScope属性所以在这之后的每一行代码都不会被执行 – 这就是为什么窗口没有在移动设备上关闭.

一个办法

在您的authComplete.js文件中,而不是尝试调用
window.opener.$windowScope.authCompletedCB并传递用户的片段,使用JSON.stringify()将片段保存在localStorage或cookie中(在authComplete.html的所有页面与应用程序的源相同之后)然后使用window.close()关闭窗口.

在loginController.js中,为100ms之类的内容创建一个$interval来检查localStorage或cookie中的值(当$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','$interval',$interval,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();
    });

}]);

猜你在找的JavaScript相关文章