Enterprise-grade license management for Laravel applications with offline verification, seat-based licensing, cryptographic security, and multi-product support through License Scopes.
Install the package via Composer:
composer require masterix21/laravel-licensingPublish the configuration and migrations:
php artisan vendor:publish --provider="LucaLongo\Licensing\LicensingServiceProvider"Run the migrations:
php artisan migrateGenerate your root certificate authority key:
php artisan licensing:keys:make-rootPassphrase required: The command encrypts keys using a passphrase stored in the
LICENSING_KEY_PASSPHRASEenvironment variable (configurable vialicensing.crypto.keystore.passphrase_env). If the variable is missing, the command will now prompt you to create one unless you run it with--silent/--no-interaction. Set it ahead of time (for exampleexport LICENSING_KEY_PASSPHRASE="your-strong-passphrase") to enable non-interactive automation.
Issue your first signing key:
php artisan licensing:keys:issue-signing --kid signing-key-1- π Offline Verification: PASETO v4 tokens with Ed25519 signatures
- πͺ Seat-Based Licensing: Control device/user limits per license
- π License Lifecycles: Activation, renewal, grace periods, and expiration
- π’ Multi-Product Support: License Scopes for product/software isolation
- π Two-Level Key Hierarchy: Root CA β Signing Keys for secure rotation
- π Comprehensive Audit Trail: Track all license and usage events
- π― Flexible Assignment: Polymorphic relationships for any model
- πΎ Flexible Key Management: Auto-generation, custom keys, optional retrieval
- π Secure Storage: Encrypted key storage with configurable retrieval
- β‘ High Performance: Optimized for enterprise workloads
use LucaLongo\Licensing\Models\License;
use LucaLongo\Licensing\Models\LicenseScope;
// Method 1: Auto-generate license key
$license = License::createWithKey([
'licensable_type' => User::class,
'licensable_id' => $user->id,
'max_usages' => 5,
'expires_at' => now()->addYear(),
]);
// The generated key is available immediately after creation
$licenseKey = $license->license_key; // e.g., "LIC-A3F2-B9K1-C4D8-E5H7"
// Method 2: Provide your own license key
$customKey = 'CUSTOM-KEY-12345';
$license = License::createWithKey([
'licensable_type' => User::class,
'licensable_id' => $user->id,
'max_usages' => 5,
'expires_at' => now()->addYear(),
], $customKey);
// Method 3: Traditional approach with hash only
$activationKey = Str::random(32);
$license = License::create([
'key_hash' => License::hashKey($activationKey),
'licensable_type' => User::class,
'licensable_id' => $user->id,
'license_scope_id' => $scope->id ?? null, // Optional scope
'max_usages' => 5,
'expires_at' => now()->addYear(),
]);
$license->activate();use LucaLongo\Licensing\Facades\Licensing;
$usage = Licensing::register(
$license,
'device-fingerprint-hash',
['device_name' => 'MacBook Pro']
);$token = Licensing::issueToken($license, $usage, [
'ttl_days' => 7,
]);if ($license->isUsable()) {
$remainingDays = $license->daysUntilExpiration();
$availableSeats = $license->getAvailableSeats();
}// Retrieve the original license key (if stored encrypted)
$originalKey = $license->retrieveKey();
// Check if retrieval is available
if ($license->canRetrieveKey()) {
$key = $license->retrieveKey();
}
// Regenerate a license key
if ($license->canRegenerateKey()) {
$newKey = $license->regenerateKey();
// Old key no longer works, new key is returned
}
// Verify a license key
$isValid = $license->verifyKey($providedKey);
// Find license by key
$license = License::findByKey($licenseKey);The package provides flexible license key management with three configurable services:
// config/licensing.php
'services' => [
'key_generator' => \LucaLongo\Licensing\Services\EncryptedLicenseKeyGenerator::class,
'key_retriever' => \LucaLongo\Licensing\Services\EncryptedLicenseKeyRetriever::class,
'key_regenerator' => \LucaLongo\Licensing\Services\EncryptedLicenseKeyRegenerator::class,
],
'key_management' => [
'retrieval_enabled' => true, // Allow retrieving original keys
'regeneration_enabled' => true, // Allow regenerating keys
'key_prefix' => 'LIC', // Prefix for generated keys
'key_separator' => '-', // Separator for key segments
],You can implement your own key management services:
use LucaLongo\Licensing\Contracts\LicenseKeyGeneratorContract;
class CustomKeyGenerator implements LicenseKeyGeneratorContract
{
public function generate(?License $license = null): string
{
// Your custom key generation logic
return 'CUSTOM-' . strtoupper(bin2hex(random_bytes(8)));
}
}Then register it in the config:
'services' => [
'key_generator' => \App\Services\CustomKeyGenerator::class,
],- Hashed Storage: Keys are always stored as salted SHA-256 hashes
- Encrypted Retrieval: Original keys can be stored encrypted (optional)
- Regeneration History: Previous key hashes are maintained for audit
- Configurable: Disable retrieval/regeneration for maximum security
License Scopes enable you to manage multiple products/software with isolated signing keys, preventing key compromise in one product from affecting others.
use LucaLongo\Licensing\Models\LicenseScope;
// Create scope for your ERP system
$erpScope = LicenseScope::create([
'name' => 'ERP System',
'slug' => 'erp-system',
'identifier' => 'com.company.erp',
'key_rotation_days' => 90,
'default_max_usages' => 100,
]);
// Create scope for your mobile app
$mobileScope = LicenseScope::create([
'name' => 'Mobile App',
'slug' => 'mobile-app',
'identifier' => 'com.company.mobile',
'key_rotation_days' => 30, // More frequent rotation
'default_max_usages' => 3,
]);# Issue signing key for ERP system
php artisan licensing:keys:issue-signing --scope erp-system --kid erp-key-2024
# Issue signing key for mobile app
php artisan licensing:keys:issue-signing --scope mobile-app --kid mobile-key-2024// Create license for ERP system
$erpLicense = License::create([
'key_hash' => License::hashKey($erpActivationKey),
'license_scope_id' => $erpScope->id, // Scoped to ERP
'licensable_type' => Company::class,
'licensable_id' => $company->id,
'max_usages' => 100,
'expires_at' => now()->addYear(),
]);
// Create license for mobile app
$mobileLicense = License::create([
'key_hash' => License::hashKey($mobileActivationKey),
'license_scope_id' => $mobileScope->id, // Scoped to mobile
'licensable_type' => User::class,
'licensable_id' => $user->id,
'max_usages' => 3,
'expires_at' => now()->addMonths(6),
]);
// Tokens are automatically signed with the correct scope-specific key
$erpToken = Licensing::issueToken($erpLicense, $erpUsage);
$mobileToken = Licensing::issueToken($mobileLicense, $mobileUsage);- Key Isolation: Each product has its own signing keys
- Independent Rotation: Different rotation schedules per product
- Blast Radius Limitation: Key compromise affects only one product
- Product-Specific Defaults: Configure max usages, trial days per scope
- Flexible Management: Programmatic or CLI-based key management
Client package for Laravel applications that need to validate licenses against a licensing server.
composer require masterix21/laravel-licensing-clientFeatures:
- Automatic license validation
- Offline token verification
- Usage registration and heartbeat
- Caching for performance
- Middleware for route protection
Complete admin panel for Filament to manage licenses, monitor usage, and handle key rotation.
composer require masterix21/laravel-licensing-filament-managerFeatures:
- License management dashboard
- Usage analytics and monitoring
- Key rotation interface
- Scope management
- Audit trail viewer
- Token generation tools
Run the test suite:
composer testRun tests with coverage:
composer test-coverageStatic analysis:
composer analyseFor comprehensive documentation visit the documentation.
This package includes comprehensive guidelines for AI coding assistants. See AI_GUIDELINES.md for:
- Claude Code integration patterns
- ChatGPT/Codex usage examples
- GitHub Copilot autocomplete triggers
- Junie configuration and patterns
- PHP 8.3+
- Laravel 12.0+
- OpenSSL extension
- Sodium extension (for PASETO tokens and Ed25519 signatures)
If you find this package useful and want to support its continued development, please consider sponsoring:
Your sponsorship helps:
- π Maintain and improve the package
- π Keep documentation up-to-date
- π Fix bugs and add new features
- π¬ Provide community support
- π Ensure security updates
Contributions are welcome! Please see CONTRIBUTING.md for details.
If you discover any security-related issues, please email security@example.com instead of using the issue tracker. All security vulnerabilities will be promptly addressed.
The MIT License (MIT). Please see License File for more information.