
Wenn ein Playwright-Test oder Scraper Authentifizierung braucht, gibt es meist keinen Grund, bei jedem Lauf von vorn einzuloggen. Ein üblicher Weg: einmal einloggen, Cookies und den zugehörigen Browser-Status mit storageState speichern und diesen Status in späteren Läufen laden, um dieselbe Sitzung wiederzuverwenden. Eine gespeicherte storageState-Datei bedeutet aber nicht, dass der Login unbegrenzt hält. Die Sitzung kann ablaufen, vom Server widerrufen werden oder schon beim ersten Laden falsch sein.
Wenn Sie Ihr eigenes Konto automatisieren und im Browser, den Sie täglich nutzen, bereits eingeloggt sind, gibt es eine andere Option: diese bestehende Browser-Sitzung direkt wiederverwenden. ego (lite) lässt KI-Agenten in einem Browser arbeiten, der den benötigten Login-Status bereits hat, und hält jede Aufgabe in einem eigenen Space isoliert. Erreicht die Aufgabe einen Bestätigungscode, QR-Login oder einen anderen Schritt, der Sie braucht, übernehmen Sie im sichtbaren Browser, statt MFA zu automatisieren.
Welchen Weg Sie auch wählen, das Ziel bleibt dasselbe: Ihre Automatisierung muss eine gültige authentifizierte Sitzung haben. Ein Lauf, der plötzlich auf der Login-Seite landet, eine API mit 401 oder ein Element nach dem Login, das nie erscheint, kann nach drei getrennten Problemen aussehen: Navigation, Berechtigungen oder ein Selektorproblem, obwohl alle drei auf dieselbe kaputte Sitzung zeigen können. In den folgenden Abschnitten zeigen wir mit einem wiederholbaren lokalen Setup, wie Sie Authentifizierungsstatus in Playwright speichern, wiederverwenden, prüfen und erneuern.
So sieht ein kaputter Playwright-Login aus
Im Fixture-Lauf erzeugte eine widerrufene Sitzung alle drei Symptome gleichzeitig. Nach dem Löschen der serverseitigen Sitzung wurde ein Kontext, der den gespeicherten Status wiederverwendete, mit einem next-Parameter zurück zum Dashboard auf die Login-Seite umgeleitet, eine Probe-Anfrage an den authentifizierten JSON-Endpunkt lieferte 401, und das Warten auf die Dashboard-Tabelle lief nach den konfigurierten 3 Sekunden in den Timeout.
Beim oeffentlichen the-internet-Login ist dieselbe Fehlerklasse der Bounce. /secure ohne lebende Sitzung landet auf /login mit dem roten Flash You must login to view the secure area. Das ist die URL-Pruefung aus dem Callout darunter: der Browser steht auf dem Loginformular, nicht auf der Seite, die der Locator erwartet. Dieser Screenshot ist dieser Bounce. Er beweist nicht, dass Logout eine storageState-Datei geloescht hat. the-internet Logout beendet die aktuelle Fenstersitzung; eine bereits auf die Platte geschriebene Datei kann /secure weiter oeffnen, bis das Cookie selbst tot ist.

