Добавлено обновление jwt при входи в систему
This commit is contained in:
@@ -12,6 +12,8 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
|
||||
private readonly HttpClient _http;
|
||||
private Timer? _refreshTimer;
|
||||
private ClaimsPrincipal _currentUser = new(new ClaimsIdentity());
|
||||
private string? _currentToken;
|
||||
private string? _currentRefreshToken;
|
||||
|
||||
public AuthStateProvider(TokenStorage tokenStorage, ApiClient apiClient, HttpClient http)
|
||||
{
|
||||
@@ -26,21 +28,68 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
|
||||
if (string.IsNullOrEmpty(token))
|
||||
return new AuthenticationState(_currentUser);
|
||||
|
||||
var principal = ParseToken(token);
|
||||
if (principal == null)
|
||||
return new AuthenticationState(_currentUser);
|
||||
|
||||
var (isValid, principal) = await ValidateTokenAsync(token);
|
||||
if (isValid && principal != null)
|
||||
{
|
||||
_currentUser = principal;
|
||||
_currentToken = token;
|
||||
_currentRefreshToken = refreshToken;
|
||||
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
ScheduleTokenRefresh(token, refreshToken);
|
||||
return new AuthenticationState(principal);
|
||||
}
|
||||
|
||||
// Токен невалиден – пробуем обновить
|
||||
if (!string.IsNullOrEmpty(refreshToken))
|
||||
{
|
||||
var newTokenResponse = await _apiClient.RefreshTokenAsync(refreshToken);
|
||||
if (newTokenResponse != null && !string.IsNullOrEmpty(newTokenResponse.Token))
|
||||
{
|
||||
await MarkUserAsAuthenticated(newTokenResponse.Token, newTokenResponse.RefreshToken);
|
||||
// После MarkUserAsAuthenticated состояние обновится через NotifyAuthenticationStateChanged,
|
||||
// но текущий вызов всё равно должен вернуть нового пользователя
|
||||
var (newIsValid, newPrincipal) = await ValidateTokenAsync(newTokenResponse.Token);
|
||||
if (newIsValid && newPrincipal != null)
|
||||
return new AuthenticationState(newPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
// Всё плохо — логаут
|
||||
await MarkUserAsLoggedOut();
|
||||
return new AuthenticationState(_currentUser);
|
||||
}
|
||||
|
||||
// Вспомогательный метод проверки валидности токена (включая срок)
|
||||
private async Task<(bool IsValid, ClaimsPrincipal? Principal)> ValidateTokenAsync(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwt = handler.ReadJwtToken(token);
|
||||
|
||||
// Проверяем, не истёк ли токен
|
||||
if (jwt.ValidTo < DateTime.UtcNow)
|
||||
{
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
var identity = new ClaimsIdentity(jwt.Claims, "jwt");
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
return (true, principal);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return (false, null);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MarkUserAsAuthenticated(string token, string refreshToken)
|
||||
{
|
||||
await _tokenStorage.SetTokensAsync(token, refreshToken);
|
||||
var principal = ParseToken(token);
|
||||
_currentUser = principal ?? new ClaimsPrincipal(new ClaimsIdentity());
|
||||
_currentToken = token;
|
||||
_currentRefreshToken = refreshToken;
|
||||
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
@@ -49,10 +98,51 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
|
||||
{
|
||||
await _tokenStorage.ClearTokensAsync();
|
||||
_currentUser = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
_currentToken = null;
|
||||
_currentRefreshToken = null;
|
||||
_http.DefaultRequestHeaders.Authorization = null;
|
||||
_refreshTimer?.Dispose();
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
|
||||
public async Task<string?> TryRefreshTokenAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_currentRefreshToken))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var newToken = await _apiClient.RefreshTokenAsync(_currentRefreshToken);
|
||||
if (newToken != null && !string.IsNullOrEmpty(newToken.Token))
|
||||
{
|
||||
await MarkUserAsAuthenticated(newToken.Token, newToken.RefreshToken);
|
||||
return newToken.Token;
|
||||
}
|
||||
else
|
||||
{
|
||||
await MarkUserAsLoggedOut();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MarkUserAsLoggedOut();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTokenExpiringSoon()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_currentToken))
|
||||
return false;
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwt = handler.ReadJwtToken(_currentToken);
|
||||
var expiresAt = jwt.ValidTo;
|
||||
var timeToExpiry = expiresAt - DateTime.UtcNow;
|
||||
return timeToExpiry < TimeSpan.FromMinutes(1);
|
||||
}
|
||||
|
||||
private ClaimsPrincipal? ParseToken(string token)
|
||||
{
|
||||
try
|
||||
@@ -62,7 +152,7 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
|
||||
var identity = new ClaimsIdentity(jwt.Claims, "jwt");
|
||||
return new ClaimsPrincipal(identity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -81,18 +171,7 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
|
||||
_refreshTimer?.Dispose();
|
||||
_refreshTimer = new Timer(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var newToken = await _apiClient.RefreshTokenAsync(refreshToken);
|
||||
if (newToken != null)
|
||||
await MarkUserAsAuthenticated(newToken.Token, newToken.RefreshToken);
|
||||
else
|
||||
await MarkUserAsLoggedOut();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MarkUserAsLoggedOut();
|
||||
}
|
||||
await TryRefreshTokenAsync();
|
||||
}, null, (int)refreshTime.TotalMilliseconds, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user