Logika.License 1.0.0
Logika.License
Libreria .NET per la creazione, verifica e migrazione delle licenze software Logika.
Compatibile con .NET 6, .NET 8 e .NET 10.
Installazione
dotnet add package Logika.License --source <url-server-nuget-privato>
Concetti chiave
La libreria gestisce 3 chiavi (i nomi sono storici, non modificabili):
| Chiave | Significato |
|---|---|
LicenseKey |
Identifica la licenza, generata dall'hardware della macchina (CreateLicenseKey) |
CheckKey |
Chiave per la verifica offline della licenza, in assenza di connettività |
ActivationKey |
Contiene lo stato della licenza restituito dal server |
Il punto d'ingresso della libreria è l'interfaccia ILicenseService. L'implementazione di default, LicenseService, comunica con il server delle licenze via HTTP.
Utilizzo manuale (senza DI)
using Logika.License.Abstractions;
using Logika.License.Services;
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://license.miaazienda.it")
};
ILicenseService service = new LicenseService(httpClient);
// Genera una nuova LicenseKey legata all'hardware della macchina corrente
string licenseKey = service.CreateLicenseKey();
HttpClientdeve avereBaseAddressimpostato sull'URL del server delle licenze. La sua creazione/gestione (riuso, timeout, ecc.) resta a carico dell'utilizzatore, come da best practice .NET.API key: in uso manuale impostala tu sull'
HttpClient, ad esempiohttpClient.DefaultRequestHeaders.Add("ApiKey", "la-tua-api-key"); con la DI basta impostareLogikaLicenseOptions.ApiKey.
Utilizzo con Dependency Injection
Registra la libreria nel container all'avvio dell'applicazione (Program.cs / Startup.cs):
using Logika.License.DependencyInjection;
builder.Services.AddLogikaLicense(options =>
{
options.BaseAddress = new Uri("https://license.miaazienda.it");
// Opzionale: API key inviata ad ogni richiesta nell'header "ApiKey"
options.ApiKey = "la-tua-api-key";
// Opzionale: abilita il salvataggio automatico delle 3 chiavi su file JSON
options.KeyStoreFilePath = @"C:\ProgramData\MiaApp\license.json";
// Opzionale: configurazione aggiuntiva dell'HttpClient (header, timeout, ecc.)
options.ConfigureHttpClient = client =>
{
client.Timeout = TimeSpan.FromSeconds(10);
};
});
Poi inietta ILicenseService (e, se configurato, ILicenseKeyStore) dove serve:
public class LicenseController
{
private readonly ILicenseService _licenseService;
private readonly ILicenseKeyStore _keyStore;
public LicenseController(ILicenseService licenseService, ILicenseKeyStore keyStore)
{
_licenseService = licenseService;
_keyStore = keyStore;
}
// ...
}
Utilizzo ad alto livello (ILicenseManager)
Se l'applicazione non vuole gestire manualmente il flusso della licenza
(caricare/salvare le chiavi, creare la LicenseKey, distinguere online/offline, ecc.),
può affidarsi a ILicenseManager, che orchestra automaticamente ILicenseService
e ILicenseKeyStore. Basta passare i dati anagrafici e si riceve un esito leggibile.
ILicenseManagerè registrato automaticamente in DI quando impostiLogikaLicenseOptions.KeyStoreFilePath(serve uno store su cui persistere le chiavi).
Esempio: verifica automatica della licenza
using Logika.License.Abstractions;
using Logika.License.Models;
// Inietta ILicenseManager nel tuo controller/servizio
public class StartupCheck
{
private readonly ILicenseManager _licenseManager;
public StartupCheck(ILicenseManager licenseManager)
=> _licenseManager = licenseManager;
public async Task VerifyAsync(CancellationToken ct = default)
{
// Una verifica per ogni prodotto software installato
LicenseCheckResult result = await _licenseManager.EnsureLicenseAsync(
new LicenseCheckContext
{
Customer = "ACME S.p.A.",
Company = "server-01",
User = "mario.rossi",
SoftwareName = "MioGestionale",
SoftwareVersion = "4.2.0"
}, ct);
if (result.IsEnabled)
{
// licenza valida o in demo: si può usare l'applicazione
App.UI.Show($"Licenza OK - utenze attive: {result.CalCount} - {result.Reason}");
}
else
{
// result.Status indica il motivo (ExpiredLicense, ExpiredDemo, InvalidLicense...)
App.UI.ShowError(result.Reason);
}
// La LicenseKey usata (creata automaticamente al primo avvio)
string key = result.LicenseKey;
}
}
Nella prima esecuzione EnsureLicenseAsync genera automaticamente la LicenseKey
legata all'hardware e la salva; nelle successive la riutilizza. CheckKey e
ActivationKey aggiornate vengono salvate ad ogni verifica andata a buon fine.
Ogni prodotto software (identificato da SoftwareName) ha la sua licenza: basta
chiamare EnsureLicenseAsync una volta per prodotto.
Conoscere lo stato delle licenze salvate
GetLicensesAsync restituisce una fotografia (stato + CAL) di tutte le licenze
persistite, senza contattare il server:
IReadOnlyList<LicenseState> states = await licenseManager.GetLicensesAsync(ct);
foreach (LicenseState state in states)
{
Console.WriteLine($"{state.SoftwareName}: {state.Status} - utenze attive: {state.CalCount}");
}
// Stato di un singolo prodotto (null se non ancora salvato)
LicenseState? gestione = await licenseManager.GetLicenseAsync("MioGestionale", ct);
Ottenere la sola LicenseKey di un prodotto
string licenseKey = await licenseManager.GetOrCreateLicenseKeyAsync("MioGestionale");
Esito (LicenseCheckResult)
| Proprietà | Descrizione |
|---|---|
SoftwareName |
Prodotto software a cui appartiene la licenza verificata |
Status |
Stato puntuale (ValidLicense, DemoLicense, ExpiredDemo, ExpiredLicense, InvalidLicense) |
IsEnabled |
true se la licenza è valida o in demo attiva |
LicenseKey |
La LicenseKey corrente (creata automaticamente al primo utilizzo) |
CalCount |
Numero di CAL (utenze attive) associate alla licenza |
Reason |
Motivazione dello stato, in italiano, pronta per l'utente finale |
Error |
Eccezione tecnica intercettata (es. server irraggiungibile), null se nessun errore |
Salvataggio delle chiavi (ILicenseKeyStore)
La libreria fornisce un'implementazione di default, JsonLicenseKeyStore, che salva in un
file JSON le licenze di più prodotti software, ognuna identificata dal SoftwareName:
using Logika.License.Persistence;
var keyStore = new JsonLicenseKeyStore(@"C:\ProgramData\MiaApp\license.json");
await keyStore.SaveAsync(new StoredLicenseKeys
{
SoftwareName = "MioGestionale",
LicenseKey = licenseKey,
CheckKey = result.CheckKey,
ActivationKey = result.ActivationKey
});
// Licenza di un singolo prodotto (null se non ancora salvata)
StoredLicenseKeys? saved = await keyStore.LoadAsync("MioGestionale");
// Tutte le licenze salvate
IReadOnlyList<StoredLicenseKeys> all = await keyStore.LoadAllAsync();
Se serve un meccanismo di salvataggio diverso (registro di sistema, database, key vault, ecc.), è sufficiente implementare ILicenseKeyStore:
public interface ILicenseKeyStore
{
Task SaveAsync(StoredLicenseKeys storedKeys, CancellationToken cancellationToken = default);
Task<StoredLicenseKeys?> LoadAsync(string softwareName, CancellationToken cancellationToken = default);
Task<IReadOnlyList<StoredLicenseKeys>> LoadAllAsync(CancellationToken cancellationToken = default);
}
API di ILicenseService
| Metodo | Descrizione |
|---|---|
CreateLicenseKey() |
Genera una nuova LicenseKey legata all'hardware della macchina corrente |
CheckLicense(LicenseValidationRequest) |
Verifica una licenza (online, con fallback offline se il server non è raggiungibile) |
MigrateLicense(LicenseMigrationRequest) |
Migra una licenza da una vecchia LicenseKey a una nuova |
GetCALsFromActivationKey(activationKey) |
Estrae il numero di CAL (Client Access License) da una ActivationKey |
LastException |
Ultima eccezione intercettata internamente durante una CheckLicense fallita (utile per diagnostica) |
Esempio: verifica di una licenza
var request = new LicenseValidationRequest
{
Customer = "ACME S.p.A.",
Company = "server-01",
User = "mario.rossi",
SoftwareName = "MioGestionale",
SoftwareVersion = "4.2.0",
LicenseKey = savedKeys.LicenseKey,
CheckKey = savedKeys.CheckKey,
LastActivationKey = savedKeys.ActivationKey
};
LicenseValidationResult result = await service.CheckLicense(request);
if (result.IsEnabled)
{
// licenza valida o in demo -> aggiorno le chiavi salvate
await keyStore.SaveAsync(new StoredLicenseKeys
{
LicenseKey = request.LicenseKey,
CheckKey = result.CheckKey,
ActivationKey = result.ActivationKey
});
}
else
{
// result.Status indica il motivo (InvalidLicense, ExpiredLicense, ExpiredDemo...)
}
Esempio: migrazione di una licenza
var migrationResult = await service.MigrateLicense(new LicenseMigrationRequest
{
OldLicenseKey = vecchiaLicenseKey,
NewLicenseKey = nuovaLicenseKey,
Customer = "ACME S.p.A.",
Company = "server-02",
User = "mario.rossi",
SoftwareName = "MioGestionale",
SoftwareVersion = "4.2.0"
});
if (migrationResult.Success)
{
// migrazione completata
}
Modelli pubblici
LicenseValidationRequest— dati necessari perCheckLicenseLicenseValidationResult— esito diCheckLicense(Status,IsEnabled,CheckKey,ActivationKey)StoredLicenseKeys— le 3 chiavi da persistere per un prodotto (SoftwareName,LicenseKey,CheckKey,ActivationKey)LicenseState— fotografia di una licenza persistita (stato,IsEnabled,CalCount, chiavi)LicenseCheckContext/LicenseCheckResult— dati/esito dell'uso ad alto livello conILicenseManagerLicenseMigrationRequest/LicenseMigrationResult— richiesta/esito diMigrateLicenseLicenseStatus(enum) —ValidLicense,DemoLicense,ExpiredDemo,ExpiredLicense,InvalidLicense
Vocabolario
Per evitare ambiguità, ogni tipo ha un ruolo preciso:
| Tipo | Ruolo |
|---|---|
ILicenseService / LicenseService |
entry point della libreria: genera chiavi, verifica, migra, estrae CAL |
ILicenseManager / LicenseManager |
livello ad alto livello che orchetra automaticamente service + store (più licenze, una per prodotto) |
ILicenseKeyStore / JsonLicenseKeyStore |
persistenza delle licenze dei prodotti (file JSON di default) |
LicenseServerClient (interno) |
client HTTP verso il server (api/Licenses/*) |
Test
Il progetto Logika.License.UnitTests (xUnit) copre la verifica online/offline, la decodifica delle chiavi,
il multi-prodotto di ILicenseManager e la persistenza, senza rete né hardware reali.
Per eseguirli da Visual Studio: Test > Test Explorer > Run All (oppure dotnet test da terminale).
Test end-to-end contro il server reale
I test E2E (progetto Logika.License.IntegrationTests) colpiscono la tua API delle licenze
(https://localhost:7092) con l'hardware reale della macchina e vengono saltati se non configurati.
Per attivarli:
- Apri
Logika.License.IntegrationTests/appsettings.e2e.jsone compila:{ "baseUrl": "https://localhost:7092", "apiKey": "0123456789" } - Avvia il server LogikaApi (F5 sul progetto
LogikaApi) e rilancia i test da Test Explorer.
In alternativa (per CI o terminale) si possono usare le variabili d'ambiente
LICENSE_API_BASE_URL e LICENSE_API_KEY, che hanno precedenza sul file.
La suite E2E è autocontenuta: un fixture di collection chiama una sola volta POST /api/Licenses/ResetLicenses
(endpoint dev-only di LogikaApi) prima dell'esecuzione, così le licenze di test (che usano il prefisso
integration-test-server-) vengono azzerate a ogni run e il database riparte pulito.
I test E2E coprono:
CheckLicenseIntegrationTests— chiave nuova → demo abilitata conActivationKeyben formata (80 caratteri, controllo00DE, data =LicenseKey+ 100 giorni, 1 CAL); doppia verifica della stessa chiave; API key errata → 401 → fallback offline; server irraggiungibile → verifica offline conCheckKeyvalida (e senzaCheckKey→ licenza non valida).MigrateLicenseIntegrationTests— migrazione completa (vecchia chiave disattivata, nuova attiva); migrazione già eseguita → rifiutata; vecchia chiave sconosciuta →Success=false.LicenseManagerIntegrationTests—EnsureLicenseAsyncend-to-end (crea e persiste le 3 chiavi su file JSON, riusa laLicenseKey, restituisce stato/CAL) eGetLicensesAsyncmulti-prodotto.LicenseAdminEndpointsIntegrationTests— tramiteFindLicenses/GetLicense/GetLicensesverifica che i campi inviati dalla libreria arrivino sul database senza alterazioni, che la licenza creata sia visibile e che il codice cliente sia scritto solo alla prima verifica (first-write-wins).
I test E2E creano licenze demo reali sul server di sviluppo (una per run). Per non interferire con la logica anti-licenze-multiple del server, ogni run usa valori univoci (
ServerName/User/SoftwareNamecon suffisso casuale).
Note
- Tutto ciò che si trova nel namespace
Logika.License.Apiè dettaglio implementativo interno e non fa parte della superficie pubblica della libreria. - I nomi
LicenseKey,CheckKeyeActivationKeysono storici e volutamente non descrittivi del loro reale significato tecnico; fanno riferimento alla tabella nella sezione "Concetti chiave" sopra.
No packages depend on Logika.License.
.NET 10.0
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- System.Management (>= 10.0.10)
.NET 6.0
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- System.Management (>= 8.0.0)
.NET 8.0
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- System.Management (>= 10.0.10)
| Version | Downloads | Last updated |
|---|---|---|
| 1.0.0 | 2 | 08/07/2026 |