mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 14:31:12 -05:00
142 lines
3.1 KiB
TypeScript
142 lines
3.1 KiB
TypeScript
/**
|
|
* Token Storage Utility
|
|
*
|
|
* Handles JWT token storage, retrieval, and validation in localStorage.
|
|
*/
|
|
|
|
import { TokenPayload, TokenStorage } from '@/lib/types/auth';
|
|
|
|
// Storage keys
|
|
const ACCESS_TOKEN_KEY = 'auth_access_token';
|
|
const REFRESH_TOKEN_KEY = 'auth_refresh_token';
|
|
|
|
/**
|
|
* Decode JWT token payload without verification
|
|
* (Verification happens on the server)
|
|
*/
|
|
function decodeToken(token: string): TokenPayload | null {
|
|
try {
|
|
const parts = token.split('.');
|
|
if (parts.length !== 3) {
|
|
return null;
|
|
}
|
|
|
|
const payload = parts[1];
|
|
const decoded = JSON.parse(atob(payload));
|
|
|
|
return decoded as TokenPayload;
|
|
} catch (error) {
|
|
console.error('Failed to decode token:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a token is expired
|
|
*/
|
|
function isTokenExpired(token: string): boolean {
|
|
const decoded = decodeToken(token);
|
|
if (!decoded || !decoded.exp) {
|
|
return true;
|
|
}
|
|
|
|
// Check if token expires in the next 60 seconds (add buffer)
|
|
const expiryTime = decoded.exp * 1000; // Convert to milliseconds
|
|
const now = Date.now();
|
|
const bufferTime = 60 * 1000; // 60 seconds buffer
|
|
|
|
return now >= (expiryTime - bufferTime);
|
|
}
|
|
|
|
/**
|
|
* Token storage implementation
|
|
*/
|
|
export const tokenStorage: TokenStorage = {
|
|
getAccessToken(): string | null {
|
|
if (typeof window === 'undefined') {
|
|
return null;
|
|
}
|
|
return localStorage.getItem(ACCESS_TOKEN_KEY);
|
|
},
|
|
|
|
setAccessToken(token: string): void {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
localStorage.setItem(ACCESS_TOKEN_KEY, token);
|
|
},
|
|
|
|
getRefreshToken(): string | null {
|
|
if (typeof window === 'undefined') {
|
|
return null;
|
|
}
|
|
return localStorage.getItem(REFRESH_TOKEN_KEY);
|
|
},
|
|
|
|
setRefreshToken(token: string): void {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
localStorage.setItem(REFRESH_TOKEN_KEY, token);
|
|
},
|
|
|
|
clearTokens(): void {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
localStorage.removeItem(ACCESS_TOKEN_KEY);
|
|
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
|
},
|
|
|
|
hasValidAccessToken(): boolean {
|
|
const token = this.getAccessToken();
|
|
if (!token) {
|
|
return false;
|
|
}
|
|
return !isTokenExpired(token);
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Get the decoded payload from the access token
|
|
*/
|
|
export function getTokenPayload(): TokenPayload | null {
|
|
const token = tokenStorage.getAccessToken();
|
|
if (!token) {
|
|
return null;
|
|
}
|
|
return decodeToken(token);
|
|
}
|
|
|
|
/**
|
|
* Check if the refresh token is expired
|
|
*/
|
|
export function isRefreshTokenExpired(): boolean {
|
|
const token = tokenStorage.getRefreshToken();
|
|
if (!token) {
|
|
return true;
|
|
}
|
|
return isTokenExpired(token);
|
|
}
|
|
|
|
/**
|
|
* Get time until access token expires (in milliseconds)
|
|
*/
|
|
export function getTimeUntilExpiry(): number | null {
|
|
const token = tokenStorage.getAccessToken();
|
|
if (!token) {
|
|
return null;
|
|
}
|
|
|
|
const decoded = decodeToken(token);
|
|
if (!decoded || !decoded.exp) {
|
|
return null;
|
|
}
|
|
|
|
const expiryTime = decoded.exp * 1000;
|
|
const now = Date.now();
|
|
const timeRemaining = expiryTime - now;
|
|
|
|
return timeRemaining > 0 ? timeRemaining : 0;
|
|
}
|