license: force UTC + fixed-Z date format on both sides
Symptom: "Réponse serveur non authentifiée (signature invalide)" on
license activation. Root cause: PHP \DateTimeInterface::ATOM emits the
server's local timezone offset (e.g. +02:00 on a CET host), while the
C# canonicalizer emitted +00:00 after ToUniversalTime(). Same instant,
different string, different bytes, signature mismatch.
Server (ValidateLicense.php):
- All timestamps now built with `new DateTimeZone('UTC')` and formatted
as 'Y-m-d\TH:i:s\Z' — fixed string, no offset variation.
- Reads issued_at / download_entitlement_until from MySQL as UTC; the
display is consistent with what the client sees.
Client (LicenseService.cs):
- FormatDateAtom now produces "yyyy-MM-ddTHH:mm:ssZ" with literal Z and
handles null safely (previous version would have produced "Z" alone
for a null input thanks to string + null concatenation).
Both sides therefore agree on the canonical bytes for any datetime,
including across daylight savings transitions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,16 +71,22 @@ final class ValidateLicense
|
||||
)->execute([$lic['id'], $machineId, $machineLabel ?: null, $now, $now]);
|
||||
}
|
||||
|
||||
// Construit la réponse, signe-la, renvoie
|
||||
$entUntil = (new \DateTimeImmutable($lic['download_entitlement_until']))->format(\DateTimeInterface::ATOM);
|
||||
$serverTime = (new \DateTimeImmutable('now'))->format(\DateTimeInterface::ATOM);
|
||||
// Construit la réponse, signe-la, renvoie.
|
||||
// Tous les timestamps sont émis en UTC + format figé "yyyy-MM-ddTHH:mm:ssZ"
|
||||
// pour que la canonicalisation côté client produise EXACTEMENT les mêmes bytes.
|
||||
$utc = new \DateTimeZone('UTC');
|
||||
$fmt = 'Y-m-d\TH:i:s\Z';
|
||||
|
||||
$issuedAt = (new \DateTimeImmutable($lic['issued_at'], $utc))->format($fmt);
|
||||
$entUntil = (new \DateTimeImmutable($lic['download_entitlement_until'], $utc))->format($fmt);
|
||||
$serverTime = (new \DateTimeImmutable('now', $utc))->format($fmt);
|
||||
$expired = strtotime($lic['download_entitlement_until']) < time();
|
||||
|
||||
$payload = [
|
||||
'status' => $expired ? 'expired' : 'valid',
|
||||
'licenseId' => 'lic_' . $lic['id'],
|
||||
'ownerName' => $lic['owner_name'],
|
||||
'issuedAt' => (new \DateTimeImmutable($lic['issued_at']))->format(\DateTimeInterface::ATOM),
|
||||
'issuedAt' => $issuedAt,
|
||||
'downloadEntitlementUntil' => $entUntil,
|
||||
'maxMachines' => (int)$lic['max_machines'],
|
||||
'serverTime' => $serverTime,
|
||||
|
||||
Reference in New Issue
Block a user