Добавлено обновление jwt при входи в систему

This commit is contained in:
FrigaT
2026-04-22 09:28:16 +03:00
parent e2e117a539
commit c32eee0954

View File

@@ -12,6 +12,8 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
private readonly HttpClient _http; private readonly HttpClient _http;
private Timer? _refreshTimer; private Timer? _refreshTimer;
private ClaimsPrincipal _currentUser = new(new ClaimsIdentity()); private ClaimsPrincipal _currentUser = new(new ClaimsIdentity());
private string? _currentToken;
private string? _currentRefreshToken;
public AuthStateProvider(TokenStorage tokenStorage, ApiClient apiClient, HttpClient http) public AuthStateProvider(TokenStorage tokenStorage, ApiClient apiClient, HttpClient http)
{ {
@@ -26,21 +28,68 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
return new AuthenticationState(_currentUser); return new AuthenticationState(_currentUser);
var principal = ParseToken(token); var (isValid, principal) = await ValidateTokenAsync(token);
if (principal == null) if (isValid && principal != null)
return new AuthenticationState(_currentUser); {
_currentUser = principal; _currentUser = principal;
_currentToken = token;
_currentRefreshToken = refreshToken;
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
ScheduleTokenRefresh(token, refreshToken); ScheduleTokenRefresh(token, refreshToken);
return new AuthenticationState(principal); 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) public async Task MarkUserAsAuthenticated(string token, string refreshToken)
{ {
await _tokenStorage.SetTokensAsync(token, refreshToken); await _tokenStorage.SetTokensAsync(token, refreshToken);
var principal = ParseToken(token); var principal = ParseToken(token);
_currentUser = principal ?? new ClaimsPrincipal(new ClaimsIdentity()); _currentUser = principal ?? new ClaimsPrincipal(new ClaimsIdentity());
_currentToken = token;
_currentRefreshToken = refreshToken;
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
} }
@@ -49,10 +98,51 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
{ {
await _tokenStorage.ClearTokensAsync(); await _tokenStorage.ClearTokensAsync();
_currentUser = new ClaimsPrincipal(new ClaimsIdentity()); _currentUser = new ClaimsPrincipal(new ClaimsIdentity());
_currentToken = null;
_currentRefreshToken = null;
_http.DefaultRequestHeaders.Authorization = null; _http.DefaultRequestHeaders.Authorization = null;
_refreshTimer?.Dispose();
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); 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) private ClaimsPrincipal? ParseToken(string token)
{ {
try try
@@ -62,7 +152,7 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
var identity = new ClaimsIdentity(jwt.Claims, "jwt"); var identity = new ClaimsIdentity(jwt.Claims, "jwt");
return new ClaimsPrincipal(identity); return new ClaimsPrincipal(identity);
} }
catch (Exception ex) catch
{ {
return null; return null;
} }
@@ -81,18 +171,7 @@ public class AuthStateProvider : AuthenticationStateProvider, IDisposable
_refreshTimer?.Dispose(); _refreshTimer?.Dispose();
_refreshTimer = new Timer(async _ => _refreshTimer = new Timer(async _ =>
{ {
try await TryRefreshTokenAsync();
{
var newToken = await _apiClient.RefreshTokenAsync(refreshToken);
if (newToken != null)
await MarkUserAsAuthenticated(newToken.Token, newToken.RefreshToken);
else
await MarkUserAsLoggedOut();
}
catch
{
await MarkUserAsLoggedOut();
}
}, null, (int)refreshTime.TotalMilliseconds, Timeout.Infinite); }, null, (int)refreshTime.TotalMilliseconds, Timeout.Infinite);
} }
} }