atlp-rwanda/atlp-devpulse-fn

View on GitHub
src/utils/tokenExpirationHandler.ts

Summary

Maintainability
A
0 mins
Test Coverage
import jwtDecode from 'jwt-decode';
import { toast } from 'react-toastify';

interface DecodedToken {
  exp?: number;
  iat?: number; 
  data?: {
    email: string;
  };
  email?: string;
}

class TokenExpirationHandler {
  private static readonly STORAGE_KEYS = {
    ACCESS_TOKEN: 'access_token',
    TOKEN_ISSUED_TIME: 'token_issued_time', 
  } as const;

  private static readonly LOGIN_ROUTE = '/login';

  private static showToast(message: string) {
    toast.error(message);
  }

  private static clearAuthData() {
    localStorage.clear();
  }

  private static redirect() {
    window.location.href = this.LOGIN_ROUTE;
  }

  private static handleRedirection(message: string) {
    this.showToast(message);
    this.clearAuthData();
    setTimeout(() => this.redirect(), 400);
  }

  public static isTokenExpired(token: string): boolean {
    try {
      const decoded = jwtDecode<DecodedToken>(token);
      const tokenIssuedTime = localStorage.getItem(this.STORAGE_KEYS.TOKEN_ISSUED_TIME);
      const issuedTime = tokenIssuedTime ? parseInt(tokenIssuedTime, 10) : null;

     
      const currentTime = Date.now() / 1000; 

     
      const tokenIat = issuedTime || decoded.iat;
      if (!tokenIat) return false;

      const timeElapsedSinceIssue = currentTime - tokenIat;
      const ExTime = 60 * 60; 

     
      return timeElapsedSinceIssue > ExTime;
    } catch {
      return true; 
    }
  }

  public static validateToken(): boolean {
    const token = localStorage.getItem(this.STORAGE_KEYS.ACCESS_TOKEN);

    if (!token) {
      this.handleRedirection('Please login to continue');
      return false;
    }

    if (this.isTokenExpired(token)) {
      this.handleRedirection('Your session has expired after 1 hour. Please login again');
      return false;
    }

    return true;
  }

  public static handleAuthError(error: any): void {
    const errorMessage = error?.message?.toLowerCase() || '';

    if (errorMessage.includes('jwt expired')) {
      this.handleRedirection('Your session has expired. Please login again');
    } else if (errorMessage.includes('invalid token')) {
      this.handleRedirection('Invalid authentication. Please login again');
    } else if (errorMessage.includes('unauthorized')) {
      this.handleRedirection('Unauthorized access. Please login again');
    } else {
      this.handleRedirection('Authentication error. Please login again');
    }
  }

  public static forceLogout(message?: string): void {
    this.handleRedirection(message || 'You have been logged out');
  }

  public static storeToken(token: string): void {
    localStorage.setItem(this.STORAGE_KEYS.ACCESS_TOKEN, token);
  
    localStorage.setItem(this.STORAGE_KEYS.TOKEN_ISSUED_TIME, Math.floor(Date.now() / 1000).toString());
  }
}

export default TokenExpirationHandler;