@eventcatalog/license
Version:
License verification for EventCatalog
282 lines (205 loc) • 8.65 kB
Markdown
# @eventcatalog/license
License verification library for EventCatalog supporting both offline JWT verification and online license key validation.
## Features
- **Dual verification modes** - Offline JWT tokens and online license keys
- **Ed25519 signatures** - Using industry-standard elliptic curve cryptography for offline verification
- **JWT-based offline licensing** - Standard token format with custom EventCatalog claims
- **Online license key validation** - Real-time verification with EventCatalog API
- **Input validation** - Secure license key format validation
- **Caching** - Verification results cached for performance
- **Machine binding** - Optional fingerprint-based license binding (offline mode)
- **Enhanced error handling** - Structured error codes for different failure scenarios
- **TypeScript** - Full type safety and IntelliSense support
## Installation
```bash
npm install @eventcatalog/license
```
## Quick Start
### Offline JWT Verification
```typescript
import { verifyOfflineLicense, isFeatureEnabled } from '@eventcatalog/license';
// Verify offline JWT license (auto-locates license file)
try {
const entitlements = await verifyOfflineLicense();
console.log('License valid for:', entitlements.org);
} catch (error) {
console.error('License verification failed:', error.message);
}
// Check if a plugin is enabled
if (isFeatureEnabled('@eventcatalog/generator-asyncapi')) {
console.log('AsyncAPI generator is licensed');
}
```
### Online License Key Verification
```typescript
import { verifyOnlineLicense } from '@eventcatalog/license';
// Verify online license key
try {
const isValid = await verifyOnlineLicense('XXXX-XXXX-XXXX-XXXX-XXXX-XXXX', '@eventcatalog/eventcatalog-scale');
if (isValid) {
console.log('License key is valid');
}
} catch (error) {
console.error('License key verification failed:', error.message);
}
```
## API Reference
### `verifyOfflineLicense(opts?: VerifyOptions): Promise<Entitlements>`
Verifies an offline JWT license and returns entitlements. Results are cached after first successful verification.
**Options:**
- `licensePath?: string` - Custom path to license file (overrides auto-discovery)
- `audience?: string` - Expected JWT audience (default: "EventCatalog")
- `issuer?: string` - Expected JWT issuer (default: "EventCatalog Ltd")
- `clockTolerance?: string` - Clock skew tolerance (default: "12h")
- `currentVersion?: string` - EventCatalog version for range validation
- `fingerprintProvider?: () => string | null` - Machine fingerprint provider
**Example with options:**
```typescript
const entitlements = await verifyOfflineLicense({
licensePath: '/custom/path/license.jwt',
audience: 'MyEventCatalog',
clockTolerance: '24h',
fingerprintProvider: () => getMachineId(),
});
```
### `verifyOnlineLicense(key: string, plugin: string): Promise<boolean>`
Verifies a license key online using the EventCatalog API.
**Parameters:**
- `key: string` - License key in format `XXXX-XXXX-XXXX-XXXX-XXXX-XXXX`
- `plugin: string` - Plugin identifier to verify against
**Supported plugins:**
- `@eventcatalog/eventcatalog-starter`
- `@eventcatalog/eventcatalog-scale`
- `@eventcatalog/eventcatalog-enterprise`
**Example:**
```typescript
const isValid = await verifyOnlineLicense('XXXX-XXXX-XXXX-XXXX-XXXX-XXXX', '@eventcatalog/eventcatalog-scale');
```
### `isFeatureEnabled(name: string): boolean`
Checks if a plugin or feature is enabled in the current license.
```typescript
// Check string plugin names
if (isFeatureEnabled('@eventcatalog/generator-openapi')) {
// Enable OpenAPI generator
}
// Also works with object-style plugin specs in license
```
### `getEntitlements(): Entitlements | null`
Returns currently cached entitlements without re-verification.
```typescript
const entitlements = getEntitlements();
if (entitlements?.org) {
console.log(`License for: ${entitlements.org}`);
}
```
## License Discovery
License files are located in this order:
1. `EC_LICENSE` environment variable (file path)
2. `./license.jwt` (current directory)
3. `/etc/eventcatalog/license.jwt` (system-wide)
## Environment Variables
### `EC_LICENSE`
Path to license file (overrides auto-discovery).
```bash
export EC_LICENSE="/path/to/my/license.jwt"
```
### `EC_PUBLIC_KEY_PATH`
Path to custom public key PEM file (overrides bundled key).
```bash
export EC_PUBLIC_KEY_PATH="/path/to/custom/public.pem"
```
## License Format
Licenses are JWT tokens with standard and EventCatalog-specific claims:
**Standard Claims:**
- `iss` - Issuer ("EventCatalog Ltd")
- `aud` - Audience ("EventCatalog")
- `iat` - Issued at timestamp
- `nbf` - Not before timestamp
- `exp` - Expiration timestamp
**EventCatalog Claims:**
- `licenseId` - Unique license identifier
- `org` - Organization name
- `plugins` - Enabled plugins array
- `fingerprint` - Machine binding fingerprint (optional)
**Example payload:**
```json
{
"iss": "EventCatalog Ltd",
"aud": "EventCatalog",
"iat": 1672531200,
"nbf": 1672531200,
"exp": 1735689600,
"licenseId": "ec_2025_000001",
"org": "Acme Corp",
"plugins": [
"@eventcatalog/generator-asyncapi",
{
"name": "@eventcatalog/generator-openapi",
"versionRange": "^1.0.0"
}
]
}
```
### Offline License Errors
| Error Code | Message | Description |
| ------------------------------ | ---------------------------------------------------------------------- | -------------------------------------------- |
| `LICENSE_EXPIRED` | License has expired | JWT token past expiration time |
| `LICENSE_NOT_YET_VALID` | License is not yet valid | JWT token not yet active |
| `LICENSE_VALIDATION_FAILED` | License validation failed - invalid issuer or audience | JWT claims validation failed |
| `LICENSE_SIGNATURE_INVALID` | License signature verification failed - invalid or tampered license | Digital signature verification failed |
| `LICENSE_FILE_NOT_FOUND` | License file not found | No license file found in discovery locations |
| `LICENSE_FILE_EMPTY` | License file is empty | License file exists but contains no content |
| `LICENSE_FINGERPRINT_MISMATCH` | License fingerprint mismatch - license is bound to a different machine | Machine binding violation |
### Online License Errors
| Error Code | Message | Description |
| ---------------------------- | -------------------------------------------------------------------------- | ----------------------------------------- |
| `INVALID_LICENSE_KEY_FORMAT` | Invalid license key format. Expected format: XXXX-XXXX-XXXX-XXXX-XXXX-XXXX | License key doesn't match required format |
## Building
```bash
# Install dependencies
npm install
# Build TypeScript
npm run build
# The compiled package will be in ./dist/
```
## Development
The package structure:
```
eventcatalog-license/
├── src/
│ ├── index.ts # Public API exports
│ ├── types.ts # TypeScript type definitions
│ ├── key.ts # Public key loading & validation
│ └── verify.ts # Verification logic (offline & online)
├── assets/
│ └── ec-ed25519-public.pem # Public key for offline verification
└── dist/ # Compiled output
```
### Testing License Verification
#### Offline JWT Testing
1. Create a test license file:
```bash
echo "your.jwt.token.here" > license.jwt
```
2. Test programmatically:
```typescript
import { verifyOfflineLicense } from '@eventcatalog/license';
try {
const entitlements = await verifyOfflineLicense();
console.log('✓ Valid license:', entitlements);
} catch (error) {
console.error('✗ Invalid license:', error.message);
}
```
#### Online License Key Testing
```typescript
import { verifyOnlineLicense } from '@eventcatalog/license';
try {
const isValid = await verifyOnlineLicense('XXXX-XXXX-XXXX-XXXX-XXXX-XXXX', '@eventcatalog/eventcatalog-scale');
console.log('✓ License key valid:', isValid);
} catch (error) {
console.error('✗ License key invalid:', error.message);
}
```
## License
MIT