Azure - AD - AcquireTokenSilent выдавал ошибку failed_to_acquire_token_silently
Мы используем 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.
Пожалуйста, предложите.
2 ответа
Мы смогли решить это. Кажется, это небольшая ошибка в самом коде. Когда срок действия AccessToken истекает, он генерирует исключение и пытается извлечь новое, используя AcquireTokenByRefreshToken в блоке catch. Здесь мы не устанавливали вновь полученный токен обновления в Cookie. Нам также нужно добавить нижеприведенный оператор в блок catch, чтобы он получал токен Refresh, который затем можно было бы передать обратно для генерации нового токена доступа.
httpCookie.Value = authenticationResult.RefreshToken;
Прежде всего, перед использованием 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;
}
Вызывайте этот метод перед каждым запросом к ресурсу. Это не будет стоить дорого, в идеале ничего, или просто поразить API с помощью refreshToken.
Но когда AdalSilentTokenAcquisitionException
брошен (или это первый звонок). Вы должны вызвать процедуру, которая выполняет поиск кода полного доступа из oauth API. Какая процедура? Это зависит от типа используемого вами процесса аутентификации.
При полной аутентификации это может быть:
- перенаправить к авторитетному URI с
{"response_type", "code" }
- или ссылаясь
HttpContext.GetOwinContext().Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);
(вернуть ноль из действия контроллера какChallenge()
метод изменяет HTTP-ответ для принудительного перенаправления на сервер авторизации). Завершить обработку текущего запроса (с возвратом нулевого значения). Сервер аутентификации вызовет ваш метод авторизации (событие AuthorizationCodeReceived из уведомлений UseOpenIdConnectAuthentication) с новым кодом авторизации. Позже, перенаправьте обратно на исходную страницу, которая нуждается в токене.
Таким образом, вы можете получить AdalSilentTokenAcquisitionException, поскольку срок действия кэша истек, а refreshToken истек. Вы должны повторно подтвердить подлинность (это прозрачно, страница входа не требуется).