How to Add Time-Limited Functionality to Your JavaScript Code
Learn how to implement code expiration and time-limited functionality in your JavaScript applications for demos, trial versions, and promotional features.
Table of Contents
Introduction
Time-limited functionality is a valuable technique for JavaScript developers who want to offer trial versions, demos, seasonal features, or promotional content with an expiration date. By implementing expiration mechanisms in your code, you can control when and how long your JavaScript functionality is available to users.
While time-based restrictions can be bypassed by determined users with technical knowledge, a well-implemented expiration system combined with proper obfuscation creates a sufficient barrier for most scenarios. This tutorial will guide you through implementing effective, tamper-resistant time-limited functionality in your JavaScript applications.
Use Cases for Time-Limited Code
Before diving into implementation details, let's explore some common use cases for time-limited JavaScript functionality:
Trial Versions
Offer fully functional software for a limited period to let users experience premium features before purchasing. After the trial period, functionality is either disabled or limited.
Seasonal Features
Implement holiday-themed features, limited-time promotions, or seasonal content that automatically activates and deactivates on specific dates.
Promotional Content
Create time-sensitive promotional offers, discounts, or special features that disappear after a limited period to create urgency.
License Management
Enforce license expirations in JavaScript libraries, components, or plugins by automatically disabling functionality after the license period ends.
Basic Implementation
Let's start with basic implementation strategies for time-limited functionality. We'll cover two common approaches: date-based and usage-based expiration.
Date-Based Expiration
The simplest approach is to define a specific expiration date after which your code ceases to function:
Date-Based Expiration Basic
/**
* Date-based expiration check
* @param {Date} expirationDate - The date when the code should expire
* @returns {boolean} - True if code is still valid, false if expired
*/
function isCodeValid(expirationDate) {
const currentDate = new Date();
return currentDate < expirationDate;
}
// Usage example
const EXPIRATION_DATE = new Date('2025-12-31T23:59:59');
function initializeApp() {
if (isCodeValid(EXPIRATION_DATE)) {
// Code is still valid, initialize normally
console.log('Application initialized successfully!');
enableFeatures();
} else {
// Code has expired, disable functionality
console.log('This version has expired. Please upgrade to continue using this feature.');
showExpiredMessage();
}
}
// Initialize the application
initializeApp();
This simple approach checks the current date against a predefined expiration date. If the current date is past the expiration date, the code can display a message, redirect to a purchase page, or gracefully degrade functionality.
This basic implementation is vulnerable to tampering. Users can manipulate their system clock or modify the JavaScript code to bypass the expiration. We'll address these vulnerabilities in later sections.
Usage-Based Expiration
Another approach is to limit functionality based on the number of times the code has been used, rather than a specific date:
Usage-Based Expiration Basic
/**
* Usage-based expiration using localStorage
* @param {number} maxUsage - Maximum number of allowed uses
* @returns {boolean} - True if usage limit not exceeded, false otherwise
*/
function checkUsageLimit(maxUsage) {
const storageKey = 'app_usage_count';
// Get current usage count from storage
let usageCount = parseInt(localStorage.getItem(storageKey) || '0');
// Increment usage count
usageCount++;
// Store updated count
localStorage.setItem(storageKey, usageCount.toString());
// Check if usage is within limits
return usageCount <= maxUsage;
}
// Usage example
const MAX_USAGE = 10; // Allow 10 uses
function initializeApp() {
if (checkUsageLimit(MAX_USAGE)) {
// Usage limit not exceeded
const remainingUses = MAX_USAGE - parseInt(localStorage.getItem('app_usage_count'));
console.log(`Application initialized successfully! Remaining uses: ${remainingUses}`);
enableFeatures();
} else {
// Usage limit exceeded
console.log('Usage limit exceeded. Please purchase the full version to continue.');
showUpgradeMessage();
}
}
// Initialize the application
initializeApp();
This approach counts the number of times the application has been used and stops functioning after reaching a predefined limit. This creates a usage-limited trial rather than a time-limited one.
For a better user experience, consider showing the remaining usage count to users, allowing them to make informed decisions about when to upgrade.
Both basic approaches have their own advantages and use cases, but they are easily bypassed. Let's explore how to make our expiration checks more tamper-resistant.
Protecting Against Tampering
To create more robust time-limited functionality, we need to address common tampering methods:
Obfuscation Techniques
One of the first lines of defense is to obfuscate your expiration logic:
Obfuscation Protection Intermediate
// Instead of a simple date comparison, make the expiration check more complex
function verifyLicense() {
// Split the expiration timestamp into multiple parts
const p1 = 1735; // These values combine to form the expiration timestamp
const p2 = 686; // (Unix timestamp for December 31, 2025: 1735686000)
const p3 = 000;
// Create a decoy date to confuse analysis
const decoyDate = new Date('2026-06-30T23:59:59');
// Complex calculation to get the actual expiration timestamp
const expirationTimestamp = (p1 * 1000000) + (p2 * 1000) + p3;
// Get current timestamp with an obscured method
const now = () => {
return new Date().getTime();
};
// Multiple checks to make static analysis harder
if (decoyDate.getFullYear() !== 2026) return false;
if (now() > expirationTimestamp) return false;
if (Math.floor(now() / 1000) > Math.floor(expirationTimestamp / 1000)) return false;
return true;
}
// Additional layer: Hide the usage of the verification function
const features = {
initialize: function() {
// Multiple function calls that include the verification
this.setupEnvironment();
this.checkCompatibility();
this.prepareResources();
},
setupEnvironment: function() {
// Normal setup code
console.log('Setting up environment...');
},
checkCompatibility: function() {
// Hide license check in a seemingly unrelated function
console.log('Checking compatibility...');
if (!verifyLicense()) {
// Hide the true reason for disabling
throw new Error('Compatibility check failed: Environment not supported');
}
},
prepareResources: function() {
console.log('Preparing resources...');
// Normal initialization continues
}
};
// Start application
features.initialize();
This approach makes the expiration mechanism more difficult to identify and modify by:
- Splitting the expiration timestamp into multiple parts
- Adding decoy variables and checks
- Hiding the verification logic in seemingly unrelated functions
- Using obscure error messages that don't directly indicate license expiration
Multi-Source Time Verification
To mitigate system clock manipulation, implement multiple time sources:
Multi-Source Time Verification Intermediate
/**
* Fetches time from multiple sources to prevent clock manipulation
* @returns {Promise} - Promise resolving to the verified timestamp
*/
async function getVerifiedTime() {
const sources = [
'https://worldtimeapi.org/api/ip',
'https://timeapi.io/api/Time/current/zone?timeZone=UTC',
// Add more time API sources as backup
];
const localTime = new Date().getTime();
let timeResults = [localTime]; // Include local time as fallback
// Fetch time from external sources
const fetchPromises = sources.map(async (source) => {
try {
const response = await fetch(source);
const data = await response.json();
// Extract timestamp - adjust based on API response format
let timestamp;
if (source.includes('worldtimeapi')) {
timestamp = new Date(data.utc_datetime).getTime();
} else if (source.includes('timeapi')) {
timestamp = new Date(data.dateTime).getTime();
}
return timestamp;
} catch (error) {
console.warn(`Failed to fetch time from ${source}`, error);
return null;
}
});
// Collect all successful time results
const results = await Promise.allSettled(fetchPromises);
results.forEach(result => {
if (result.status === 'fulfilled' && result.value) {
timeResults.push(result.value);
}
});
// Use median time to avoid outliers
timeResults.sort((a, b) => a - b);
const medianTime = timeResults[Math.floor(timeResults.length / 2)];
return medianTime;
}
/**
* Checks if license is valid using verified time
* @param {number} expirationTimestamp - Expiration timestamp
* @returns {Promise} - Promise resolving to license validity
*/
async function verifyLicenseWithTime(expirationTimestamp) {
try {
const verifiedTime = await getVerifiedTime();
return verifiedTime < expirationTimestamp;
} catch (error) {
console.error('Error during time verification:', error);
// Fallback to local time if all time sources fail
return new Date().getTime() < expirationTimestamp;
}
}
// Usage example
const EXPIRATION_TIMESTAMP = 1735686000000; // Dec 31, 2025
async function initializeApp() {
const isValid = await verifyLicenseWithTime(EXPIRATION_TIMESTAMP);
if (isValid) {
console.log('License valid, initializing application...');
enableFeatures();
} else {
console.log('License expired, showing upgrade prompt...');
showUpgradeMessage();
}
}
// Initialize with time verification
initializeApp();
This implementation retrieves time from multiple external sources, making it much harder for users to bypass expiration by changing their system clock. By using the median time from multiple sources, you also handle potential outliers or API failures gracefully.
This method requires an internet connection to function properly. Include a sensible fallback for offline usage, potentially with degraded functionality.
Server-Side Verification
For the strongest protection, combine client-side checks with server-side verification:
Server-Side Verification Advanced
/**
* Server-side license verification
* @param {string} licenseKey - User's license key
* @returns {Promise
This approach offers the strongest protection by verifying licenses on your server, where users cannot tamper with the code. Key features include:
- Server-side verification that cannot be bypassed by client-side manipulation
- Unique instance ID to prevent sharing licenses across multiple installations
- Offline grace period for legitimate users with temporary internet issues
- Periodic re-verification to ensure continued license validity
- Feature-based licensing that can enable/disable specific functionality
User Experience Considerations
A well-designed expiration system should provide a good user experience, even when features expire:
Clear Communication
Always inform users about time limitations upfront and provide clear notifications as the expiration date approaches:
/**
* Check and display appropriate expiration notifications
* @param {Date} expirationDate - When the code will expire
*/
function checkExpirationStatus(expirationDate) {
const currentDate = new Date();
const daysRemaining = Math.ceil((expirationDate - currentDate) / (1000 * 60 * 60 * 24));
if (daysRemaining <= 0) {
// Already expired
showExpiredMessage();
} else if (daysRemaining <= 7) {
// Expiring soon - within a week
showExpiringMessage(daysRemaining);
} else if (daysRemaining <= 30) {
// Approaching expiration - within a month
showUpgradeReminder(daysRemaining);
}
}
function showExpiringMessage(days) {
const message = `
Your trial expires in ${days} day${days !== 1 ? 's' : ''}!
Upgrade now to keep access to all premium features.
`;
// Display message to user
const noticeElement = document.createElement('div');
noticeElement.innerHTML = message;
noticeElement.className = 'floating-notice';
document.body.appendChild(noticeElement);
// Add event listeners
noticeElement.querySelector('.upgrade-button').addEventListener('click', function() {
window.location.href = 'https://your-product.com/upgrade';
});
noticeElement.querySelector('.dismiss-button').addEventListener('click', function() {
noticeElement.remove();
// Don't show again for 24 hours
localStorage.setItem('notice_dismissed', new Date().getTime().toString());
});
}
Graceful Degradation
Instead of completely disabling your application, consider graceful degradation where basic functionality remains available:
/**
* Enable features based on license status
* @param {object} licenseStatus - Current license status
*/
function enableFeaturesBasedOnLicense(licenseStatus) {
// Core features always available
enableCoreFeatures();
if (licenseStatus.valid) {
// Premium features for valid licenses
enablePremiumFeatures();
// Specific feature flags from license
licenseStatus.features.forEach(featureFlag => {
enableFeatureByFlag(featureFlag);
});
} else if (licenseStatus.expired && licenseStatus.wasValid) {
// User had a valid license before, enable read-only mode
enableReadOnlyMode();
showRenewalMessage();
} else {
// Never had a valid license or in trial mode
enableTrialFeatures();
showUpgradeMessage();
}
}
function enableReadOnlyMode() {
// Allow viewing content but disable editing
document.querySelectorAll('input, textarea, select, button.edit').forEach(element => {
element.disabled = true;
});
// Add visual indication
document.body.classList.add('read-only-mode');
// Show read-only banner
const banner = document.createElement('div');
banner.className = 'read-only-banner';
banner.innerHTML = 'Read-Only Mode: Your license has expired. Renew now to restore full functionality.';
document.body.prepend(banner);
}
Conversion Optimization
Design your expiration system to maximize conversion from trial to paid versions:
- Include direct links to purchase or upgrade pages
- Offer special discounts as the expiration date approaches
- Highlight the value of premium features that will be lost
- Make the upgrade process as frictionless as possible
Integration with JS Obfuscator Pro
JS Obfuscator Pro provides built-in features for implementing secure code expiration. Here's how to use them:
JS Obfuscator Pro Integration Advanced
To implement expiration using JS Obfuscator Pro:
- Open the "Protection" panel in JS Obfuscator Pro
- Enable "Code Expiration" option
- Set the expiration date using the date picker
- Choose the action to take when code expires:
Expiration Action | Description | Best For |
---|---|---|
Halt Execution | Completely stops code execution when expired | Strict trial versions |
Redirect User | Sends the user to a specified URL (e.g., renewal page) | Trial versions with direct conversion path |
Show Message | Displays a custom message explaining the expiration | Informational expiration notices |
Custom Action | Executes your custom JavaScript code on expiration | Complex degradation or custom user experiences |
JS Obfuscator Pro handles all the technical implementation details, including:
- Secure time verification that's resistant to tampering
- Obfuscation of the expiration mechanism
- Multiple verification points to prevent bypass attempts
- Proper error handling for a smooth user experience
For maximum security, combine JS Obfuscator Pro's expiration feature with server-side verification and domain locking to create a multi-layered protection system.
Conclusion
Implementing time-limited functionality in your JavaScript code is a powerful way to create trial versions, seasonal features, or subscription-based services. While no client-side protection is foolproof, combining multiple techniques provides robust security for most use cases.
To summarize the key approaches:
- Basic Implementation: Simple date or usage-based checks for minimal protection
- Anti-Tampering: Obfuscation and multi-source time verification to prevent clock manipulation
- Server-Side Verification: The strongest approach, combining client and server checks
- User Experience: Clear communication and graceful degradation for a positive user experience
- JS Obfuscator Pro: Built-in tools to implement secure expiration with minimal effort
Remember that the level of protection should match the value of your code. For most commercial JavaScript products, a combination of obfuscation and server-side verification provides the optimal balance between security and implementation complexity.
Comments
Comments are loading...