Je construis un outil d'analyse et je peux actuellement obtenir l'adresse IP de l'utilisateur, son navigateur et son système d'exploitation auprès de son agent utilisateur.
Je me demande s'il est possible de détecter le même utilisateur sans utiliser de cookies ou de stockage local? Je ne m'attends pas à des exemples de code ici; juste un indice simple de l'endroit où chercher plus loin.
J'ai oublié de mentionner qu'il devrait être compatible avec plusieurs navigateurs s'il s'agit du même ordinateur/périphérique. Fondamentalement, je suis après la reconnaissance de l'appareil pas vraiment l'utilisateur.
Introduction
Si je vous ai bien compris, vous devez identifier un utilisateur pour lequel vous ne possédez pas d'identificateur unique. Vous devez donc déterminer qui il est en mettant en correspondance des données aléatoires. Vous ne pouvez pas stocker l'identité de l'utilisateur de manière fiable pour les raisons suivantes:
A Java Applet ou Com Object aurait été une solution facile en utilisant un hachage d'informations matérielles, mais de nos jours, les utilisateurs sont tellement conscients de la sécurité qu'il serait difficile de les amener à installer ce type de logiciel. programmes sur leur système, ce qui vous oblige à utiliser des cookies et d’autres outils similaires.
Cookies et autres outils similaires
Vous pouvez envisager de créer un profil de données, puis d’utiliser des tests de probabilité pour identifier un utilisateur probable . Un profil utile pour cela peut être généré par une combinaison des éléments suivants:
Les éléments que j'ai énumérés ne sont, bien sûr, que quelques moyens d'identifier un utilisateur de manière unique. Il y en a beaucoup plus.
Avec ce jeu d'éléments de données aléatoires pour créer un profil de données, quelle est la suite?
La prochaine étape consiste à développer un logique floue , ou, mieux encore, un réseau de neurones artificiels (qui utilise la logique floue). Dans les deux cas, l’idée est de former votre système, puis de combiner sa formation avec Bayesian Inference pour augmenter la précision de vos résultats.
La bibliothèque NeuralMesh pour PHP vous permet de générer des réseaux de neurones artificiels. Pour implémenter Bayesian Inference, consultez les liens suivants:
À ce stade, vous pensez peut-être:
Pourquoi tant de math et de logique pour une tâche apparemment simple?
Fondamentalement, parce que ce n'est pas une tâche simple . Ce que vous essayez d’atteindre, c’est en fait la probabilité pure . Par exemple, étant donné les utilisateurs connus suivants:
User1 = A + B + C + D + G + K
User2 = C + D + I + J + K + F
Lorsque vous recevez les données suivantes:
B + C + E + G + F + K
La question que vous posez essentiellement est la suivante:
Quelle est la probabilité que les données reçues (B + C + E + G + F + K) soient réellement User1 ou User2? Et laquelle de ces deux correspondances est la plus probable?
Afin de répondre efficacement à cette question, vous devez comprendre Format de la fréquence par rapport aux probabilités et pourquoi Probabilité conjointe pourrait être une meilleure approche. Les détails sont trop compliqués pour entrer ici (c'est pourquoi je vous donne des liens), mais un bon exemple serait un diagnostic médical Wizard Application , qui utilise une combinaison de symptômes pour identifier les maladies possibles.
Pensez un instant à la série de points de données qui composent votre profil de données (B + C + E + G + F + K dans l'exemple ci-dessus) en tant que Symptômes et utilisateurs inconnus. comme Maladies . En identifiant la maladie, vous pouvez en outre identifier un traitement approprié (traiter cet utilisateur comme un utilisateur1).
Évidemment, une maladie pour laquelle nous avons identifié plus de 1 symptôme est plus facile à identifier. En fait, plus nous pouvons identifier de symptômes , plus notre diagnostic sera facile et précis.
Y a-t-il d'autres alternatives?
Bien sûr. À titre de mesure alternative, vous pouvez créer votre propre algorithme de scoring simple et le baser sur des correspondances exactes. Ce n'est pas aussi efficace que la probabilité, mais peut être plus simple à mettre en œuvre.
A titre d'exemple, considérons ce tableau de score simple:
+ ------------------------- + -------- + --------- --- + | Propriété | Poids | Importance | + ------------------------- + -------- + ------- ----- + | Adresse IP réelle | 60 | 5 | | Adresse IP proxy utilisée | 40 | 4 | | Cookies HTTP | 80 | 8 | | Cookies de session | 80 | 6 | | Cookies de tiers | 60 | 4 | | Cookies Flash | 90 | 7 | | PDF Bug | 20 | 1 | | Bug Flash | 20 | 1 | | Java Bug | 20 | 1 | | Pages fréquentes | 40 | 1 | | Empreintes digitales de navigateurs | 35 | 2 | | Plugins installés | 25 | 1 | | Images en cache | 40 | 3 | | URL | 60 | 4 | | Détection de polices système | 70 | 4 | | Stockage local | 90 | 8 | | Géolocalisation | 70 | 6 | | AOLTR | 70 | 4 | | API d'informations réseau | 40 | 3 | | API d'état de la batterie | 20 | 1 | + ------------------------- + -------- + ------------ +
Attribuez le score associé à chaque information que vous pouvez recueillir sur une demande donnée, puis utilisez Importance pour résoudre les conflits lorsque les scores sont identiques.
Preuve de concept
Pour une simple validation de principe, veuillez jeter un oeil à Perceptron . Le perceptron est un modèle d'ARN généralement utilisé dans les applications de reconnaissance de formes. Il existe même un vieux PHP Class qui l'implémente parfaitement, mais vous devrez probablement le modifier pour vos besoins.
Bien que Perceptron soit un excellent outil, il peut toujours renvoyer plusieurs résultats (correspondances possibles). Il est donc utile d’utiliser une comparaison Score et Différence pour identifier le meilleur meilleur de ces correspondances.
Hypothèses
Attente
Code de validation de principe
$features = array(
'Real IP address' => .5,
'Used proxy IP address' => .4,
'HTTP Cookies' => .9,
'Session Cookies' => .6,
'3rd Party Cookies' => .6,
'Flash Cookies' => .7,
'PDF Bug' => .2,
'Flash Bug' => .2,
'Java Bug' => .2,
'Frequent Pages' => .3,
'Browsers Finger Print' => .3,
'Installed Plugins' => .2,
'URL' => .5,
'Cached PNG' => .4,
'System Fonts Detection' => .6,
'Localstorage' => .8,
'Geolocation' => .6,
'AOLTR' => .4,
'Network Information API' => .3,
'Battery Status API' => .2
);
// Get RNA Lables
$labels = array();
$n = 1;
foreach ($features as $k => $v) {
$labels[$k] = "x" . $n;
$n ++;
}
// Create Users
$users = array();
for($i = 0, $name = "A"; $i < 5; $i ++, $name ++) {
$users[] = new Profile($name, $features);
}
// Generate Unknown User
$unknown = new Profile("Unknown", $features);
// Generate Unknown RNA
$unknownRNA = array(
0 => array("o" => 1),
1 => array("o" => - 1)
);
// Create RNA Values
foreach ($unknown->data as $item => $point) {
$unknownRNA[0][$labels[$item]] = $point;
$unknownRNA[1][$labels[$item]] = (- 1 * $point);
}
// Start Perception Class
$perceptron = new Perceptron();
// Train Results
$trainResult = $perceptron->train($unknownRNA, 1, 1);
// Find matches
foreach ($users as $name => &$profile) {
// Use shorter labels
$data = array_combine($labels, $profile->data);
if ($perceptron->testCase($data, $trainResult) == true) {
$score = $diff = 0;
// Determing the score and diffrennce
foreach ($unknown->data as $item => $found) {
if ($unknown->data[$item] === $profile->data[$item]) {
if ($profile->data[$item] > 0) {
$score += $features[$item];
} else {
$diff += $features[$item];
}
}
}
// Ser score and diff
$profile->setScore($score, $diff);
$matchs[] = $profile;
}
}
// Sort bases on score and Output
if (count($matchs) > 1) {
usort($matchs, function ($a, $b) {
// If score is the same use diffrence
if ($a->score == $b->score) {
// Lower the diffrence the better
return $a->diff == $b->diff ? 0 : ($a->diff > $b->diff ? 1 : - 1);
}
// The higher the score the better
return $a->score > $b->score ? - 1 : 1;
});
echo "<br />Possible Match ", implode(",", array_slice(array_map(function ($v) {
return sprintf(" %s (%0.4f|%0.4f) ", $v->name, $v->score,$v->diff);
}, $matchs), 0, 2));
} else {
echo "<br />No match Found ";
}
Possible Match D (0.7416|0.16853),C (0.5393|0.2809)
Print_r of "D":
echo "<pre>";
print_r($matchs[0]);
Profile Object(
[name] => D
[data] => Array (
[Real IP address] => -1
[Used proxy IP address] => -1
[HTTP Cookies] => 1
[Session Cookies] => 1
[3rd Party Cookies] => 1
[Flash Cookies] => 1
[PDF Bug] => 1
[Flash Bug] => 1
[Java Bug] => -1
[Frequent Pages] => 1
[Browsers Finger Print] => -1
[Installed Plugins] => 1
[URL] => -1
[Cached PNG] => 1
[System Fonts Detection] => 1
[Localstorage] => -1
[Geolocation] => -1
[AOLTR] => 1
[Network Information API] => -1
[Battery Status API] => -1
)
[score] => 0.74157303370787
[diff] => 0.1685393258427
[base] => 8.9
)
Si Debug = true, vous pourrez voir entrée (capteur et souhaité), poids initiaux, sortie (capteur, somme, réseau), erreur, correction et poids finaux .
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| o | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15 | x16 | x17 | x18 | x19 | x20 | Bias | Yin | Y | deltaW1 | deltaW2 | deltaW3 | deltaW4 | deltaW5 | deltaW6 | deltaW7 | deltaW8 | deltaW9 | deltaW10 | deltaW11 | deltaW12 | deltaW13 | deltaW14 | deltaW15 | deltaW16 | deltaW17 | deltaW18 | deltaW19 | deltaW20 | W1 | W2 | W3 | W4 | W5 | W6 | W7 | W8 | W9 | W10 | W11 | W12 | W13 | W14 | W15 | W16 | W17 | W18 | W19 | W20 | deltaBias |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 0 | -1 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | -1 | -1 | 1 | -19 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
| 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 19 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | -1 | -1 | 1 | -19 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
x1 à x20 représentent les entités converties par le code.
// Get RNA Labels
$labels = array();
$n = 1;
foreach ( $features as $k => $v ) {
$labels[$k] = "x" . $n;
$n ++;
}
Voici un démo en ligne
Classe utilisée:
class Profile {
public $name, $data = array(), $score, $diff, $base;
function __construct($name, array $importance) {
$values = array(-1, 1); // Perception values
$this->name = $name;
foreach ($importance as $item => $point) {
// Generate Random true/false for real Items
$this->data[$item] = $values[mt_Rand(0, 1)];
}
$this->base = array_sum($importance);
}
public function setScore($score, $diff) {
$this->score = $score / $this->base;
$this->diff = $diff / $this->base;
}
}
Classe de Perceptron modifiée
class Perceptron {
private $w = array();
private $dw = array();
public $debug = false;
private function initialize($colums) {
// Initialize perceptron vars
for($i = 1; $i <= $colums; $i ++) {
// weighting vars
$this->w[$i] = 0;
$this->dw[$i] = 0;
}
}
function train($input, $alpha, $teta) {
$colums = count($input[0]) - 1;
$weightCache = array_fill(1, $colums, 0);
$checkpoints = array();
$keepTrainning = true;
// Initialize RNA vars
$this->initialize(count($input[0]) - 1);
$just_started = true;
$totalRun = 0;
$yin = 0;
// Trains RNA until it gets stable
while ($keepTrainning == true) {
// Sweeps each row of the input subject
foreach ($input as $row_counter => $row_data) {
// Finds out the number of columns the input has
$n_columns = count($row_data) - 1;
// Calculates Yin
$yin = 0;
for($i = 1; $i <= $n_columns; $i ++) {
$yin += $row_data["x" . $i] * $weightCache[$i];
}
// Calculates Real Output
$Y = ($yin <= 1) ? - 1 : 1;
// Sweeps columns ...
$checkpoints[$row_counter] = 0;
for($i = 1; $i <= $n_columns; $i ++) {
/** DELTAS **/
// Is it the first row?
if ($just_started == true) {
$this->dw[$i] = $weightCache[$i];
$just_started = false;
// Found desired output?
} elseif ($Y == $row_data["o"]) {
$this->dw[$i] = 0;
// Calculates Delta Ws
} else {
$this->dw[$i] = $row_data["x" . $i] * $row_data["o"];
}
/** WEIGHTS **/
// Calculate Weights
$this->w[$i] = $this->dw[$i] + $weightCache[$i];
$weightCache[$i] = $this->w[$i];
/** CHECK-POINT **/
$checkpoints[$row_counter] += $this->w[$i];
} // END - for
foreach ($this->w as $index => $w_item) {
$debug_w["W" . $index] = $w_item;
$debug_dw["deltaW" . $index] = $this->dw[$index];
}
// Special for script debugging
$debug_vars[] = array_merge($row_data, array(
"Bias" => 1,
"Yin" => $yin,
"Y" => $Y
), $debug_dw, $debug_w, array(
"deltaBias" => 1
));
} // END - foreach
// Special for script debugging
$empty_data_row = array();
for($i = 1; $i <= $n_columns; $i ++) {
$empty_data_row["x" . $i] = "--";
$empty_data_row["W" . $i] = "--";
$empty_data_row["deltaW" . $i] = "--";
}
$debug_vars[] = array_merge($empty_data_row, array(
"o" => "--",
"Bias" => "--",
"Yin" => "--",
"Y" => "--",
"deltaBias" => "--"
));
// Counts training times
$totalRun ++;
// Now checks if the RNA is stable already
$referer_value = end($checkpoints);
// if all rows match the desired output ...
$sum = array_sum($checkpoints);
$n_rows = count($checkpoints);
if ($totalRun > 1 && ($sum / $n_rows) == $referer_value) {
$keepTrainning = false;
}
} // END - while
// Prepares the final result
$result = array();
for($i = 1; $i <= $n_columns; $i ++) {
$result["w" . $i] = $this->w[$i];
}
$this->debug($this->print_html_table($debug_vars));
return $result;
} // END - train
function testCase($input, $results) {
// Sweeps input columns
$result = 0;
$i = 1;
foreach ($input as $column_value) {
// Calculates teste Y
$result += $results["w" . $i] * $column_value;
$i ++;
}
// Checks in each class the test fits
return ($result > 0) ? true : false;
} // END - test_class
// Returns the html code of a html table base on a hash array
function print_html_table($array) {
$html = "";
$inner_html = "";
$table_header_composed = false;
$table_header = array();
// Builds table contents
foreach ($array as $array_item) {
$inner_html .= "<tr>\n";
foreach ( $array_item as $array_col_label => $array_col ) {
$inner_html .= "<td>\n";
$inner_html .= $array_col;
$inner_html .= "</td>\n";
if ($table_header_composed == false) {
$table_header[] = $array_col_label;
}
}
$table_header_composed = true;
$inner_html .= "</tr>\n";
}
// Builds full table
$html = "<table border=1>\n";
$html .= "<tr>\n";
foreach ($table_header as $table_header_item) {
$html .= "<td>\n";
$html .= "<b>" . $table_header_item . "</b>";
$html .= "</td>\n";
}
$html .= "</tr>\n";
$html .= $inner_html . "</table>";
return $html;
} // END - print_html_table
// Debug function
function debug($message) {
if ($this->debug == true) {
echo "<b>DEBUG:</b> $message";
}
} // END - debug
} // END - class
Conclusion
Identifier un utilisateur sans identifiant unique n'est pas une tâche simple ou directe. cela dépend de la collecte d'une quantité suffisante de données aléatoires que vous pouvez collecter auprès de l'utilisateur par diverses méthodes.
Même si vous choisissez de ne pas utiliser de réseau de neurones artificiels, je suggère au moins d'utiliser une matrice de probabilité simple avec des priorités et des probabilités - et j'espère que le code et les exemples fournis ci-dessus vous en donneront suffisamment pour continuer.
Cette technique (pour détecter les mêmes utilisateurs sans cookies - ou même sans adresse IP) est appelée empreinte du navigateur . Fondamentalement, vous explorez autant que possible les informations relatives au navigateur - vous pouvez obtenir de meilleurs résultats avec javascript, flash ou Java (par exemple, les extensions installées, les polices, etc.). peut stocker les résultats hachés, si vous voulez.
Ce n'est pas infaillible, mais:
83,6% des navigateurs consultés avaient une empreinte digitale unique; parmi ceux avec Flash ou Java activé, 94,2%. Cela n'inclut pas les cookies!
Plus d'informations:
Avez-vous examiné Evercookie ? Cela peut ou peut ne pas fonctionner avec les navigateurs. Un extrait de leur site.
"Si un utilisateur est enregistré dans un navigateur et passe à un autre, tant qu'il dispose du cookie Local Shared Object, le cookie est reproduit dans les deux navigateurs."
Les empreintes de pouce mentionnées ci-dessus fonctionnent, mais peuvent tout de même souffrir de collisions.
Une solution consiste à ajouter l'UID à l'URL de chaque interaction avec l'utilisateur.
http://someplace.com/12899823/user/profile
Où chaque lien du site est adapté avec ce modificateur. Cela ressemble à la façon dont ASP.Net utilisait les données FORM entre les pages.
Vous pourriez faire cela avec un png en cache, ce serait quelque peu peu fiable (différents navigateurs se comportent différemment, et cela échouera si l'utilisateur efface son cache), mais c'est une option.
1: configurer une base de données qui stocke un ID utilisateur unique sous forme de chaîne hexadécimale
2: créez un fichier genUser.php (ou n’importe quelle langue) qui génère un ID utilisateur, le stocke dans la base de données, puis crée une vraie couleur .png à partir des valeurs de cette chaîne hexadécimale (chaque pixel sera de 4 octets), puis retourne cela au navigateur. Veillez à définir les en-têtes de type de contenu et de cache.
3: dans HTML ou JS, créez une image du type <img id='user_id' src='genUser.php' />
4: dessine cette image sur une toile ctx.drawImage(document.getElementById('user_id'), 0, 0);
5: lisez les octets de cette image en utilisant ctx.getImageData
Et convertissez les entiers en chaîne hexadécimale.
6: C'est votre identifiant utilisateur unique qui est maintenant mis en cache sur l'ordinateur de vos utilisateurs.
Sur la base de ce que vous avez dit:
Fondamentalement, je suis après la reconnaissance de l'appareil pas vraiment l'utilisateur
La meilleure façon de le faire est d’envoyer l’adresse MAC qui est le NIC ID.
Vous pouvez jeter un oeil à ce post: Comment puis-je obtenir le MAC et l'adresse IP d'un client connecté en PHP?
Vous pouvez éventuellement créer un blob pour stocker un identifiant de périphérique ...
l'inconvénient est que l'utilisateur doit télécharger le blob ( vous pouvez forcer le téléchargement ), car le navigateur ne peut pas accéder au système de fichiers pour enregistrer directement le fichier.
référence:
https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-22/blobs
Vous pouvez le faire avec etags. Bien que je ne sois pas sûr si cette action légale a été entamée.
Si vous prévenez correctement vos utilisateurs ou si vous avez quelque chose comme un site intranet, tout ira bien.
Je ne peux pas croire, http://browserspy.dk n'a toujours pas été mentionné ici! Le site décrit de nombreuses fonctionnalités (en termes de reconnaissance de modèle), qui pourraient être utilisées pour créer un classificateur.
Et bien sûr, pour évaluer les fonctionnalités, je suggérerais Support Vector Machines et libsvm en particulier.
Les suivre au cours d'une session ou d'une session à l'autre?
Si votre site est HTTPS Everywhere, vous pouvez utiliser l'ID de session TLS pour suivre la session de l'utilisateur.
Inefficace, mais peut vous donner les résultats souhaités, serait de sonder une API de votre côté. Avoir un processus en arrière-plan côté client qui envoie les données utilisateur à un intervalle. Vous aurez besoin d'un identifiant d'utilisateur à envoyer à votre API. Une fois que vous avez cela, vous pouvez envoyer toutes les informations associées à cet identifiant unique.
Cela supprime le besoin de cookies et de stockage local.