- CachedValidityDays bumped 7 → 100 days. Covers the "user goes online once every 3 months for updates" use case without repeatedly nagging for re-auth. - LastValidationAt now uses response.ServerTime (signed Ed25519 by the server) rather than DateTime.UtcNow, so the client's local clock can't be used to forge a fresh validation date. - LastSeenUtc monotonic counter, persisted on every launch as max(stored, now). Combined with a 1h grace window: if the user rolls their PC clock backward to extend the offline cache, the rollback is detected (now < LastSeen - 1h) and the cache is invalidated → forced re-validation next time online. - CachedStatus persists the server-returned status (valid/expired/revoked/ invalid/machine_limit_exceeded) so a revocation done while the user is offline still shows correctly when they next launch with cached data. Auto-override to "expired" if entitlement date has passed (handles the valid → expired transition without needing a server round-trip). - ILicenseService.GetDecryptedKey() exposed for the new auto-revalidation flow. This puts the trust boundary in the right place: cached data is informational offline, the actual download authorization always re-validates against the server (the gate to download is /api/download-url/, which checks the live license state on each call). User can't fake offline-only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
29 lines
1.1 KiB
C#
29 lines
1.1 KiB
C#
using PSLauncher.Models;
|
|
|
|
namespace PSLauncher.Core.Licensing;
|
|
|
|
public interface ILicenseService
|
|
{
|
|
Task<LicenseValidationResponse> ValidateAsync(string licenseKey, CancellationToken ct);
|
|
LicenseValidationResponse? GetCached();
|
|
void SaveCached(string licenseKey, LicenseValidationResponse response);
|
|
void Clear();
|
|
bool HasLicense();
|
|
string GetMachineId();
|
|
bool CanDownloadVersion(LicenseValidationResponse? license, VersionManifest version);
|
|
|
|
/// <summary>
|
|
/// Demande au serveur une URL HMAC-signée valide 1 h pour le ZIP de cette version.
|
|
/// Retourne null si l'endpoint n'est pas dispo / pas configuré (ancien serveur),
|
|
/// auquel cas l'appelant retombe sur l'URL publique du manifest.
|
|
/// </summary>
|
|
Task<string?> GetSignedDownloadUrlAsync(string version, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Décrypte la clé de license stockée en cache (DPAPI CurrentUser). Retourne null
|
|
/// si pas de clé stockée ou si le déchiffrement échoue (cache déplacé vers une
|
|
/// autre machine ou autre user Windows).
|
|
/// </summary>
|
|
string? GetDecryptedKey();
|
|
}
|