Skip to content

Refresh Token Flow

This page documents the token refresh flow in GrydAuth, which allows users to obtain new access tokens without re-entering their credentials.

Overview

Access tokens in GrydAuth are short-lived (default: 1 hour) for security purposes. When an access token expires, the client can use the refresh token to obtain a new access token without requiring the user to log in again.

Token Lifetimes

Token TypeDefault LifetimePurpose
Access Token1 hourAPI authentication
Refresh Token7 daysObtain new access tokens
Sliding ExpirationOptionalExtends refresh token on use

Sequence Diagram

100% 💡 Use Ctrl + Scroll para zoom | Arraste para navegar

Step-by-Step Explanation

Phase 1: Access Token Expiration

When the access token expires, API requests return 401 Unauthorized with error: "token_expired". The client should detect this and initiate the refresh flow.

Phase 2: Token Refresh

2.1 Send Refresh Request

json
POST /api/auth/refresh
{
  "refreshToken": "550e8400-e29b-41d4-a716-446655440000"
}

2.2 Validate Refresh Token

The refresh token is validated against Redis cache:

csharp
var tokenData = await _cache.GetAsync<RefreshTokenData>(refreshToken);
if (tokenData == null)
    return Result.Failure("Invalid refresh token");

2.3 Validate User Status

The system ensures:

  • User still exists
  • User is active (IsActive = true)
  • User is not locked out

2.4 Token Version Check

Token version prevents use of old tokens after:

  • Password change
  • Admin-forced logout
  • Security incident response
csharp
if (tokenData.TokenVersion != user.TokenVersion)
{
    await _cache.DeleteAsync(refreshToken);
    return Result.Failure("Token invalidated");
}

2.5 Tenant Validation

Ensures the user still has access to the tenant from the original token.

2.6 Refresh Roles & Permissions

Important: The new access token contains the current roles and permissions, not the cached ones. This ensures permission changes take effect immediately.

2.7 Token Rotation

For security, GrydAuth implements refresh token rotation:

  1. Old refresh token is deleted
  2. New refresh token is generated
  3. Client must use new refresh token for next refresh

This limits the damage if a refresh token is compromised.

Phase 3: Resume Operations

Client stores the new tokens and continues making API requests.

Automatic Token Refresh

Client-Side Implementation

typescript
// Axios interceptor for automatic refresh
axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;
    
    if (error.response?.status === 401 && 
        error.response?.data?.error === 'token_expired' &&
        !originalRequest._retry) {
      
      originalRequest._retry = true;
      
      try {
        const { accessToken, refreshToken } = await authService.refresh();
        authStore.setTokens(accessToken, refreshToken);
        
        originalRequest.headers.Authorization = `Bearer ${accessToken}`;
        return axios(originalRequest);
        
      } catch (refreshError) {
        // Refresh failed - redirect to login
        authStore.clearTokens();
        router.push('/login');
        return Promise.reject(refreshError);
      }
    }
    
    return Promise.reject(error);
  }
);

Token Refresh Service

typescript
class AuthService {
  private refreshPromise: Promise<TokenResponse> | null = null;
  
  async refresh(): Promise<TokenResponse> {
    // Prevent multiple simultaneous refresh requests
    if (this.refreshPromise) {
      return this.refreshPromise;
    }
    
    this.refreshPromise = this.doRefresh();
    
    try {
      return await this.refreshPromise;
    } finally {
      this.refreshPromise = null;
    }
  }
  
  private async doRefresh(): Promise<TokenResponse> {
    const refreshToken = authStore.getRefreshToken();
    const response = await api.post('/auth/refresh', { refreshToken });
    return response.data;
  }
}

Sliding Expiration

When enabled, refresh token expiration is extended on each use:

100% 💡 Use Ctrl + Scroll para zoom | Arraste para navegar

Error Scenarios

ErrorHTTP StatusCauseClient Action
Invalid refresh token401Token not found in cacheRedirect to login
Token expired401Refresh token TTL exceededRedirect to login
Token revoked401Password changed or admin actionRedirect to login
User inactive401Account deactivatedShow account disabled message
Tenant access revoked403Removed from tenantRedirect to tenant selection

Security Considerations

Refresh Token Storage

PlatformRecommended Storage
Web (SPA)HttpOnly cookie or memory
MobileSecure storage (Keychain/Keystore)
DesktopOS credential manager

Never Store in localStorage

Refresh tokens should never be stored in localStorage due to XSS vulnerability.

Token Rotation Benefits

  1. Limits exposure window - Stolen tokens become invalid after one use
  2. Detects theft - If legitimate user's refresh fails, theft is detected
  3. Audit trail - Each rotation is logged for security analysis

Absolute Expiration

Even with sliding expiration, consider an absolute maximum lifetime:

csharp
public class RefreshTokenOptions
{
    public TimeSpan SlidingExpiration { get; set; } = TimeSpan.FromDays(7);
    public TimeSpan AbsoluteExpiration { get; set; } = TimeSpan.FromDays(30);
}

Configuration

json
{
  "JwtSettings": {
    "AccessTokenExpirationMinutes": 60,
    "RefreshTokenExpirationDays": 7,
    "EnableSlidingExpiration": true,
    "EnableTokenRotation": true
  }
}

Released under the MIT License.