Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

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

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.

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

...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?

like image 847
Jason P. Avatar asked Nov 01 '15 22:11

Jason P.


People also ask

How do I increase access token expiration time in Azure AD?

Refresh tokens expires in 14 days (see the refresh_token_expires_in attribute that is returned when acquiring an access token). Access tokens can be refreshed using the refresh-token for a maximum period of time of 90 days, from the date that the access token was acquired by prompting the user.

How long refresh token is valid for Azure AD?

The default lifetime for the refresh tokens is 24 hours for single page apps and 90 days for all other scenarios. Refresh tokens replace themselves with a fresh token upon every use.

How do I set session timeout in Azure?

Configure session timeoutSelect Settings > Product > Privacy + Security. Set Session Expiration and Inactivity timeout. These settings apply to all users.


1 Answers

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):

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.

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.

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.

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
like image 171
Gaurav Mantri Avatar answered Oct 08 '22 23:10

Gaurav Mantri