Le contexte

Pour un admin durci, j'ai lié le token JWT à une empreinte navigateur (fingerprint) pour limiter le vol de token. À chaque requête, on revérifie l'empreinte.

Le symptôme

Des utilisateurs déconnectés au bout de quelques minutes, de façon aléatoire. Token pourtant non expiré.

La cause

Le fingerprint incluait des composants instables :

// Mauvais : ces valeurs bougent
const fp = hash([
  navigator.userAgent,
  window.innerWidth + 'x' + window.innerHeight, // change au resize !
  navigator.language,
]);

innerWidth/innerHeight change au moindre redimensionnement → le hash ne correspond plus → rejet.

La solution

Ne garder que des composants stables, et vérifier avec tolérance :

const fp = hash([
  navigator.userAgent,
  navigator.language,
  screen.width + 'x' + screen.height, // resolution ecran, pas la fenetre
  navigator.platform,
]);
public function isFingerprintValid(array $tokenFp, array $requestFp): bool
{
    foreach (['ua', 'platform'] as $key) {
        if (($tokenFp[$key] ?? null) !== ($requestFp[$key] ?? null)) {
            return false;
        }
    }
    return true; // les composants secondaires ne bloquent pas
}

Ce que je retiens

  • Un fingerprint = uniquement des composants stables.
  • Égalité stricte sur des données volatiles = bugs aléatoires garantis.
  • Le fingerprint complète, il ne remplace pas, les bonnes pratiques JWT.