@atlashub/license-validator
v1.0.1
Published
License validator for AtlasHub products - validates JWT licenses signed by AtlasHub License Generator
Readme
@atlashub/license-validator
Validate JWT licenses signed by AtlasHub License Generator. Use this package to protect your CLI tools, npm packages, and Node.js applications with license-based access control.
Installation
npm install @atlashub/license-validatorQuick Start
import { validateLicense, hasFeature } from '@atlashub/license-validator';
const result = validateLicense(process.env.LICENSE_KEY, {
productId: 'my-cli-tool'
});
if (!result.valid) {
console.error('License error:', result.error);
process.exit(1);
}
console.log('Licensed to:', result.license.company);
console.log('Edition:', result.license.edition);
console.log('Expires in:', result.license.daysRemaining, 'days');API Reference
validateLicense(licenseKey, options)
Validates a license key and returns detailed license information.
Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| licenseKey | string \| undefined | The JWT license key to validate |
| options.productId | string | (Optional) Expected product ID - validates the audience claim |
| options.gracePeriodDays | number | (Optional) Days to allow after expiration (default: 0) |
Returns: ValidationResult
interface ValidationResult {
valid: boolean;
license?: License;
error?: string;
}
interface License {
company: string; // Company/tenant identifier
productId: string; // Product identifier
edition: string; // starter | professional | enterprise
features: string[]; // Enabled features (or ['*'] for all)
limits: {
users: number;
storage_gb: number;
api_calls_per_day: number;
};
issuedAt: Date;
expiresAt: Date;
daysRemaining: number;
isExpired: boolean;
isValid: boolean;
}hasFeature(license, featureName)
Checks if a specific feature is enabled in the license.
import { validateLicense, hasFeature } from '@atlashub/license-validator';
const { license } = validateLicense(licenseKey);
if (hasFeature(license, 'premium')) {
// Enable premium functionality
}
if (hasFeature(license, 'ai')) {
// Enable AI features
}Note: Returns true if features includes '*' (enterprise licenses).
isWithinLimits(license, type, currentUsage)
Checks if usage is within license limits.
import { isWithinLimits } from '@atlashub/license-validator';
if (!isWithinLimits(license, 'users', currentUserCount)) {
console.error('User limit exceeded');
}
if (!isWithinLimits(license, 'api_calls_per_day', todaysCalls)) {
console.error('API rate limit exceeded');
}decodeLicenseUnsafe(licenseKey)
Decodes a license key without signature verification. For debugging only.
import { decodeLicenseUnsafe } from '@atlashub/license-validator';
const payload = decodeLicenseUnsafe(licenseKey);
console.log(payload);
// { sub: 'acme-corp', product_id: 'my-cli', edition: 'professional', ... }Complete Integration Example
// src/license.ts
import { validateLicense, hasFeature, License } from '@atlashub/license-validator';
import fs from 'fs';
import path from 'path';
import os from 'os';
const PRODUCT_ID = 'my-awesome-cli';
/**
* Load license from multiple sources
*/
function loadLicenseKey(): string | undefined {
// 1. Environment variable
if (process.env.ATLASHUB_LICENSE) {
return process.env.ATLASHUB_LICENSE;
}
// 2. Home directory file
const homeFile = path.join(os.homedir(), '.atlashub-license');
if (fs.existsSync(homeFile)) {
return fs.readFileSync(homeFile, 'utf-8').trim();
}
// 3. Current directory file
if (fs.existsSync('license.key')) {
return fs.readFileSync('license.key', 'utf-8').trim();
}
return undefined;
}
/**
* Validate license at startup
*/
export function requireLicense(): License {
const licenseKey = loadLicenseKey();
const result = validateLicense(licenseKey, {
productId: PRODUCT_ID,
gracePeriodDays: 7
});
if (!result.valid) {
console.error('\n╔════════════════════════════════════════╗');
console.error('║ LICENSE VALIDATION FAILED ║');
console.error('╚════════════════════════════════════════╝\n');
console.error('Error:', result.error);
console.error('\nTo obtain a license:');
console.error(' 1. Contact: [email protected]');
console.error(' 2. Set via: export ATLASHUB_LICENSE="your-key"');
console.error(' 3. Or save to: ~/.atlashub-license\n');
process.exit(1);
}
const { license } = result;
// Expiration warning
if (license.daysRemaining <= 30) {
console.warn(`\n⚠️ Warning: License expires in ${license.daysRemaining} days!\n`);
}
return license;
}
/**
* Require a specific feature
*/
export function requireFeature(license: License, feature: string): void {
if (!hasFeature(license, feature)) {
console.error(`\n❌ Feature "${feature}" is not available in your ${license.edition} license.`);
console.error('Please upgrade to access this feature.\n');
process.exit(1);
}
}
/**
* Display license info
*/
export function showLicenseInfo(license: License): void {
console.log('\n┌────────────────────────────────────────┐');
console.log('│ LICENSE INFORMATION │');
console.log('├────────────────────────────────────────┤');
console.log(`│ Company: ${license.company.padEnd(26)}│`);
console.log(`│ Edition: ${license.edition.padEnd(26)}│`);
console.log(`│ Features: ${license.features.join(', ').padEnd(26).slice(0, 26)}│`);
console.log(`│ Expires: ${license.expiresAt.toLocaleDateString().padEnd(26)}│`);
console.log(`│ Remaining: ${(license.daysRemaining + ' days').padEnd(26)}│`);
console.log('└────────────────────────────────────────┘\n');
}Usage in your CLI:
// src/cli.ts
import { requireLicense, requireFeature, showLicenseInfo } from './license';
// Validate at startup
const license = requireLicense();
// Show info
showLicenseInfo(license);
// Check features for specific commands
if (command === 'advanced-export') {
requireFeature(license, 'export');
// ... run export
}
if (command === 'ai-assist') {
requireFeature(license, 'ai');
// ... run AI features
}License Editions
| Edition | Features | Typical Limits |
|---------|----------|----------------|
| Starter | core, basic-reports | 10 users, 5 GB, 1K API/day |
| Professional | core, ai, workflows, reports, entra, support | 50 users, 25 GB, 10K API/day |
| Enterprise | * (all features) | Unlimited |
Generating Licenses
Licenses are generated using the AtlasHub License Generator (internal tool):
# Generate a professional license for 1 year
license-generator generate \
--company "acme-corp" \
--product "my-awesome-cli" \
--edition professional \
--days 365
# Generate an enterprise license with custom features
license-generator generate \
--company "big-corp" \
--product "my-awesome-cli" \
--edition enterprise \
--features "*" \
--users 1000 \
--days 730Security
This package uses RSA-SHA256 asymmetric cryptography:
- Public key (included): Can only verify signatures
- Private key (secret): Required to create valid licenses
The public key cannot be used to forge licenses. This is the same security model used by SSL/TLS certificates.
Error Handling
const result = validateLicense(licenseKey, { productId: 'my-cli' });
if (!result.valid) {
switch (true) {
case result.error?.includes('No license key'):
// License not provided
break;
case result.error?.includes('expired'):
// License has expired
console.log('Expired on:', result.license?.expiresAt);
break;
case result.error?.includes('Invalid license'):
// Signature verification failed (tampered or wrong key)
break;
default:
// Other error
console.error(result.error);
}
}TypeScript Support
Full TypeScript support with exported types:
import type {
License,
LicenseLimits,
ValidationResult,
ValidateOptions
} from '@atlashub/license-validator';License
MIT - Free to use in commercial and open-source projects.
Support
- Issues: GitHub Issues
- Contact: [email protected]
