使用Azure AD时处理Asp.Net MVC中的令牌超时

[英]Handle token timeout in Asp.Net MVC when using Azure AD


This is more of a design/approach question...

这更像是一个设计/方法问题......

I think I'm missing something here. We're building an Asp.Net MVC 5 web application and securing it with Azure AD using the following scenario:

我想我在这里遗漏了一些东西。我们正在构建一个Asp.Net MVC 5 Web应用程序,并使用以下方案使用Azure AD保护它:

https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-scenarios/#web-browser-to-web-application

https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-scenarios/#web-browser-to-web-application

https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect

https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect

The token/cookie is an absolute expiry and expires after one hour. So what does that do for the user experience? Every hour they have to log back in no matter what? In our testing, when the user expires, the browser is redirected back to AD and the user prompted for credentials. This, of course, breaks any AJAX calls we have loading partial views and none of our DevExpress controls are stable as a result.

令牌/ cookie是绝对到期,并在一小时后过期。那么这对用户体验有什么影响呢?无论如何,他们每小时都必须重新登录?在我们的测试中,当用户过期时,浏览器将重定向回AD,并提示用户输入凭据。当然,这会破坏我们加载部分视图的任何AJAX调用,因此我们的DevExpress控件都不会稳定。

Based on the response to this SO post: MVC AD Azure Refresh Token via ADAL JavaScript Ajax and KnockoutJs

基于对此SO帖子的响应:通过ADAL JavaScript Ajax和KnockoutJs的MVC AD Azure刷新令牌

...what I'm seeing is expected? It seems to me like not a very viable solution for a cloud hosted line-of-business application where users are logged in and working all day.

......我所看到的是预期的?在我看来,对于用户登录并全天工作的云托管业务线应用程序而言,这似乎不是一个非常可行的解决方案。

Am I missing something? Or is this just not an ideal scenario for business apps?

我错过了什么吗?或者这不是商业应用的理想场景?

2 个解决方案

#1


6  

We faced a similar set of problems, as well as the same thoughts about how you could use Azure AD with ASP.NET MVC in web apps with such a low session timeout (60 minutes).

我们遇到了类似的问题,以及关于如何在具有如此低的会话超时(60分钟)的Web应用程序中使用Azure MV和ASP.NET MVC的相同想法。

The solution we came up with, that seems to be working (albeit with limited testing), is to have an iFrame on the page that we refresh every 5 minutes.

我们提出的解决方案似乎是有效的(虽然测试有限),是在页面上有一个iFrame,我们每隔5分钟刷新一次。

<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms" id="refreshAuthenticationIframe" src="@Url.Action("CheckSessionTimeout", "Home", new { area = "" })" style="display:none;"></iframe>

The "CheckSessionTimeout" page is basically blank.

“CheckSessionTimeout”页面基本上是空白的。

In a Javascript file referenced by the whole app, we have:

在整个应用程序引用的Javascript文件中,我们有:

var pageLoadTime = moment();

setInterval(refreshAuthenticationCookies, 1000);

function refreshAuthenticationCookies() {
    if (moment().diff(pageLoadTime, "seconds") > 300) {
        document.getElementById("refreshAuthenticationIframe").contentDocument.location = "/Home/ForceSessionRefresh";
        pageLoadTime = moment();
    }
}

(NB: moment is a JS date/time library we use). On the Home controller, we have:

(注意:时刻是我们使用的JS日期/时间库)。在Home控制器上,我们有:

    public ActionResult CheckSessionTimeout() => View();

    public ActionResult ForceSessionRefresh()
    {
        HttpContext.GetOwinContext()
               .Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/Home/CheckSessiontimeout" },
                   OpenIdConnectAuthenticationDefaults.AuthenticationType);

        return null;
    }

