Azure - AD - AcquireTokenSilent 给出错误 failed_to_acquire_token_silently
Azure - AD - AcquireTokenSilent giving error failed_to_acquire_token_silently
我们正在使用 Azure AD 进行身份验证并每 30 分钟获取一次刷新的访问令牌。我们调用下面的方法获取安全令牌并将其添加到请求 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"]);
}
在上面的方法中,我们使用了 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),但它在生产环境中随机抛出错误。
求推荐。
首先,在使用 AcquireTokenSilent
之前,您必须调用 AcquireTokenByAuthorizationCodeAsync
。
var context = new AuthenticationContext(authorityUri);
var credential = new ClientCredential(clientId, clientSecretKey);
await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);
AcquireTokenByAuthorizationCodeAsync
将访问令牌和刷新令牌存储在 TokenCache.DefaultShared
中(对于从身份验证过程接收到的用户 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;
}
在每次请求资源之前调用此方法。它花费不多,理想情况下没有花费,或者 oauth API 使用 refreshToken 命中。
但是当 AdalSilentTokenAcquisitionException
被抛出时(或者是第一次调用)。您必须调用从 oauth API 执行完全访问代码检索的过程。什么程序?这取决于您使用的身份验证过程的类型。
使用完整的 owin 身份验证可以是:
- 使用
{"response_type", "code" }
重定向到权限 uri
- 或调用
HttpContext.GetOwinContext().Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);
(控制器操作中的 return 为空,因为 Challenge()
方法会更改 HTTP 响应以强制重定向到身份验证服务器)。结束当前请求的处理(returning null)。 Auth 服务器将使用新的授权代码调用您的授权方法(来自 UseOpenIdConnectAuthentication 的通知的 AuthorizationCodeReceived 事件)。稍后,重定向回需要令牌的原始页面。
因此,您可能会收到 AdalSilentTokenAcquisitionException,因为缓存已过期且 refreshToken 已过期。您必须重新进行身份验证(它是透明的,不需要登录页面)。
我们能够解决这个问题。这似乎是代码本身的一个小错误。
当 AccessToken 过期时,它会抛出一个异常,并尝试在 catch 块中使用 AcquireTokenByRefreshToken 获取一个新的异常。这里我们没有在 Cookie 中设置新收到的刷新令牌。
我们还需要在 catch 块中添加以下语句,以便它获得刷新令牌,然后可以将其传回以生成新的访问令牌。
httpCookie.Value = authenticationResult.RefreshToken;
我们正在使用 Azure AD 进行身份验证并每 30 分钟获取一次刷新的访问令牌。我们调用下面的方法获取安全令牌并将其添加到请求 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"]);
}
在上面的方法中,我们使用了 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),但它在生产环境中随机抛出错误。
求推荐。
首先,在使用 AcquireTokenSilent
之前,您必须调用 AcquireTokenByAuthorizationCodeAsync
。
var context = new AuthenticationContext(authorityUri);
var credential = new ClientCredential(clientId, clientSecretKey);
await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);
AcquireTokenByAuthorizationCodeAsync
将访问令牌和刷新令牌存储在 TokenCache.DefaultShared
中(对于从身份验证过程接收到的用户 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;
}
在每次请求资源之前调用此方法。它花费不多,理想情况下没有花费,或者 oauth API 使用 refreshToken 命中。
但是当 AdalSilentTokenAcquisitionException
被抛出时(或者是第一次调用)。您必须调用从 oauth API 执行完全访问代码检索的过程。什么程序?这取决于您使用的身份验证过程的类型。
使用完整的 owin 身份验证可以是:
- 使用
{"response_type", "code" }
重定向到权限 uri
- 或调用
HttpContext.GetOwinContext().Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);
(控制器操作中的 return 为空,因为Challenge()
方法会更改 HTTP 响应以强制重定向到身份验证服务器)。结束当前请求的处理(returning null)。 Auth 服务器将使用新的授权代码调用您的授权方法(来自 UseOpenIdConnectAuthentication 的通知的 AuthorizationCodeReceived 事件)。稍后,重定向回需要令牌的原始页面。
因此,您可能会收到 AdalSilentTokenAcquisitionException,因为缓存已过期且 refreshToken 已过期。您必须重新进行身份验证(它是透明的,不需要登录页面)。
我们能够解决这个问题。这似乎是代码本身的一个小错误。 当 AccessToken 过期时,它会抛出一个异常,并尝试在 catch 块中使用 AcquireTokenByRefreshToken 获取一个新的异常。这里我们没有在 Cookie 中设置新收到的刷新令牌。 我们还需要在 catch 块中添加以下语句,以便它获得刷新令牌,然后可以将其传回以生成新的访问令牌。
httpCookie.Value = authenticationResult.RefreshToken;