Das dritte Symptom ist das teure. Ein Locator, der auf einem Element nach dem Login timeoutet, sagt nicht, dass der Locator falsch ist; er sagt, dass die Seite vor Ihnen nicht die Seite ist, die Sie angenommen haben. Im Lauf oben wurde die Tabelle nie gerendert, weil der Browser auf dem Login-Formular saß. Das als Selektorproblem zu behandeln führt dazu, eine Abfrage zu flicken, die nie kaputt war.
Vier Ursachen hinter Authentifizierungsfehlern
Authentifizierungsfehler bündeln sich in vier Ursachen, und jede hat eine andere Reparatur. Einordnen vor dem Flicken verhindert, dass die Reparatur nur ein weiteres wait wird.
1. Der Status wurde nie geladen
Ein Kontext ohne storageState startet ausgeloggt, unabhängig davon, was in einem vorherigen Lauf passiert ist. Dasselbe folgt, wenn die Datei zum falschen Zeitpunkt gespeichert wird, ein relativer Pfad woanders auflöst oder der Setup-Schritt in einem Projekt läuft, dessen Status nicht an das abhängige Projekt weitergegeben wird. Das Erkennungsmerkmal: ein brandneuer Kontext verhält sich identisch zum fehlschlagenden, beide sind ausgeloggt.
2. Das Cookie ist da, aber tot
Ein Session-Cookie kann existieren, die richtige Domain und Flags tragen und trotzdem vom Server abgelehnt werden. Session-Cookies haben eine eigene Lebensdauer, und ein Server kann eines jederzeit widerrufen, etwa bei einem Redeploy, einer Passwortänderung oder einem Idle-Timeout. Das expires-Feld des Cookies ist ein Hinweis, keine Garantie: die serverseitige Sitzung kann früher sterben. Genau diesen Fall reproduziert das Fixture mit einem expliziten revoke-Aufruf.
3. Isolation zwischen Kontexten
Cookies und localStorage gehören zu einem Browser-Kontext, nicht zum Browser. Status aus einem Kontext zu speichern und zu erwarten, dass ein anders konfigurierter Kontext ihn teilt, scheitert lautlos. Dasselbe gilt für parallele Worker: jeder Worker bekommt seinen eigenen Kontext, also braucht jeder Worker entweder seine eigene Storage-Datei oder seinen eigenen Login-Schritt. Diese Isolation ist ein Feature. Sie ist auch der Grund, warum eine Sitzung, die an einer Stelle funktioniert, an einer anderen zu verschwinden scheint.
4. SSO-Redirect-Ketten
Bei Single Sign-On hält selten die App, die Sie wollen, die Sitzung. Der Login leitet zu einem Identity Provider auf einer anderen Domain um, der Provider setzt eigene Cookies, und die Kontrolle kehrt mit einem Code oder einem Token zur App zurück. Status zu speichern, bevor diese Kette abgeschlossen ist, fängt nur einen Teil der Sitzung und lässt die IdP-Cookies weg; der nächste Lauf wiederholt die Umleitung. Das zuverlässige Muster: auf die finale Anwendungs-URL und eine authentifizierte Markierung warten, bevor gespeichert wird, damit die Statusdatei jede Origin der Kette trägt.
Einmal einloggen und den Status korrekt speichern
Der Login-einmal-Ablauf ist kurz: navigieren, authentifizieren, den authentifizierten Status bestätigen, dann storageState in eine Datei schreiben. Der Bestätigungsschritt trennt eine funktionierende Statusdatei von einer, die gelegentlich nicht funktioniert.
import { chromium } from "playwright";
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto("https://app.example.com/login");
await page.fill("input[name=email]", process.env.APP_EMAIL);
await page.fill("input[name=password]", process.env.APP_PASSWORD);
await page.click("button[type=submit]");
// Wait for the authenticated destination, not just for the click to land.
await page.waitForURL("**/dashboard");
// Verify before saving: an authenticated element plus a 200 from the API.
await page.waitForSelector("#revenue-table");
const probe = await context.request.get("https://app.example.com/api/summary");
if (probe.status() !== 200) throw new Error("login did not take");
await context.storageState({ path: "playwright/.auth/user.json" });
await browser.close();Im Fixture-Lauf dauerte dieser Schritt 119 Millisekunden vom Öffnen der Login-Seite bis zum Schreiben der Statusdatei. Die Datei hielt ein Cookie, die Sitzungskennung, die der Server ausgestellt hatte, und die Probe lieferte 200. Zahlen wie diese gehören zum Fixture, übertragbar ist die Form der Prüfung: Ziel-URL prüfen, ein sichtbares authentifiziertes Element prüfen, einen authentifizierten Endpunkt prüfen und erst dann persistieren.
Der headed Lauf unten nutzte the-internet.herokuapp.com, nicht das Fixture-Dashboard. Die 119 Millisekunden bleiben beim Fixture. Der Screenshot zeigt dieselbe Speicherform auf einem oeffentlichen Login: /secure nach der Anmeldung, Logout sichtbar, und Claude prueft /tmp/pw-auth.json.

Für Testsuiten steckt dieselbe Idee in einem Setup-Projekt, das die Datei erzeugt, und abhängigen Projekten, die sie verbrauchen, das Muster, das der offizielle Authentifizierungsleitfaden dokumentiert. Für Skripte und Scraper ist die Sequenz oben der gesamte Ablauf. In beiden Fällen ist die Datei ein Zugangsgeheimnis: sie enthält lebende Session-Cookies, behandeln Sie sie deshalb wie ein Passwort und halten Sie sie aus der Versionskontrolle.
Den gespeicherten Status in Tests und Scrapern wiederverwenden
Wiederverwendung ist eine Einzeiler-Änderung bei der Kontext-Erzeugung. In einem Skript übergeben Sie die Datei an den Kontext. In einer Playwright-Testsuite setzen Sie sie pro Projekt oder Testdatei, damit jeder Worker vom selben eingeloggten Status startet.
// Script: create the context from the saved state.
const context = await browser.newContext({
storageState: "playwright/.auth/user.json",
});
// Test: use the state for this file (or configure it per project).
test.use({ storageState: "playwright/.auth/user.json" });Im Fixture-Lauf erreichte ein neuer Kontext aus der gespeicherten Datei das geschützte Dashboard in 6 Millisekunden ohne Login-Schritt, und die authentifizierte Probe lieferte 200 mit der erwarteten Nutzlast. Die interessante Zahl sind nicht die 6 Millisekunden; interessant ist, dass sich am Ablauf nichts änderte außer der Herkunft des Kontext-Status.
Der Wiederverwendungs-Screenshot ist dieselbe oeffentliche Seite. Ein neuer headed Kontext lud /tmp/pw-auth.json, uebersprang das Loginformular und oeffnete /secure mit bereits sichtbarem Logout. Die 6 Millisekunden sind weiter die Fixture-Zeit.

