masterix21 / laravel-licensing
Laravel licensing package with polymorphic assignment to any model, activation keys, expirations/renewals, and seat control via LicenseUsage. Supports offline verification with public-key–signed tokens, a CLI to generate/rotate/revoke keys, and an extensible architecture via config and contracts.
Fund package maintenance!
Requires
- php: ^8.3
- ext-openssl: *
- ext-sodium: *
- illuminate/console: ^12.0 || ^13.0
- illuminate/contracts: ^12.0 || ^13.0
- illuminate/database: ^12.0 || ^13.0
- illuminate/http: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
- paragonie/paseto: ^3.5
- spatie/crypto: ^2.0
- spatie/laravel-package-tools: ^1.16
- spatie/laravel-sluggable: ^3.7 || ^3.8
- symfony/uid: ^7.0 || ^8.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.5 || ^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- spatie/pest-plugin-test-time: ^2.2
README
A licensing package for Laravel with offline verification, seat management, cryptographic key rotation, and multi-product support.
Features
- Offline verification — PASETO v4 tokens signed with Ed25519, verifiable without a server connection
- Seat-based licensing — control how many devices, users, or instances can use a license
- Full lifecycle management — activation, renewal, grace periods, expiration, suspension
- Multi-product scopes — isolate signing keys per product so a compromise doesn't spread
- Two-level key hierarchy — root CA signs short-lived signing keys; rotate without breaking clients
- Audit trail — append-only log of every license, usage, and key event
- Polymorphic assignment — attach a license to any Eloquent model
- Flexible key management — auto-generation, custom keys, encrypted storage with optional retrieval
Requirements
- PHP 8.3+
- Laravel 12 or 13
ext-opensslandext-sodium
Installation
composer require masterix21/laravel-licensing
Publish config and migrations, then migrate:
php artisan vendor:publish --provider="LucaLongo\Licensing\LicensingServiceProvider"
php artisan migrate
Generate your root key and first signing key:
php artisan licensing:keys:make-root php artisan licensing:keys:issue-signing --kid signing-key-1
The root key is encrypted with the passphrase from the
LICENSING_KEY_PASSPHRASEenv variable. If missing, the command will prompt you to set one (unless running with--no-interaction).
MySQL / MariaDB notes
Migrations are tested against MySQL 8 and MariaDB 11 in CI. Two points worth knowing if you run into errors on older setups:
- Identifier 1059 errors (
Identifier name '…' is too long): the package already ships explicit short names for the only composite indexes that would exceed MySQL's 64-char limit. If you add custom migrations on top, remember to pass a short alias tomorphs()/index()when the auto-generated name would overflow. - Key length 1071 errors (
Specified key was too long): only relevant on MySQL < 5.7 or MariaDB < 10.2 with InnoDB's old row format. AddSchema::defaultStringLength(191);in yourAppServiceProvider::boot()as per the Laravel docs. This is unrelated to identifier length — it caps the indexed VARCHAR prefix, not the index name.
Quick Start
Create and activate a license
use LucaLongo\Licensing\Models\License; $license = License::createWithKey([ 'licensable_type' => User::class, 'licensable_id' => $user->id, 'max_usages' => 5, 'expires_at' => now()->addYear(), ]); // The plain-text key is available right after creation $licenseKey = $license->license_key; // e.g. "LIC-A3F2-B9K1-C4D8-E5H7" $license->activate();
You can also pass your own key as second argument to createWithKey(), or use the lower-level License::create() with a pre-hashed key via License::hashKey().
Register a device (seat)
use LucaLongo\Licensing\Facades\Licensing; $usage = Licensing::register( $license, 'device-fingerprint-hash', ['device_name' => 'MacBook Pro'] );
Issue an offline token
$token = Licensing::issueToken($license, $usage, [ 'ttl_days' => 7, ]);
Check license status
if ($license->isUsable()) { $remainingDays = $license->daysUntilExpiration(); $availableSeats = $license->getAvailableSeats(); }
Key retrieval and regeneration
$originalKey = $license->retrieveKey(); // if encrypted storage is enabled $newKey = $license->regenerateKey(); // old key stops working $isValid = $license->verifyKey($providedKey); $license = License::findByKey($licenseKey);
Multi-Product Scopes
Scopes let you manage multiple products with independent signing keys and rotation schedules.
use LucaLongo\Licensing\Models\LicenseScope; $scope = LicenseScope::create([ 'name' => 'ERP System', 'slug' => 'erp-system', 'identifier' => 'com.company.erp', 'key_rotation_days' => 90, 'default_max_usages' => 100, ]);
Issue a signing key for this scope:
php artisan licensing:keys:issue-signing --scope erp-system --kid erp-key-2024
When you create a license with a license_scope_id, tokens are automatically signed with the scope's key. A compromised key in one scope doesn't affect the others.
Key Management
Key generation, retrieval, and regeneration are handled by pluggable 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, ],
Implement LicenseKeyGeneratorContract (or the retriever/regenerator contracts) to plug in your own logic.
Related Packages
| Package | Description |
|---|---|
| laravel-licensing-client | Client package for validating licenses against a server — offline verification, usage registration, route middleware |
| laravel-licensing-filament-manager | Filament admin panel for license management, usage monitoring, key rotation, and audit trail |
Testing
composer test # run tests composer test-coverage # with coverage composer analyse # static analysis
Documentation
Full documentation is available in the docs folder.
Sponsor
If this package is useful to you, consider sponsoring its development.
Contributing
Contributions are welcome. See CONTRIBUTING.md for details.
Security
If you discover a security vulnerability, please email security@example.com instead of using the issue tracker.
License
MIT. See LICENSE.md.