Cookies iOS : isoler les sessions et vérifier la déconnexion
Lors d’un test de régression du changement de compte sur un même Mac cloud, le compte B reçoit les données du compte A après sa connexion ? Ne commencez pas par réinitialiser le simulateur. Examinez séparément le stockage des cookies, les en-têtes de requête et les tâches encore en cours : l’ancienne session peut provenir d’un stockage partagé, d’un en-tête ajouté manuellement par la couche réseau, voire d’une réponse arrivée après la déconnexion.
Nous commencerons par une vérification sans accès réseau, à l’aide du framework réseau fourni avec macOS, puis nous appliquerons les mêmes assertions dans le simulateur iOS. En conservant le code de vérification dans le dépôt, vous pourrez le relancer sur le Mac cloud après chaque récupération du code, sans dépendre de l’état laissé par la précédente session graphique.
Définir d’abord à qui appartient chaque session
Créez un objet de session distinct pour chaque compte connecté. Cet objet doit posséder sa configuration, son stockage de cookies et son ensemble de tâches. N’utilisez pas une configuration mutable comme singleton global et ne comptez pas sur la modification de la configuration d’origine pour changer une URLSession déjà créée : la session copie la configuration au moment de sa création.
Définissez au minimum trois règles : les cookies de A ne doivent pas apparaître dans les requêtes de B ; la déconnexion ne doit supprimer que les données appartenant au compte concerné ; une ancienne réponse reçue après la déconnexion ne doit pas réactiver l’état connecté. Si les requêtes anonymes n’ont aucun besoin de cookies, désactivez leur gestion automatique et vérifiez également que la couche réseau n’ajoute pas manuellement d’en-tête Cookie.
ephemeralsignifie que les données de session ne sont pas conservées sur disque. Cela ne signifie ni qu’il n’y a pas de cookies en mémoire, ni que deux comptes applicatifs sont automatiquement isolés.
Vérifier par script que les stockages sont bien indépendants
Enregistrez le contenu suivant dans cookie-check.swift. Ce script utilise uniquement des valeurs de session fictives sur le domaine local localhost. Il n’envoie aucune requête réseau et n’accède pas au stockage partagé par défaut.
import Foundation
func require(_ condition: Bool, _ message: String) {
if !condition {
fatalError(message)
}
}
func makeCookie(_ value: String) -> HTTPCookie {
HTTPCookie(properties: [
.domain: "localhost",
.path: "/",
.name: "session",
.value: value
])!
}
let first = URLSessionConfiguration.ephemeral
let second = URLSessionConfiguration.ephemeral
guard let a = first.httpCookieStorage,
let b = second.httpCookieStorage else {
fatalError("Cookie storage unavailable")
}
require(a !== b, "Stores must be independent")
let target = URL(string: "http://localhost/account")!
a.setCookie(makeCookie("fixture-a"))
b.setCookie(makeCookie("fixture-b"))
func values(_ store: HTTPCookieStorage) -> [String] {
(store.cookies(for: target) ?? [])
.filter { $0.name == "session" }
.map(\.value)
.sorted()
}
require(values(a) == ["fixture-a"], "Account A mismatch")
require(values(b) == ["fixture-b"], "Account B mismatch")
for cookie in a.cookies ?? [] {
a.deleteCookie(cookie)
}
require(values(a).isEmpty, "Account A was not cleared")
require(values(b) == ["fixture-b"], "Account B was affected")
Sur une machine où les outils en ligne de commande ont déjà été initialisés, exécutez :
set -eu
xcrun swift cookie-check.swift
Si tout se passe bien, le script n’affiche rien. Si une assertion échoue, il se termine avec un code de sortie non nul. Ici, tout le contenu de a est supprimé, ce qui suppose que ce stockage appartient exclusivement à A. Si l’application utilise encore un stockage partagé, il faut d’abord filtrer les cookies selon le compte auquel ils appartiennent ; ne reprenez pas telle quelle cette suppression intégrale.
Le script démontre l’indépendance des stockages et vérifie le périmètre de la suppression. Il ne prouve pas que les requêtes réelles utilisent correctement ces stockages. La réussite de cette étape ne vaut donc pas validation de la couche réseau.
Intégrer les requêtes et les réponses réelles
Dans la cible de tests de l’application, réutilisez le véritable client réseau et créez des sessions distinctes pour A et B. Faites renvoyer par un service de test contrôlé des en-têtes Set-Cookie portant le même nom de cookie, mais des valeurs différentes. Appelez ensuite un point de terminaison qui renvoie les cookies reçus et vérifiez que ceux reçus par le serveur appartiennent bien au compte correspondant.
Les requêtes doivent passer par la couche réseau de l’application, sans contourner temporairement les intercepteurs. Vérifiez en particulier que les en-têtes par défaut, les mécanismes de nouvelle tentative et la gestion des redirections ne consultent pas à nouveau un stockage partagé. Lors de la validation du comportement automatique des cookies, ne renseignez pas non plus les en-têtes à la main : vous risqueriez de masquer une erreur de raccordement des composants sous-jacents.
Tester les limites imposées par les attributs
Ajoutez des cas de test pour Path, Secure et l’expiration : un cookie limité à un chemin précis ne doit pas être envoyé sur un autre chemin ; un cookie expiré ne doit plus être envoyé ; un cookie sécurisé doit être vérifié dans un environnement HTTPS contrôlé. Un dispositif de test HTTP local ne permet pas de valider le comportement lié aux connexions sécurisées.
Plusieurs cookies de même nom peuvent coexister s’ils diffèrent par leur domaine ou leur chemin. Les assertions doivent comparer des ensembles de tuples composés du nom, du domaine, du chemin et de la valeur, plutôt que de supposer que le stockage ne contient jamais qu’un seul session. Évitez aussi de faire des hypothèses trop strictes sur l’ordre des éléments dans la chaîne de l’en-tête : analysez-la, puis comparez les données sur le plan sémantique.
Gérer les réponses tardives lors de la déconnexion
Commencez par faire passer le compte à l’état « déconnexion en cours » et refusez la création de nouvelles tâches. Annulez ensuite les tâches existantes ou attendez qu’elles se terminent. Supprimez alors les cookies de ce compte, invalidez l’ancienne session et créez un nouvel objet pour la prochaine connexion.
Une simple suppression laisse subsister une condition de concurrence : si une ancienne requête reçoit plus tard un Set-Cookie, celui-ci peut encore être écrit dans l’ancien stockage. L’annulation n’est pas non plus une barrière de synchronisation : des callbacks peuvent déjà être en attente d’exécution. Attribuez donc un numéro de génération de session à chaque connexion et vérifiez-le avant de traiter les réponses. Une réponse d’une ancienne génération ne doit jamais mettre à jour l’état de l’utilisateur courant.
Dans le test, retardez la réponse destinée à A, déclenchez sa déconnexion, connectez B, puis libérez la réponse de A. À la fin, vérifiez à la fois l’état de l’interface de B, l’identité du compte utilisée par la logique applicative et les cookies de la requête suivante. Vérifier uniquement que le stockage est vide au moment de la déconnexion ne permet pas de détecter ce type de problème.
Exécuter la validation en deux niveaux
À chaque fusion, exécutez d’abord le script de stockage sans réseau, puis les tests de renvoi des cookies et de réponses tardives sur un simulateur fixe. Chaque test doit créer sa propre configuration et ses propres données et services de test. À la fin, arrêtez les services et annulez les tâches. Ne partagez aucune session mutable afin d’éviter que les exécutions parallèles n’interfèrent entre elles.
Dans les traces d’échec, conservez l’identifiant de la requête, la génération de session ainsi que le nom et les attributs des cookies, mais jamais leurs valeurs réelles. Pour comparer des valeurs, utilisez des marqueurs fixes issus des données de test ; ne copiez pas d’état de connexion de production sur le Mac cloud.
Enfin, faites volontairement partager le même stockage aux deux comptes et vérifiez que les tests échouent. Supprimez ensuite volontairement la vérification de génération et assurez-vous que le cas de réponse tardive est détecté. Ce n’est qu’en détectant systématiquement ces deux types de régression que votre contrôle de validation prouve qu’il vérifie les frontières entre comptes, et pas seulement quelques assertions qui passent par hasard.
Questions fréquentes
Une configuration éphémère suffit-elle à isoler les cookies ?
Non. Une session éphémère conserve des cookies en mémoire. Créez des configurations distinctes et vérifiez que la couche réseau ne réintroduit ni stockage partagé ni en-tête Cookie manuel.
Peut-on simplement appeler reset à la déconnexion ?
Non. Bloquez les nouvelles requêtes, terminez les tâches existantes, puis effacez les cookies du compte. reset touche aussi le cache et les identifiants ; il ne remplace pas ce séquencement.
Réservez un Mac dédié à votre prochain build.
Choisissez un Mac cloud non virtualisé et conservez votre environnement Xcode et vos workflows CI. Configurez la machine selon la charge de vos projets ; sa disponibilité réelle est indiquée en temps réel dans la console.