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
JavaScript
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
;