Files
SerpentRace/SerpentRace_Backend/dist/Application/Services/JWTService.js
T

102 lines
3.8 KiB
JavaScript

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JWTService = void 0;
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
class JWTService {
constructor() {
this.secretKey = process.env.JWT_SECRET || 'your-secret-key';
let expiry = 86400;
if (process.env.JWT_EXPIRY) {
expiry = parseInt(process.env.JWT_EXPIRY);
}
else if (process.env.JWT_EXPIRATION) {
expiry = this.parseDuration(process.env.JWT_EXPIRATION);
}
this.tokenExpiry = expiry;
this.cookieName = 'auth_token';
if (process.env.NODE_ENV === 'production' && (!process.env.JWT_SECRET || process.env.JWT_SECRET === 'your-secret-key')) {
throw new Error('JWT_SECRET environment variable must be set in production');
}
}
create(payload, res) {
const now = Math.floor(Date.now() / 1000);
const payloadWithTimestamps = {
...payload,
iat: now,
exp: now + this.tokenExpiry
};
// Don't use expiresIn option since we're manually setting exp in payload
const options = {};
const token = jsonwebtoken_1.default.sign(payloadWithTimestamps, this.secretKey, options);
res.cookie(this.cookieName, token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: this.tokenExpiry * 1000, // Convert to milliseconds
});
return token;
}
verify(req) {
try {
const token = req.cookies[this.cookieName];
if (!token)
return null;
const decoded = jsonwebtoken_1.default.verify(token, this.secretKey);
return decoded;
}
catch (error) {
return null;
}
}
// Check if token needs refresh (within 25% of expiry time)
shouldRefreshToken(payload) {
if (!payload.exp || !payload.iat)
return false;
const now = Math.floor(Date.now() / 1000);
const tokenAge = now - payload.iat;
const tokenLifetime = payload.exp - payload.iat;
const refreshThreshold = tokenLifetime * 0.75; // Refresh when 75% of lifetime has passed
return tokenAge >= refreshThreshold;
}
// Conditionally refresh token only if needed
refreshIfNeeded(payload, res) {
if (this.shouldRefreshToken(payload)) {
// Create new token with fresh timestamps, but same user data
const freshPayload = {
userId: payload.userId,
authLevel: payload.authLevel,
userStatus: payload.userStatus,
orgId: payload.orgId
};
this.create(freshPayload, res);
return true;
}
return false;
}
/**
* Parse duration string to seconds (e.g., "24h", "7d", "30m")
* @param duration Duration string
* @returns Duration in seconds
*/
parseDuration(duration) {
const match = duration.match(/^(\d+)([smhd])$/);
if (!match) {
throw new Error(`Invalid duration format: ${duration}. Use format like '24h', '7d', '30m'`);
}
const [, value, unit] = match;
const num = parseInt(value);
switch (unit) {
case 's': return num; // seconds
case 'm': return num * 60; // minutes
case 'h': return num * 60 * 60; // hours
case 'd': return num * 60 * 60 * 24; // days
default:
throw new Error(`Unsupported duration unit: ${unit}`);
}
}
}
exports.JWTService = JWTService;
//# sourceMappingURL=JWTService.js.map