Laravel License Onboarding, Activation, and Offline Validation
Laravel License Onboarding, Activation, and Offline Validation
Overview
This walkthrough shows a complete, production-safe flow for Laravel:
- Install
getkeymanager/laravel-sdk. - Configure
config/getkeymanager.php. - Add a license capture page with controller + route + Blade view.
- Assign and activate the license using the request domain + IP.
- Save the returned
.licfile and related metadata. - Parse the
.licfile offline usingparseLicenseFile. - Enforce licensing in routes, middleware, jobs, queues, and commands.
- Send telemetry data to keep usage visibility and security signals.
Goal: keep licensing server-side, deterministic, and tamper-resistant—without blocking your app during temporary API outages.
1) Install the SDK
composer require getkeymanager/laravel-sdkOptional config publish:
php artisan vendor:publish --tag=getkeymanager-config2) Configure config/getkeymanager.php
Update .env:
LICENSE_MANAGER_API_KEY=your-api-key-here
LICENSE_MANAGER_BASE_URL=https://api.getkeymanager.com
LICENSE_MANAGER_ENVIRONMENT=production
LICENSE_MANAGER_VERIFY_SIGNATURES=true
LICENSE_MANAGER_PUBLIC_KEY_FILE=storage_path:app/keys/product_public.pemThen in config/getkeymanager.php ensure the keys match:
return [
'api_key' => env('LICENSE_MANAGER_API_KEY'),
'base_url' => env('LICENSE_MANAGER_BASE_URL', 'https://api.getkeymanager.com'),
'environment' => env('LICENSE_MANAGER_ENVIRONMENT', 'production'),
'verify_signatures' => env('LICENSE_MANAGER_VERIFY_SIGNATURES', true),
'public_key_file' => env('LICENSE_MANAGER_PUBLIC_KEY_FILE'),
'timeout' => env('LICENSE_MANAGER_TIMEOUT', 30),
'cache_enabled' => env('LICENSE_MANAGER_CACHE_ENABLED', true),
'cache_ttl' => env('LICENSE_MANAGER_CACHE_TTL', 300),
'middleware' => [
'redirect_to' => '/license-required',
'cache_in_session' => true,
'session_key' => 'license_validation',
],
];3) Create a license capture page (controller + route + Blade)
Route
// routes/web.php
use App\Http\Controllers\LicenseController;
Route::get('/license', [LicenseController::class, 'show']);
Route::post('/license', [LicenseController::class, 'store']);Controller
// app/Http/Controllers/LicenseController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use GetKeyManager\Laravel\Facades\GetKeyManager;
class LicenseController extends Controller
{
public function show()
{
return view('license');
}
public function store(Request $request)
{
$request->validate([
'license_key' => ['required', 'string'],
'email' => ['required', 'email'],
]);
$licenseKey = $request->input('license_key');
$email = $request->input('email');
// Domain + IP from the request
$domain = $request->getHost();
$ip = $request->ip();
// 1) Assign license to the customer
$assign = GetKeyManager::assignLicenseKey($licenseKey, $email);
// 2) Activate the license with domain + IP metadata
$activation = GetKeyManager::activateLicense($licenseKey, [
'domain' => $domain,
'ip' => $ip,
]);
// 3) Store the .lic file if returned
$licContent = $activation['response']['data']['licFileContent'] ?? null;
if ($licContent) {
$path = "licenses/{$licenseKey}.lic";
Storage::disk('local')->put($path, $licContent);
}
// Optional: store the activation metadata for internal auditing
$metaPath = "licenses/{$licenseKey}.json";
Storage::disk('local')->put($metaPath, json_encode([
'license_key' => $licenseKey,
'email' => $email,
'domain' => $domain,
'ip' => $ip,
'activation' => $activation,
], JSON_PRETTY_PRINT));
return redirect('/license')->with('status', 'License activated successfully.');
}
}Blade view
<!-- resources/views/license.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
<h1>Activate Your License</h1>
@if (session('status'))
<div class="alert alert-success">{{ session('status') }}</div>
@endif
<form method="POST" action="/license">
@csrf
<div class="mb-3">
<label class="form-label">License Key</label>
<input name="license_key" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Email Address</label>
<input name="email" type="email" class="form-control" required>
</div>
<button class="btn btn-primary">Activate</button>
</form>
</div>
@endsection4) Parse the .lic file offline with parseLicenseFile
Once the .lic file is stored, you can parse it offline with the PHP SDK validator. This is ideal for offline verification, forensic checks, and server-side audits.
use GetKeyManager\SDK\Config\Configuration;
use GetKeyManager\SDK\Cache\CacheManager;
use GetKeyManager\SDK\Http\HttpClient;
use GetKeyManager\SDK\SignatureVerifier;
use GetKeyManager\SDK\StateResolver;
use GetKeyManager\SDK\StateStore;
use GetKeyManager\SDK\Validation\LicenseValidator;
$publicKey = file_get_contents(storage_path('app/keys/product_public.pem'));
$licContent = file_get_contents(storage_path('app/licenses/LIC-XXXX.lic'));
$config = new Configuration([
'apiKey' => config('getkeymanager.api_key'),
'baseUrl' => config('getkeymanager.base_url'),
'environment' => config('getkeymanager.environment'),
'verifySignatures' => true,
'publicKey' => $publicKey,
'cacheEnabled' => false,
'cacheTtl' => 0,
]);
$verifier = new SignatureVerifier($publicKey);
$stateResolver = new StateResolver($verifier, $config->getEnvironment(), $config->getProductId());
$stateStore = new StateStore($verifier, 3600);
$cache = new CacheManager(false, 0);
$httpClient = new HttpClient($config, $verifier, $stateResolver);
$validator = new LicenseValidator(
$config,
$httpClient,
$cache,
$stateStore,
$stateResolver,
$verifier
);
$licenseData = $validator->parseLicenseFile($licContent, $publicKey);
// $licenseData now contains decrypted fields like license_key, expires_at, features, etc.
If you want full offline validation (signature + expiry + hardware checks), use:
$result = GetKeyManager::validateOfflineLicense($licenseData, [ 'publicKey' => $publicKey, 'hardwareId' => GetKeyManager::generateHardwareId(), ]);
5) Protect routes, middleware, jobs, and commands
Route guards
Route::middleware(['license.validate'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});Middleware enforcement
// app/Http/Middleware/RequireLicense.php
public function handle($request, Closure $next)
{
$licenseKey = $request->header('X-License-Key');
$result = GetKeyManager::validateLicense($licenseKey, [
'domain' => $request->getHost(),
]);
if (!($result['response']['data']['valid'] ?? false)) {
return redirect('/license');
}
return $next($request);
}Console commands
// app/Console/Commands/GenerateReports.php
public function handle()
{
$licenseKey = config('app.license_key');
$state = GetKeyManager::resolveLicenseState($licenseKey);
if (!$state->isValid()) {
$this->error('License is not active.');
return 1;
}
// Run command
}Jobs and queues
// app/Jobs/ProcessReport.php
public function handle()
{
$licenseKey = config('app.license_key');
$state = GetKeyManager::resolveLicenseState($licenseKey);
if (!$state->allows('reporting')) {
$this->release(30);
return;
}
// Process job
}6) Send telemetry data
Telemetry helps you spot misuse, suspicious activity, and feature adoption.
GetKeyManager::sendTelemetry($licenseKey, [
'event' => 'feature_used',
'feature' => 'pdf_export',
'domain' => request()->getHost(),
'ip' => request()->ip(),
'timestamp' => now()->toIso8601String(),
]);Security tips for Laravel apps
- Never trust the client for license state; always validate server-side.
- Use domain binding for web apps (
$request->getHost()), and hardware IDs for on-prem installs. - Cache license state for short periods to reduce API calls without going stale.
- Enforce in multiple layers: middleware + controllers + service classes + queues.
- Store .lic files securely (private storage, not public web directories).
- Log activation results to your audit trail for support and forensics.
Next steps
- Add feature-gate checks with
GetKeyManager::checkFeature(). - Use
assignAndActivateLicenseKey()for single-step onboarding. - Rotate keys safely by updating
public_key_fileand refreshing cached state.
If you need help integrating, reach out to support or explore the SDK examples folder for a full project scaffold.
Last updated on