См. ниже код, который решил эту проблему
Я пытаюсь найти лучший и наиболее эффективный способ справиться с токеном обновления, срок действия которого истек в ASP.NET Core 2.1.
Позвольте мне объяснить еще немного.
Я использую OAUTH2 и OIDC для запроса потоков предоставления кода авторизации (или гибридного потока с OIDC). Этот тип потока / гранта дает мне доступ к AccessToken и RefreshToken (также к коду авторизации, но это не для этого вопроса).
Маркер доступа и маркер обновления хранятся в ядре ASP.NET и могут быть получены с помощью HttpContext.GetTokenAsync("access_token");
и HttpContext.GetTokenAsync("refresh_token");
соответственно.
Я могу обновить access_token
без проблем. Проблема возникает, когда refresh_token
истек, аннулирован или недействителен каким-либо образом.
Правильный поток будет заключаться в том, чтобы пользователь вошел в систему и снова вернулся через весь поток аутентификации. Затем приложению возвращается новый набор токенов.
Мой вопрос в том, как я могу добиться этого наилучшим и наиболее правильным способом. Я решил написать специальное промежуточное ПО, которое пытается обновить access_token
, если срок его действия истек. Затем промежуточное программное обеспечение устанавливает новый токен в AuthenticationProperties
для HttpContext, чтобы его можно было использовать в любых последующих вызовах по конвейеру.
Если по какой-либо причине обновить токен не удается, мне нужно снова вызвать ChallengeAsync. Я вызываю ChallengeAsync из промежуточного программного обеспечения.
Здесь я столкнулся с интересным поведением. В большинстве случаев это работает, однако иногда я получаю 500 ошибок без полезной информации о том, что именно не удается. Похоже, что у промежуточного программного обеспечения возникают проблемы с попыткой вызвать ChallengeAsync из промежуточного программного обеспечения, и, возможно, другое промежуточное программное обеспечение также пытается получить доступ к контексту.
Я не совсем понимаю, что происходит. Я не совсем уверен, правильное ли это место для размещения этой логики. Возможно, мне не следовало бы иметь это в промежуточном программном обеспечении, может быть, где-то еще. Может быть, Polly для HttpClient - лучшее место.
Я открыт для любых идей.
Спасибо за любую помощь, которую вы можете предоставить.
Кодовое решение, которое у меня сработало
Спасибо Микаэлю Дерри за помощь и направление (не забудьте увидеть его ответ для получения дополнительной информации в контекст этого решения). Это решение, которое я придумал, и оно работает для меня:
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = context =>
{
//check to see if user is authenticated first
if (context.Principal.Identity.IsAuthenticated)
{
//get the user's tokens
var tokens = context.Properties.GetTokens();
var refreshToken = tokens.FirstOrDefault(t => t.Name == "refresh_token");
var accessToken = tokens.FirstOrDefault(t => t.Name == "access_token");
var exp = tokens.FirstOrDefault(t => t.Name == "expires_at");
var expires = DateTime.Parse(exp.Value);
//check to see if the token has expired
if (expires < DateTime.Now)
{
//token is expired, let's attempt to renew
var tokenEndpoint = "https://token.endpoint.server";
var tokenClient = new TokenClient(tokenEndpoint, clientId, clientSecret);
var tokenResponse = tokenClient.RequestRefreshTokenAsync(refreshToken.Value).Result;
//check for error while renewing - any error will trigger a new login.
if (tokenResponse.IsError)
{
//reject Principal
context.RejectPrincipal();
return Task.CompletedTask;
}
//set new token values
refreshToken.Value = tokenResponse.RefreshToken;
accessToken.Value = tokenResponse.AccessToken;
//set new expiration date
var newExpires = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
exp.Value = newExpires.ToString("o", CultureInfo.InvariantCulture);
//set tokens in auth properties
context.Properties.StoreTokens(tokens);
//trigger context to renew cookie with new token values
context.ShouldRenew = true;
return Task.CompletedTask;
}
}
return Task.CompletedTask;
}
};