Es lohnt sich zu wissen, was die Statusdatei trägt und was nicht. Sie enthält Cookies für jede Origin, die der Kontext berührt hat, und localStorage-Einträge pro Origin. Sie enthält kein IndexedDB, keinen Session Storage und keinen Service-Worker-Status. Anwendungen, die ihr Token in IndexedDB halten, brauchen deshalb einen zusätzlichen Capture-Schritt oder einen erneuten Login; von storageState zu erwarten, dass es sie abdeckt, ist eine häufige Quelle von Phantomfehlern. Cookies selbst unterliegen Domain, Pfad und Flags, beschrieben in dem MDN-Cookie-Leitfaden und der Set-Cookie-Referenz.
Eine abgelaufene oder widerrufene Sitzung erkennen
Der günstigste Detektor ist eine authentifizierte Anfrage vor der teuren Arbeit. Die meisten Anwendungen haben einen kleinen Endpunkt, der mit dem aktuellen Nutzer, einem Zähler oder einem Konfigurationsobjekt antwortet, und sein Statuscode ist der Statuscode der Sitzung. Eine Anfrage kostet Millisekunden; den Fehler mitten in einem Scrape zu entdecken kostet den ganzen Lauf.
async function sessionIsAlive(context) {
const probe = await context.request.get(
"https://app.example.com/api/summary",
{ failOnStatusCode: false },
);
return probe.status() === 200;
}
if (!(await sessionIsAlive(context))) {
await loginAndSaveState(context);
}Verlassen Sie sich nicht allein auf das expires-Feld des Cookies. Es sagt, wann der Browser das Cookie nicht mehr senden soll, nicht wann der Server es nicht mehr akzeptiert. Serverseitiger Widerruf bleibt unsichtbar, bis Sie nachfragen. Der revoke-Aufruf des Fixtures ist genau diese Situation: das Cookie war noch da und wohlgeformt, und der Server antwortete 401.
Automatisch neu authentifizieren
Automatische Neuauthentifizierung ist eine kleine Zustandsmaschine: erkennen, neu einloggen, den gespeicherten Status erneuern, den fehlgeschlagenen Schritt einmal erneut ausführen und prüfen. Die Deckelung zählt. Ein Workflow, der still in einer Schleife neu authentifiziert, kann ein falsches Passwort, eine Kontosperre oder eine geänderte Login-Seite verdecken und dabei fleißig Traffic erzeugen.
async function withSession(page, context, task) {
if (!(await sessionIsAlive(context))) {
await loginAndSaveState(context, page); // refresh file after login
}
try {
return await task();
} catch (error) {
if (!(await sessionIsAlive(context))) {
await loginAndSaveState(context, page); // one retry, then fail loudly
return await task();
}
throw error;
}
}Im Fixture sah der Detektor die 401, Re-Login und Verifikation dauerten 99 Millisekunden, und die erneuerte Statusdatei hielt wieder genau ein Session-Cookie. Der Lauf endete auf dem geschützten Dashboard mit sichtbarer Tabelle und drei gerenderten Zeilen. Eine Neuauthentifizierung ohne diese drei Bestätigungen ist nicht fertig.
Wenn mehrere Worker eine Statusdatei teilen, können sie den Ablauf im selben Moment bemerken und um den Re-Login rennen. Das Mittel ist Single-Flight-Koordination: der erste Worker erneuert die Datei und die anderen warten, oder jeder Worker erneuert seine eigene Kopie. Die Mechanik ist dieselbe wie bei den Problemen mit persistenter Sitzung, die auftreten, wenn mehrere KI-Agenten gegen dasselbe Browser-Profil laufen.
Mehrere Konten und parallele Läufe
Isolation gilt pro Kontext, also die Regel: eine Storage-Datei pro Konto oder Rolle, ein Kontext pro Worker und keine geteilte veränderbare Sitzung. Im Fixture erzeugten zwei parallele Logins zwei verschiedene Session-Cookies, beide Proben lieferten 200 in derselben Millisekunde, und kein Kontext sah die Sitzung des anderen. Genau diese Eigenschaft sollten Sie bewusst erhalten, weil der Fehlerfall subtil ist: zwei Konten überschreiben gegenseitig den Status und ein Test besteht für den falschen Nutzer.
Dieselbe Regel gilt im echten Browser. Zwei Spaces sind zwei Kontexte: einer kann auf einem neuen Tab idle bleiben, waehrend der andere eine angemeldete Airbnb-Sitzung haelt, ohne Cookies wie ein einziges headed Fenster zu teilen. Isolation ist der Punkt. Die Uebersicht zeigt nur beides gleichzeitig.

