Azure - AD - AcquireTokenSilent给出错误failed_to_acquire_token_silently

问题描述 投票:3回答:2

我们使用Azure AD进行身份验证,并每隔30分钟获取刷新的访问令牌。我们调用以下方法获取安全令牌并将其添加到请求头。

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"]);
    }

在上面的方法中,我们使用了AcquireTokenSilent方法,它为我们提供了访问令牌。由于访问令牌仅持续一段时间。到期后,我们调用AcquireTokenByRefreshToken来获取刷新令牌。

上面的代码效果很好,但我们随机地得到以下异常:

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

这种不一致的行为可能是什么原因?相同的代码在少数环境(Stage / Dev)上工作,但它在Production上随机抛出错误。

请建议。

c# azure azure-active-directory adal
2个回答
2
投票

我们能够解决这个问题。这似乎是代码本身的一个小错误。当AccessToken到期时,它会抛出异常并尝试使用catch块中的AcquireTokenByRefreshToken获取新的异常。这里我们没有在Cookie中设置新接收的刷新令牌。我们还需要在catch块中添加以下语句,以便它获得Refresh标记,然后可以将其传递回来生成新的Access Token。

httpCookie.Value = authenticationResult.RefreshToken;

1
投票

首先,在使用AcquireTokenSilent之前,你必须调用AcquireTokenByAuthorizationCodeAsync

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

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

AcquireTokenByAuthorizationCodeAsyncTokenCache.DefaultShared中存储访问令牌和刷新令牌(对于从auth过程接收的用户uniqueId)。

假设您这样做,访问令牌和刷新令牌确实会过期。如果发生这种情况,你必须抓住AdalSilentTokenAcquisitionException例外:

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;
}

在每次请求资源之前调用此方法。它没有花费太多,理想情况下没有,或者使用refreshToken命中oauth API。

但是当AdalSilentTokenAcquisitionException被抛出时(或者它是第一次召唤)。您必须调用从oauth API执行完全访问代码检索的过程。什么程序?这取决于您正在使用的身份验证过程的类型。

使用完整的owin auth,它可以是:

  • {"response_type", "code" }重定向到权威uri
  • 或调用HttpContext.GetOwinContext().Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);(从控制器的操作返回null,因为Challenge()方法改变HTTP响应以强制重定向到auth服务器)。结束当前请求的处理(返回null)。 Auth服务器将使用新的授权代码调用您的授权方法(来自UseOpenIdConnectAuthentication的通知的AuthorizationCodeReceived事件)。稍后,重定向回需要令牌的原始页面。

因此,您可能会收到AdalSilentTokenAcquisitionException,因为缓存已过期且refreshToken已过期。您必须重新进行身份验证(它是透明的,不需要登录页面)。

© www.soinside.com 2019 - 2024. All rights reserved.