Files
ReadMeABook/src/lib/utils/jwt-client.ts
T
kikootwo 682836237b Implement centralized logging with RMABLogger
Replaces scattered console statements with a unified RMABLogger across backend API routes and services. Adds LOG_LEVEL-based filtering, job-aware database persistence, and context-based logging. Updates documentation to describe the new logging system and usage patterns. Also documents qBittorrent CSRF header fix
2026-01-28 11:41:58 -05:00

79 lines
1.9 KiB
TypeScript

/**
* Component: Client-Side JWT Utilities
* Documentation: documentation/frontend/routing-auth.md
*/
import { RMABLogger } from './logger';
const logger = RMABLogger.create('JWTClient');
interface JWTPayload {
sub: string;
plexId: string;
username: string;
role: string;
iat: number;
exp: number;
}
/**
* Decode JWT without verification (client-side only)
* Note: This does NOT verify the signature - only use for reading claims
*/
export function decodeJWT(token: string): JWTPayload | null {
try {
const parts = token.split('.');
if (parts.length !== 3) {
return null;
}
const payload = parts[1];
const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
return decoded as JWTPayload;
} catch (error) {
logger.error('Failed to decode JWT', { error: error instanceof Error ? error.message : String(error) });
return null;
}
}
/**
* Check if token is expired
*/
export function isTokenExpired(token: string): boolean {
const decoded = decodeJWT(token);
if (!decoded || !decoded.exp) {
return true;
}
const now = Math.floor(Date.now() / 1000);
return decoded.exp < now;
}
/**
* Get milliseconds until token expires
*/
export function getTokenExpiryMs(token: string): number | null {
const decoded = decodeJWT(token);
if (!decoded || !decoded.exp) {
return null;
}
const now = Math.floor(Date.now() / 1000);
const expiresIn = decoded.exp - now;
return expiresIn > 0 ? expiresIn * 1000 : 0;
}
/**
* Get milliseconds until token should be refreshed (5 mins before expiry)
*/
export function getRefreshTimeMs(token: string): number | null {
const expiryMs = getTokenExpiryMs(token);
if (expiryMs === null) {
return null;
}
const REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes
const refreshTime = expiryMs - REFRESH_BUFFER_MS;
return refreshTime > 0 ? refreshTime : 0;
}