Prüfen, dass die Authentifizierung greift
Verifikation sind drei Prüfungen, und das Auslassen einer davon hält einen Workflow still kaputt. Die erste Prüfung ist positiv: ein Element, das nur nach dem Login existiert, ist vorhanden. Die zweite ist negativ: das Login-Formular fehlt, damit eine Seite, die zufällig beides enthält, nicht akzeptiert wird. Die dritte ist eine Datenprüfung, die auf einer veralteten oder gecachten Seite scheitern würde, etwa ein Wert, der sich zwischen Läufen ändern muss.
await page.waitForSelector("#revenue-table"); // positive
await expect(page.locator("#login-form")).toHaveCount(0); // negative
const probe = await context.request.get("/api/summary"); // data
assert.equal(probe.status(), 200);
assert.ok((await probe.json()).q3Revenue);Alle drei bestanden im Wiederverwendungslauf des Fixtures: die Tabelle war sichtbar, die Anzahl der Login-Formulare war null, und die JSON-Nutzlast enthielt das erwartete Feld. Die drei zusammen brauchen wenige Millisekunden und machen den Unterschied zwischen einer funktionierenden und einer kaputten Sitzung aus einer Vermutung zu einer Tatsache. Mehr Debugging-Technik für Läufe, die trotzdem danebenliegen, steht im Playwright-Debug-Leitfaden, und die Best-Practices-Seite behandelt die umgebenden Gewohnheiten, inklusive welche waits sich zu schreiben lohnen.
Wenn die Sitzung zu Ihrem echten Browser gehört
Eine Playwright-storageState-Datei gehört Ihnen. Das ist das richtige Werkzeug, wenn das Konto ein Fixture ist, das Passwort in CI-Secrets liegt und niemand an der Maschine sitzt. Es ist das falsche Werkzeug, wenn der Login Ihrer ist: SSO, ein Hardware-Key, ein Push-Prompt, eine Sitzung, die Sie in Chrome täglich warmhalten. Dann besteht die Aufgabe nicht darin, den Login nachzubauen. Sie besteht darin, den Browser zu leihen, der ihn bereits hat.
ego (lite) ist dieses Leihen. Ein KI-Agent kann einen eingeloggten Tab öffnen, den Sie bereits nutzen, in einem Space weiterarbeiten, der Ihnen die Maus nicht wegnimmt, und anhalten, wenn ein zweiter Faktor oder eine Zahlungsbestätigung Sie braucht. Die Sitzung wird nie eine JSON-Datei auf der Platte. Das ist der Punkt: ein persönlicher Login soll im Browser bleiben, nicht über einen storageState-Pfad reisen, den CI später versehentlich committen würde.

