Fast jedes Angular-Tutorial legt das JWT in localStorage ab und macht weiter. Es funktioniert, es ist einfach — und es reicht das Token jedem Skript auf dem Silbertablett, das je auf Ihrer Domain läuft. In einem System, in dem ein Identitätsleck nicht hinnehmbar war, mussten wir es anders lösen.
Warum localStorage kein Tresor ist
localStorage ist aus jedem JavaScript derselben Domain lesbar. Eine einzige XSS-Lücke — im eigenen Code, in einer Abhängigkeit, in einem Marketing-Skript, das jemand über das Backend eingebunden hat — und der Angreifer hat das Token und alles, was es öffnet. Dasselbe gilt für sessionStorage und für ein gewöhnliches Cookie ohne httpOnly-Flag.
Das Muster: Token im Speicher, Erneuerung im Cookie
Das Zugriffstoken lebt ausschließlich im Speicher — als Feld des Auth-Service, nirgends auf der Platte. Ein Interceptor hängt es an jede Anfrage. Für eine langlebige Sitzung stellt der Server ein Refresh-Token als httpOnly-, Secure- und SameSite-Cookie aus: Der Browser sendet es von selbst, und JavaScript kann es weder lesen noch stehlen.
Lädt der Nutzer die Seite neu, ist der Speicher leer. Beim Start ruft die Anwendung den Erneuerungs-Endpunkt auf; das Cookie reist von allein mit, der Server liefert ein frisches Zugriffstoken, und der Nutzer bleibt angemeldet, ohne etwas zu bemerken. Eine kurze Gültigkeit des Zugriffstokens begrenzt den Schaden, falls es doch einmal abfließt.
Was sich in der Anwendung ändert
Drei Dinge verlangen Sorgfalt. Erstens hat der erste Seitenaufruf einen Aufruf mehr, also muss der Routen-Guard das Ergebnis abwarten, statt den Nutzer sofort zur Anmeldung zu schicken. Zweitens versucht der Interceptor bei einem 401 genau einmal eine Erneuerung und wiederholt die ursprüngliche Anfrage — ohne je in eine Schleife zu geraten. Drittens muss die Abmeldung das Cookie serverseitig ungültig machen, denn der Client kann es nicht löschen.
Der Preis — und wann er sich lohnt
Das Muster ist nicht umsonst: Der Server muss Refresh-Tokens verwalten und widerrufen können, und jeder Browser-Tab erneuert seine Sitzung für sich. Für ein internes Werkzeug mit drei Nutzern ist das vielleicht übertrieben. Für alles, was fremde personenbezogene Daten hält, stellt sich nicht die Frage, ob es sich lohnt — nur, wie gern man erklären möchte, warum man es nicht getan hat.
Ein Token, das JavaScript nicht lesen kann, kann es auch nicht stehlen — alles andere ist Hoffnung.