Sushant Sonarghare
Sushant Sonarghare

Reputation: 61

Azure - AD - AcquireTokenSilent giving error failed_to_acquire_token_silently

We are using Azure AD to authenticate and get the refreshed access token every 30 mins. We invoke below method which acquires security token and add it to request header.

var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectId));
var credential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientId"],
ConfigurationManager.AppSettings["ida:ClientSecret"]);

    try
    {
    var authenticationResult = authContext.AcquireTokenSilent(ConfigurationManager.AppSettings["WebAPIBaseAddress"], credential, new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
    //set cookie for azure oauth refresh token - on successful login
    var httpCookie = HttpContext.Current.Response.Cookies["RefreshToken"];
    if (httpCookie != null)
        httpCookie.Value = authenticationResult.RefreshToken;

    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
    }
    catch
    {
    //Get access token using Refresh Token 
    var authenticationResult = authContext.AcquireTokenByRefreshToken(httpCookie.Value, credential, ConfigurationManager.AppSettings["WebAPIBaseAddress"]);
    }

In above method, we have used AcquireTokenSilent method which gives us access token. Since access token lasts only for certain period of time. After its expiry, we call AcquireTokenByRefreshToken to get refresh token.

The above code works well, however we are getting below exception randomly:

Microsoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException: Failed to acquire token silently. Call method AcquireToken 
   at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenSilentHandler.SendTokenRequestAsync() 
   at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase.<RunAsync>d__0.MoveNext()
ErrorCode: failed_to_acquire_token_silently

What could be the reason of such inconsistent behaviour? The same code is working on few environments (Stage/Dev) but its throwing error randomly on Production.

Please suggest.

Upvotes: 3

Views: 7974

Answers (2)

Sushant Sonarghare
Sushant Sonarghare

Reputation: 61

We were able to resolve this. It seems to be a small mistake in the code itself. When the AccessToken expires, it throws an exception and it tries to fetch a new one using AcquireTokenByRefreshToken in the catch block. Here we were not setting the newly received refresh token back in the Cookie. We need to add below statement in the catch block also, so that it would get the Refresh token, which can then be passed back to generate a new Access Token.

httpCookie.Value = authenticationResult.RefreshToken;

Upvotes: 2

andrew.fox
andrew.fox

Reputation: 7933

First of all, before using AcquireTokenSilent you must invoke AcquireTokenByAuthorizationCodeAsync.

var context = new AuthenticationContext(authorityUri);
var credential = new ClientCredential(clientId, clientSecretKey);

await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);

AcquireTokenByAuthorizationCodeAsync stores access token and refresh token in TokenCache.DefaultShared (for user uniqueId received from auth procedure).

Assuming you do that, access tokens and refresh tokens do expire. If that happens, you must catch AdalSilentTokenAcquisitionException exception:

try
{
    // currentUser = new UserIdentifier() for: ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
    AuthenticationResult authResult = await context.AcquireTokenSilentAsync(resourceUri, credential, currentUser);

    return authResult.AccessToken;
}
catch (AdalSilentTokenAcquisitionException)
{
    return null;
}

Invoke this method before every request to the resource. It doesn't cost much, ideally nothing, or oauth API hit with refreshToken.

But when AdalSilentTokenAcquisitionException is thrown (or it's a first call). You must call procedure that performs full access code retrieval from oauth API. What procedure? It depends on the type of auth process you're using.

With full owin auth it can be:

  • redirect to authority uri with {"response_type", "code" }
  • or invoking HttpContext.GetOwinContext().Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType); (return null from controller's action as the Challenge() method alters HTTP response to force redirection to auth server). End processing of current request (with returning null). Auth server will invoke your authorization method (AuthorizationCodeReceived event from UseOpenIdConnectAuthentication's Notifications) with new authorization code. Later, redirect back to the origin page that needs the token.

So, you may get AdalSilentTokenAcquisitionException because cache is expired and refreshToken is expired. You have to reauthenticate again (it's transparent, no login page required).

Upvotes: 1

Related Questions