I am not sure if any of that is the best way/approach. It's just the best we can do to fix up what seems like a set of difficult constraints with Azure AD and ASP.NET MVC apps (that are not SPAs, not using Web API but are using Ajax calls), relative to where we are coming from where none of this matters with on-premises apps doing Kerberos auth (and our user's expectations that session timeout is nothing they want to see or worry about).

我不确定这是否是最好的方式/方法。这是我们能够做的最好的事情来修复Azure AD和ASP.NET MVC应用程序(不是SPA,不使用Web API但使用Ajax调用)的一系列困难约束,相对于我们即将到来的地方对于使用Kerberos身份验证的本地应用程序而言,这一点都不重要(我们的用户期望会话超时是他们想要看到或担心的)。

#2


1  

There are two ways to handle this (at least this is how we are doing it in our application; it would be interesting to see what AD gurus have to say about this so that we can also fix it if it is not the right way to to do things):

有两种方法可以处理这个问题(至少这是我们在应用程序中如何做到这一点;看看AD大师们对此有何看法会很有趣,这样我们也可以修复它,如果它不是正确的方法做事):

General Approach - Use Refresh Token

一般方法 - 使用刷新令牌

When you get an access token from AD, today you get 3 things back - access token, access token expiry and a refresh token. What you do is cache all three of them in your application. Till the time access token is expired, you can simply use that access token. Once the token is expired, you can make use of refresh token to get a new access token. The method in ADAL you want to use for this purpose is AcquireTokenByRefreshToken.

当您从AD获得访问令牌时,今天您将收到3件事 - 访问令牌,访问令牌到期和刷新令牌。你所做的就是在你的应用程序中缓存它们中的所有三个。直到访问令牌过期,您只需使用该访问令牌即可。令牌过期后,您可以使用刷新令牌获取新的访问令牌。您想要用于此目的的ADAL方法是AcquireTokenByRefreshToken。

Having said that, you should not take a hard dependency in your application on Refresh Token. Based on the best practices described here, a refresh token can expire or invalidated. Furthermore based on Vittorio's post, a refresh token is not even returned in ADAL version 3. So you may want to consider that.

话虽如此,你不应该在刷新令牌上对你的应用程序采取硬依赖。根据此处描述的最佳实践,刷新令牌可能会过期或失效。此外,基于Vittorio的帖子,在ADAL版本3中甚至没有返回刷新令牌。所以你可能想要考虑这一点。

Other Approach - Acquire Token Silently

其他方法 - 默默获取令牌

Other approach you could take is acquire a new token silently on behalf of the user once the token expires. I believe this requires that a user must sign in manually at least once in your application and follow the OAuth2 flow. The method you want to use is AcquireTokenSilent.

您可以采取的其他方法是在令牌过期后代表用户静默获取新令牌。我认为这要求用户必须在您的应用程序中至少手动登录一次并遵循OAuth2流程。您要使用的方法是AcquireTokenSilent。

Here's the pseudo code for our approach:

这是我们方法的伪代码:

                var now = DateTime.UtcNow.Ticks;
                if (now <= tokenExpiry && !string.IsNullOrWhiteSpace(accessToken)) return accessToken;
                var clientCredential = new ClientCredential(ClientId, ClientSecret);
                var authContext = new AuthenticationContext(string.Format("{0}/{1}", AzureActiveDirectorySignInEndpoint,
                            azureADTenantId));
                AuthenticationResult authResult = null;
                if (!string.IsNullOrWhiteSpace(refreshToken))
                {
                    authResult = await authContext.AcquireTokenByRefreshTokenAsync(refreshToken, clientCredential, ADEndpoint);
                }
                else
                {
authResult = await authContext.AcquireTokenSilentAsync(Endpoint, clientCredential, new UserIdentifier(userId, UserIdentifierType.UniqueId));                        
                }

                return authResult.AccessToken;//Also you may want to cache the token again

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:http://www.itdaan.com/blog/2015/11/01/d1543957454b5b953b5c07c7d9a15006.html



 
© 2014-2018 ITdaan.com 粤ICP备14056181号