houser-js-utils
Version:
A comprehensive collection of TypeScript utility functions for common development tasks including array manipulation, string processing, date handling, random number generation, validation, and much more.
206 lines (205 loc) • 7.32 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const JwtUtils = {
/**
* Decodes and parses a JWT token into its payload object.
* @param token - The JWT token string to decode
* @returns The decoded JWT payload containing all claims
* @throws Error if token is invalid, malformed, or cannot be parsed
* @example
* ```typescript
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
* const payload = JwtUtils.decodeToken(token);
* console.log(payload.sub); // '1234567890'
* console.log(payload.exp); // 1234567890
* ```
*/
decodeToken(token) {
try {
const base64Url = token.split(".")[1];
if (!base64Url) {
throw new Error("Invalid JWT token format");
}
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const jsonPayload = decodeURIComponent(
atob(base64).split("").map((c) => {
const hex = c.charCodeAt(0).toString(16);
return "%" + ("00" + hex).slice(-2);
}).join("")
);
return JSON.parse(jsonPayload);
} catch (error) {
throw new Error(
`Failed to decode JWT token: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
},
/**
* Extracts a specific claim value from a JWT token's payload.
* @param token - The JWT token string to parse
* @param claim - The name of the claim to retrieve
* @returns The claim value or undefined if the claim is not found
* @throws Error if token is invalid or cannot be parsed
* @example
* ```typescript
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
* const userId = JwtUtils.getPayloadClaim(token, 'sub'); // "1234567890"
* const role = JwtUtils.getPayloadClaim(token, 'role'); // "admin"
* const email = JwtUtils.getPayloadClaim(token, 'email'); // "user@example.com"
* ```
*/
getPayloadClaim(token, claim) {
try {
const payload = this.decodeToken(token);
return payload[claim];
} catch (error) {
throw new Error(
`Failed to get claim: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
},
/**
* Calculates the time remaining until a JWT token expires.
* @param token - The JWT token to check for expiration
* @returns The time remaining in seconds, or null if no expiration time is set
* @throws Error if token is invalid or cannot be parsed
* @example
* ```typescript
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
* const timeRemaining = JwtUtils.getTokenTimeRemaining(token);
* if (timeRemaining !== null) {
* console.log(`Token expires in ${timeRemaining} seconds`);
* } else {
* console.log('Token has no expiration time');
* }
* ```
*/
getTokenTimeRemaining(token) {
try {
const payload = this.decodeToken(token);
if (!payload.exp) return null;
const currentTime = Math.floor(Date.now() / 1e3);
return Math.max(0, payload.exp - currentTime);
} catch (error) {
throw new Error(
`Failed to get token time remaining: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
},
/**
* Validates if a string follows the correct JWT token format (three base64url-encoded parts).
* @param token - The string to validate as a JWT token
* @returns True if the string follows JWT format, false otherwise
* @example
* ```typescript
* const validToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature';
* const invalidToken = 'not.a.jwt.token';
*
* JwtUtils.isJwt(validToken); // true
* JwtUtils.isJwt(invalidToken); // false
* JwtUtils.isJwt('invalid'); // false
* ```
*/
isJwt(token) {
const parts = token.split(".");
if (parts.length !== 3) return false;
const base64UrlPattern = /^[A-Za-z0-9-_]+$/;
return parts.every(
(part) => part.length > 0 && base64UrlPattern.test(part)
);
},
/**
* Checks if the JWT token stored in localStorage will expire within the next hour.
* @returns True if token will expire within an hour, false if not expiring soon, null if no token exists
* @example
* ```typescript
* const result = JwtUtils.isTokenExpiringSoon();
* if (result === null) {
* console.log('No token found in localStorage');
* } else if (result) {
* console.log('Token expires soon - consider refreshing');
* } else {
* console.log('Token is still valid for more than an hour');
* }
* ```
*/
isTokenExpiringSoon() {
const token = localStorage.getItem("token");
if (!token) {
return null;
}
try {
const payload = this.decodeToken(token);
if (!payload.exp) return false;
const secondsDiff = (new Date(payload.exp * 1e3).getTime() - Date.now()) / 1e3;
return secondsDiff > 0 && secondsDiff < 3600;
} catch (error) {
console.error("Error checking JWT expiration:", error);
return false;
}
},
/**
* Determines if a JWT token has expired based on its expiration time claim.
* @param token - The JWT token string to check
* @returns True if the token is expired, false if still valid or has no expiration
* @throws Error if token is invalid or cannot be parsed
* @example
* ```typescript
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
*
* if (JwtUtils.isTokenExpired(token)) {
* console.log('Token has expired - please authenticate again');
* } else {
* console.log('Token is still valid');
* }
* ```
*/
isTokenExpired(token) {
try {
const payload = this.decodeToken(token);
if (!payload.exp) return false;
const currentTime = Math.floor(Date.now() / 1e3);
return payload.exp < currentTime;
} catch (error) {
throw new Error(
`Failed to check token expiration: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
},
/**
* Performs comprehensive validation of a JWT token including expiration and required claims.
* @param token - The JWT token string to validate
* @param requiredClaims - Array of claim names that must be present in the token
* @returns True if token is valid, not expired, and contains all required claims
* @throws Error if token is invalid or cannot be parsed
* @example
* ```typescript
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
*
* // Basic validation (just check expiration)
* const isValid = JwtUtils.isValidToken(token);
*
* // Validation with required claims
* const isValidWithClaims = JwtUtils.isValidToken(token, ['sub', 'role', 'email']);
*
* if (isValidWithClaims) {
* console.log('Token is valid and has all required claims');
* }
* ```
*/
isValidToken(token, requiredClaims = []) {
try {
const payload = this.decodeToken(token);
if (payload.exp && payload.exp < Math.floor(Date.now() / 1e3)) {
return false;
}
return requiredClaims.every((claim) => payload[claim] !== void 0);
} catch (error) {
throw new Error(
`Failed to validate token: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
}
};
exports.JwtUtils = JwtUtils;
//# sourceMappingURL=JwtUtils.js.map