我们使用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)上工作,但它在生产中随机抛出错误。
请建议。
答案 0 :(得分:2)
我们能够解决这个问题。这似乎是代码本身的一个小错误。 当AccessToken到期时,它会抛出异常并尝试使用catch块中的AcquireTokenByRefreshToken获取新的异常。这里我们没有在Cookie中设置新接收的刷新令牌。 我们还需要在catch块中添加以下语句,以便它获取Refresh标记,然后可以将其传回以生成新的Access Token。
httpCookie.Value = authenticationResult.RefreshToken;
答案 1 :(得分:1)
首先,在使用AcquireTokenSilent
之前,您必须调用AcquireTokenByAuthorizationCodeAsync
。
var context = new AuthenticationContext(authorityUri);
var credential = new ClientCredential(clientId, clientSecretKey);
await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);
AcquireTokenByAuthorizationCodeAsync
在TokenCache.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" }
HttpContext.GetOwinContext().Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);
(从控制器的操作返回null,因为Challenge()
方法改变HTTP响应以强制重定向到auth服务器)。结束当前请求的处理(返回null)。 Auth服务器将使用新的授权代码调用您的授权方法(来自UseOpenIdConnectAuthentication的通知的AuthorizationCodeReceived事件)。稍后,重定向回需要令牌的原始页面。 因此,您可能会收到AdalSilentTokenAcquisitionException,因为缓存已过期且refreshToken已过期。您必须再次重新进行身份验证(它是透明的,不需要登录页面)。