SPA中的ADAL JS强制退出

时间:2017-04-24 20:55:50

标签: javascript angularjs azure-active-directory adal

我编写了一个用Angular编写的SPA,并使用基于Azure AD令牌的身份验证,使用通过承载令牌保护的资源(如API)。这允许SPA从Azure检索续订令牌,并且据我所知,永远保持登录状态。

我正在使用Angular的ADAL JavaScript库来执行此操作:

https://github.com/AzureAD/azure-activedirectory-library-for-js

现在需要将用户会话限制在预定的时间内,比方说15个小时。

我写了一个很好的小服务,它在登录时存储一个cookie,每个请求都会检查cookie中的日期以及它们是否超过了它们的最大会话限制。如果用户不断使用应用程序或关闭浏览器,这样可以正常工作 - 但如果他们让浏览器保持打开状态,则令牌只会在后台续订并保持登录状态。

我现在正试图使用​​静默注销解决方案来解决这个问题。这意味着,我希望用户在会话过期后被强制进入安全登录页面。

这似乎是一种常见的情况,但我似乎无法弄清楚如何实现它,因为ADAL在后台使用了iFrame。我想过使用一个计时器/间隔,但这看起来很糟糕。

仅供参考我根据以下代码示例使用adalAuthenticationService.logout()。我也尝试清除会话缓存,但是ADAL会静默刷新令牌。我还尝试将redirectUri位置设置为非经过身份验证的页面,但是,如果用户采取操作,它只会重定向到那里。如果浏览器只是保持打开状态,则令牌将自行重置。

    var maxTime = 15; // hours allowed in session

    // event to fire check; maybe this can be different, and is my problem?
    $rootScope.$on('$viewContentLoaded', function () {
        $scope.checkLogoutCookie();
    });
    $scope.logout = function() {
        adalAuthenticationService.logout();
    };
    function setCookie(c) {} // implementation details don't matter....
    function getCookie(c) {} // implementation details don't matter....
    $scope.checkLogoutCookie = function () {
        var lastLogin = getCookie("lastLogin");
        var loginDate = new Date();
        if (lastLogin === "") { // is empty
            setCookie("lastLogin", loginDate, 365);
        } else {
            var lastDate = new Date(lastLogin);
            var hours = Math.abs(lastDate - loginDate) / 36e5;
            if (hours > maxTime) {
                setCookie("lastLogin", "", 0);
                $scope.logout();
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

  

我写了一个很好的小服务,它在登录时存储一个cookie,每个请求都会检查cookie中的日期以及它们是否超过了它们的最大会话限制。如果用户不断使用应用程序或关闭浏览器,这样可以正常工作 - 但如果他们让浏览器保持打开状态,则令牌只会在后台续订并保持登录状态。

根据描述,似乎代码检查日期是在adal-angular.js的HTTP请求拦截器之后执行的。

如果可能,您需要在ADAL库的拦截器之前实现此功能。

如果不可能,您可以将业务逻辑更改为 在获取令牌之前检查应用会话是否未过期。为此,我们需要更改JavaScript的Active Directory身份验证库(ADAL)的源代码。

例如,您可以更改用于插入代码的 adal-angular.js 的HTTP请求拦截器,以检查您的应用是否在会话中。以下是拦截器代码供您参考:

AdalModule.factory('ProtectedResourceInterceptor', ['adalAuthenticationService', '$q', '$rootScope', '$templateCache', function (authService, $q, $rootScope, $templateCache) {

    return {
        request: function (config) {
            if (config) {

                config.headers = config.headers || {};

                // if the request can be served via templateCache, no need to token
                if ($templateCache.get(config.url)) return config;

                var resource = authService.getResourceForEndpoint(config.url);
                authService.verbose('Url: ' + config.url + ' maps to resource: ' + resource);
                if (resource === null) {
                    return config;
                }
//add/modify the code here
                var tokenStored = authService.getCachedToken(resource);
                if (tokenStored) {
                    authService.info('Token is available for this url ' + config.url);
                    // check endpoint mapping if provided
                    config.headers.Authorization = 'Bearer ' + tokenStored;
                    return config;
                }
                else {
                    // Cancel request if login is starting
                    if (authService.loginInProgress()) {
                        if (authService.config.popUp) {
                            authService.info('Url: ' + config.url + ' will be loaded after login is successful');
                            var delayedRequest = $q.defer();
                            $rootScope.$on('adal:loginSuccess', function (event, token) {
                                if (token) {
                                    authService.info('Login completed, sending request for ' + config.url);
                                    config.headers.Authorization = 'Bearer ' + tokenStored;
                                    delayedRequest.resolve(config);
                                }
                            });
                            return delayedRequest.promise;
                        }
                        else {
                            authService.info('login is in progress.');
                            config.data = 'login in progress, cancelling the request for ' + config.url;
                            return $q.reject(config);
                        }
                    }
                    else {
                        // delayed request to return after iframe completes
                        var delayedRequest = $q.defer();
                        authService.acquireToken(resource).then(function (token) {
                            authService.verbose('Token is available');
                            config.headers.Authorization = 'Bearer ' + token;
                            delayedRequest.resolve(config);
                        }, function (error) {
                            config.data = error;
                            delayedRequest.reject(config);
                        });

                        return delayedRequest.promise;
                    }
                }
            }
        },
        responseError: function (rejection) {
            authService.info('Getting error in the response: ' + JSON.stringify(rejection));
            if (rejection) {
                if (rejection.status === 401) {
                    var resource = authService.getResourceForEndpoint(rejection.config.url);
                    authService.clearCacheForResource(resource);
                    $rootScope.$broadcast('adal:notAuthorized', rejection, resource);
                }
                else {
                    $rootScope.$broadcast('adal:errorResponse', rejection);
                }
                return $q.reject(rejection);
            }
        }
    };
}]);

答案 1 :(得分:1)

查看adalAuthenticationService的方法logOut()。我认为问题在于您错误地使用了logout()。请注意o方法中logout()的大写字母。