Halten Sie die beiden Instrumente auseinander. Unbeaufsichtigte Testkonten bleiben bei storageState, wie der Playwright-Auth-Leitfaden beschreibt. Persönliche Dashboards bleiben in einem sichtbaren Browser. ego (lite) klickt kein MFA, bestätigt keine Zahlungen und sammelt keine Zugangsdaten; diese Stopps stehen in der Space-Dokumentation und dem Schnellstart. Die aktuelle Produktversion ist 0.5.0.32 (changelog, 2026-09-12). Prüfen Sie das Changelog und das GitHub-Repo, bevor Sie einen neueren Build zitieren.
FAQ
Warum landet Playwright nach dem Speichern von storageState trotzdem auf der Login-Seite?
Playwright landet nach storageState trotzdem auf der Login-Seite, wenn die Datei zu früh gespeichert wurde, der Kontext sie nie geladen hat oder der Server das Cookie widerrufen hat. Prüfen Sie zuerst die Cookies der Datei, dann die Kontext-Optionen. Wenn beides stimmt, behandeln Sie es als tote Sitzung, nicht als Navigationsbug.
Überleben Session-Cookies storageState?
Ja. Die Datei zeichnet Cookies auf, ob sie ein Ablaufdatum haben oder nicht, zusammen mit localStorage pro Origin. Was nicht überlebt, ist alles, was der Browser außerhalb dieser Struktur hält: IndexedDB, Session Storage, Cache und Service Worker.
Wie lange hält ein gespeicherter Playwright-Login?
So lange der Server die Sitzung akzeptiert, eine Entscheidung, die der Server trifft und jederzeit umkehren kann. Das Ablaufdatum des Cookies ist eine Obergrenze, kein Versprechen. Behandeln Sie jede langlebige Statusdatei als veraltet, bis eine Probe etwas anderes sagt.
Können parallele Worker eine storageState-Datei teilen?
Lesen können sie sie, und der Kontext jedes Workers bleibt isoliert. Das Problem entsteht, wenn ein Worker die Datei nach einem Re-Login erneuert, während ein anderer mitten im Lauf ist. Geben Sie jedem Worker eine eigene Kopie oder serialisieren Sie die Erneuerung, sodass nur ein Login gleichzeitig passiert.
Wie authentifiziere ich mich in Playwright mit OAuth oder SSO?
Durchlaufen Sie die volle Redirect-Kette einmal, in einem Kontext, der die Cookies des Identity Providers behält, und speichern Sie den Status erst, wenn die finale URL der Anwendung erreicht ist. Wenn der Provider einen interaktiven Schritt verlangt, erledigen Sie ihn einmal von Hand im selben Lauf und verwenden Sie den resultierenden Status danach wieder.
Wie sollten MFA und Einmalcodes behandelt werden?
Nicht, indem Sie den zweiten Faktor mit einem gespeicherten Code automatisieren. Das ehrliche Muster ist ein einmaliger menschlicher Schritt, dessen Ergebnis die gespeicherte Sitzung ist, oder eine Sitzung, die in einem Browser, der Ihnen gehört, bereits authentifiziert war. OTP-Geheimnisse oder SMS-Codes neben einer Storage-Datei zu speichern macht aus einer Sicherheitskontrolle eine Haftung.
Ist es sicher, storageState in die Versionskontrolle zu committen?
Nein. Die Datei enthält lebende Session-Cookies. Halten Sie sie im Arbeitsverzeichnis, ignorieren Sie sie in git und injizieren Sie sie in CI aus einem Secret Store. Wenn sie je committed wurde, behandeln Sie die Sitzung als geleakt und widerrufen Sie sie.
Wie teste ich gezielt, dass meine Automatisierung eine abgelaufene Sitzung verarbeitet?
Fügen Sie einen Test-Hook hinzu, der die Sitzung serverseitig ungültig macht, so wie der revoke-Endpunkt des Fixtures, und führen Sie den Wiederverwendungspfad dagegen. Asserten Sie die Umleitung, die 401 der Probe und die Wiederherstellung. Dieser eine Test deckt den Codepfad ab, der sonst nur in Produktion feuert.
Was, wenn die App ihr Token in IndexedDB speichert?
storageState trägt es nicht, also nutzen Sie einen von zwei Wegen: das Token nach dem Login per Page-Skript erfassen und beim Wiederverwenden erneut injizieren, oder in jedem Lauf über die API authentifizieren, die das Token ausstellt. Der erste ist schneller, der zweite weniger an die Interna der App gekoppelt.
Sollte jeder Lauf neu authentifizieren statt Status wiederzuverwenden?
Für CI mit einem Wegwerf-Testkonto ist Neuauthentifizierung pro Lauf die sicherere Voreinstellung. Für einen Job, der häufig gegen eine stabile Sitzung läuft, sind Wiederverwendung plus Probe und gedeckelter Re-Login günstiger und leichter nachzuvollziehen. Beides ist legitim; ungeprüfte Wiederverwendung ist es nicht.
Warum funktioniert der Login lokal, scheitert aber in CI?
Meist weil die Statusdatei in CI fehlt oder älter ist, die Uhr abweicht oder die Login-Seite einer frischen IP eine andere Variante liefert. Reproduzieren Sie mit derselben Headless-Einstellung, demselben Viewport und derselben Storage-Datei und prüfen Sie die Sitzung, bevor die Suite läuft. Der Fehler ist oft die Umgebung, nicht ein Codeunterschied.
Kann eine storageState-Datei zwei Konten bedienen?
Nein, und Versuche, sie zu mergen, behalten meist das Cookie, das zuletzt geschrieben wurde. Nutzen Sie eine Datei pro Konto oder Rolle, benennen Sie sie nach der Identität, zu der sie gehört, und übergeben Sie die richtige an jeden Kontext.