UNPKG

tl-shared-security

Version:

Enterprise-grade security module for frontend and backend applications with comprehensive protection against XSS, CSRF, SQL injection, and other security vulnerabilities

168 lines 5.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.jwtService = exports.JwtService = void 0; const tslib_1 = require("tslib"); const crypto = tslib_1.__importStar(require("crypto")); class JwtService { constructor() { this.defaultOptions = { expiresIn: 3600, // 1 hour issuer: 'shared-security', algorithm: 'HS256', }; } /** * Signs a JWT token * @param payload - Data to include in the token * @param secret - Secret key to sign the token * @param options - JWT options * @returns Signed JWT token */ sign(payload, secret, options) { const opts = { ...this.defaultOptions, ...options }; // Create header const header = { alg: opts.algorithm, typ: 'JWT', }; // Create payload with timestamps const now = Math.floor(Date.now() / 1000); const finalPayload = { ...payload, iat: now, exp: payload.exp || now + opts.expiresIn, iss: payload.iss || opts.issuer, }; if (opts.subject && !payload.sub) { finalPayload.sub = opts.subject; } // Encode header and payload const encodedHeader = this.base64UrlEncode(JSON.stringify(header)); const encodedPayload = this.base64UrlEncode(JSON.stringify(finalPayload)); // Create signature const signatureInput = `${encodedHeader}.${encodedPayload}`; const signature = this.createSignature(signatureInput, secret, opts.algorithm); // Return JWT token return `${signatureInput}.${signature}`; } /** * Verifies a JWT token * @param token - JWT token to verify * @param secret - Secret key to verify the token * @param options - JWT options * @returns Decoded payload if token is valid * @throws Error if token is invalid */ verify(token, secret, options) { const opts = { ...this.defaultOptions, ...options }; // Split token const parts = token.split('.'); if (parts.length !== 3) { throw new Error('Invalid token format'); } const [encodedHeader, encodedPayload, signature] = parts; // Verify signature const signatureInput = `${encodedHeader}.${encodedPayload}`; const isValid = this.verifySignature(signatureInput, signature, secret, opts.algorithm); if (!isValid) { throw new Error('Invalid signature'); } // Decode payload const payload = JSON.parse(this.base64UrlDecode(encodedPayload)); // Verify expiration const now = Math.floor(Date.now() / 1000); if (payload.exp && payload.exp < now) { throw new Error('Token expired'); } // Verify issuer if (opts.issuer && payload.iss !== opts.issuer) { throw new Error('Invalid issuer'); } // Verify subject if (opts.subject && payload.sub !== opts.subject) { throw new Error('Invalid subject'); } return payload; } /** * Decodes a JWT token without verification * @param token - JWT token to decode * @returns Decoded payload * @throws Error if token format is invalid */ decode(token) { // Split token const parts = token.split('.'); if (parts.length !== 3) { throw new Error('Invalid token format'); } const [encodedHeader, encodedPayload] = parts; // Decode header and payload const header = JSON.parse(this.base64UrlDecode(encodedHeader)); const payload = JSON.parse(this.base64UrlDecode(encodedPayload)); return { header, payload }; } /** * Refreshes a JWT token * @param token - JWT token to refresh * @param secret - Secret key to sign the new token * @param options - JWT options * @returns New JWT token */ refresh(token, secret, options) { // Decode token without verification const { payload } = this.decode(token); // Remove expiration and issued at claims delete payload.exp; delete payload.iat; // Sign new token return this.sign(payload, secret, options); } base64UrlEncode(str) { return Buffer.from(str) .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } base64UrlDecode(str) { // Add padding if needed let base64 = str.replace(/-/g, '+').replace(/_/g, '/'); while (base64.length % 4) { base64 += '='; } return Buffer.from(base64, 'base64').toString(); } createSignature(data, secret, algorithm) { const alg = algorithm.toLowerCase(); let hmacAlg; switch (alg) { case 'hs256': hmacAlg = 'sha256'; break; case 'hs384': hmacAlg = 'sha384'; break; case 'hs512': hmacAlg = 'sha512'; break; default: hmacAlg = 'sha256'; } return crypto .createHmac(hmacAlg, secret) .update(data) .digest('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } verifySignature(data, signature, secret, algorithm) { const expectedSignature = this.createSignature(data, secret, algorithm); return signature === expectedSignature; } } exports.JwtService = JwtService; // Export singleton instance exports.jwtService = new JwtService(); //# sourceMappingURL=jwt.service.js.map