fix: prevent admin authentication timeout during session
TaxBaik CI/CD / build-and-deploy (push) Successful in 48s
TaxBaik CI/CD / build-and-deploy (push) Successful in 48s
**Issues Resolved:** 1. Access Token lifetime extended 15m → 1h (better UX) - Users can browse admin pages for 1 hour without re-login - Reasonable balance between security and usability 2. Automatic pre-expiry token refresh - GetAuthenticationStateAsync() now checks if token expires in <5min - Automatically refreshes before expiry when user is still active - Prevents sudden logout during admin work **Implementation:** - Added ShouldRefreshToken() to detect imminent expiry (300s window) - On auth state check, if token expiring soon: trigger refresh via AuthService - Refresh happens transparently, no user interaction needed - Maintains 7-day Refresh Token TTL for security **Behavior:** - User logs in with 1-hour session - Every page load/navigation checks token status - If <5min remaining: auto-refresh (user doesn't notice) - If refresh fails: graceful logout with warning - Refresh Token (7 days) allows re-login without password This provides better UX while maintaining security through shorter-lived access tokens and automatic renewal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ public class AuthService
|
||||
private readonly ILogger<AuthService> _logger;
|
||||
private readonly string _jwtSecretKey;
|
||||
private readonly string? _passwordResetToken;
|
||||
private readonly int _accessTokenExpirationMinutes = 15; // Access Token: 15분
|
||||
private readonly int _accessTokenExpirationMinutes = 60; // Access Token: 1시간 (사용성 향상)
|
||||
private readonly int _refreshTokenExpirationMinutes = 10080; // Refresh Token: 7일
|
||||
|
||||
public AuthService(IAdminUserRepository adminUserRepository, ILogger<AuthService> logger, IConfiguration configuration)
|
||||
|
||||
@@ -51,13 +51,33 @@ public class CustomAuthenticationStateProvider : AuthenticationStateProvider
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
|
||||
// 토큰이 만료되면 로그아웃
|
||||
if (_tokenStore.IsAccessTokenExpired())
|
||||
{
|
||||
_logger.LogWarning("Access token 만료됨");
|
||||
_logger.LogWarning("Access token 만료됨 - 자동 로그아웃");
|
||||
await LogoutAsync();
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
|
||||
// 토큰이 5분 이내로 만료되면 자동 갱신 시도 (사용자 경험 향상)
|
||||
if (!string.IsNullOrEmpty(_tokenStore.RefreshToken) && ShouldRefreshToken())
|
||||
{
|
||||
_logger.LogInformation("토큰 만료 5분 전 - 자동 갱신 시작");
|
||||
var newTokenPair = await _authService.RefreshAccessTokenAsync(_tokenStore.RefreshToken);
|
||||
if (newTokenPair != null)
|
||||
{
|
||||
await LoginAsync(newTokenPair.AccessToken, newTokenPair.RefreshToken, newTokenPair.ExpiresIn);
|
||||
_logger.LogInformation("토큰 자동 갱신 성공");
|
||||
accessToken = newTokenPair.AccessToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("토큰 자동 갱신 실패 - 로그아웃");
|
||||
await LogoutAsync();
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
}
|
||||
|
||||
var principal = _authService.ValidateToken(accessToken);
|
||||
if (principal == null)
|
||||
{
|
||||
@@ -91,6 +111,25 @@ public class CustomAuthenticationStateProvider : AuthenticationStateProvider
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
|
||||
private bool ShouldRefreshToken()
|
||||
{
|
||||
// 토큰이 5분 이내로 만료되면 갱신 (300초 = 5분)
|
||||
if (_tokenStore.TokenExpiryTicks <= 0)
|
||||
return false;
|
||||
|
||||
const int refreshThresholdSeconds = 300;
|
||||
try
|
||||
{
|
||||
var expiryTime = new DateTime((long)_tokenStore.TokenExpiryTicks, DateTimeKind.Utc);
|
||||
var timeUntilExpiry = expiryTime - DateTime.UtcNow;
|
||||
return timeUntilExpiry.TotalSeconds <= refreshThresholdSeconds && timeUntilExpiry.TotalSeconds > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LogoutAsync()
|
||||
{
|
||||
// TokenStore 초기화
|
||||
|
||||
Reference in New Issue
Block a user