Zurück zum Blog
Headless Commerce22. Juni 2026

Headless Shopware Login: Authentifizierung und Session-Handling mit der Store API in Next.js

Produkte laden und einen Warenkorb füllen — das bekommt fast jeder mit der Store API schnell hin. Dann kommt der Login, und plötzlich ist der eingeloggte Kunde nach dem Seitenwechsel wieder weg, der Warenkorb leer, die individuellen Preise verschwunden. Der Grund ist fast immer derselbe: ein missverstandener Context-Token. Wie Authentifizierung im entkoppelten Shopware-Frontend wirklich funktioniert — und wie du sie sicher baust — zeige ich hier.

1. Warum es keinen klassischen Login gibt

Das native Shopware-Storefront kennt eine serverseitige PHP-Session und einen Session-Cookie — Login-Status, Warenkorb und Währung hängen daran. Im Headless-Setup existiert diese Session nicht. Die Store API ist zustandslos im HTTP-Sinn und arbeitet stattdessen mit dem sw-context-token.

Dieser Token ist der Schlüssel zu einem Session-Kontext, der auf Shopware-Seite gespeichert wird: Warenkorb, eingeloggter Kunde, Lieferland, Währung. Der entscheidende Punkt, an dem viele scheitern: Ein Login erzeugt einen neuen Context-Token. Der Token, mit dem die anonyme Session und ihr Gast-Warenkorb liefen, ist nach dem Login ein anderer. Wer den alten weiterverwendet, spricht gegen einen ausgeloggten Kontext — der Kunde scheint sofort wieder abgemeldet.

2. Der Login-Flow und der Token-Wechsel

Der Login ist ein einziger POST gegen /store-api/account/login mit E-Mail und Passwort. Die Antwort enthält den neuen Context-Token — sowohl im Response-Body (contextToken) als auch im Response-Header sw-context-token.

Login und Token-Übernahme

async function login(email: string, password: string, ctxToken?: string) {
  const res = await fetch(`${BASE_URL}/store-api/account/login`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "sw-access-key": ACCESS_KEY,
      // bestehenden Gast-Token mitschicken, damit der
      // Gast-Warenkorb in die Kunden-Session übernommen wird
      ...(ctxToken ? { "sw-context-token": ctxToken } : {}),
    },
    body: JSON.stringify({ email, password }),
  });

  if (!res.ok) throw new Error("Login fehlgeschlagen");

  // der NEUE Token gilt ab jetzt — den alten verwerfen
  const data = await res.json();
  return data.contextToken as string;
}

Schickst du beim Login den bestehenden Gast-Token mit, übernimmt Shopware dessen Warenkorb in die Kunden-Session. Das ist genau das Verhalten, das Nutzer erwarten: Was vor dem Login im Korb lag, ist danach noch da. Den zurückgegebenen Token speicherst du — und ab diesem Moment ist nur noch er gültig.

3. Registrierung und Gast-Bestellungen

Die Registrierung läuft über /store-api/account/register und verhält sich beim Token identisch: Ist die Double-Opt-In-Bestätigung im Sales Channel deaktiviert, ist der Kunde nach dem Request direkt eingeloggt und du bekommst einen frischen Context-Token. Ist Double-Opt-In aktiv, wird der Account erst nach Klick auf den Bestätigungslink über /account/register-confirm aktiv — dein Flow muss diesen Zwischenschritt abbilden.

Für Gast-Bestellungen setzt du im Register-Body das Flag guest: true. Der Kunde durchläuft den Checkout, ohne ein Passwort zu vergeben — kein dauerhaftes Konto, aber eine vollständige Session mit Adresse und Bestellung. Praktisch unverzichtbar, wenn du die Conversion-Hürde im B2C-Checkout niedrig halten willst.

4. Token sicher speichern: httpOnly-Cookie statt localStorage

Viele Tutorials legen den Context-Token in den localStorage. Für den Warenkorb einer anonymen Session ist das vertretbar. Für eine eingeloggte Kunden-Session ist es ein Sicherheitsproblem: Der Token autorisiert den Zugriff auf Kundendaten, Adressen und Bestellhistorie. Liegt er im localStorage, kann ihn jedes per XSS eingeschleuste Skript auslesen.

Sauberer ist ein httpOnly-Cookie, das der Browser nicht per JavaScript preisgibt. Mit dem Next.js App Router setzt du es serverseitig in einer Server Action oder einem Route Handler über die cookies()-Funktion. Der Login läuft dann nicht direkt vom Browser gegen Shopware, sondern über deinen Next.js-Server als dünnen Proxy:

Server Action: Login mit httpOnly-Cookie

"use server";
import { cookies } from "next/headers";

export async function loginAction(email: string, password: string) {
  const store = await cookies();
  const existing = store.get("sw-context-token")?.value;

  const token = await login(email, password, existing);

  store.set("sw-context-token", token, {
    httpOnly: true,
    secure: true,
    sameSite: "lax",
    path: "/",
    maxAge: 60 * 60 * 24 * 30,
  });
}

Der Token verlässt damit nie das JavaScript-sichtbare Fenster. Jeder Store-API-Call, der die Kunden-Session braucht, läuft serverseitig und liest den Token aus dem Cookie — nicht aus dem Client.

5. Der CORS-Klassiker: Header wird nicht durchgereicht

Wenn du den Login doch direkt aus dem Browser machst und der Token-Header im Frontend trotz erfolgreicher Antwort leer ankommt, liegt das fast immer an CORS. Browser geben Response-Header nur dann an JavaScript weiter, wenn der Server sie über Access-Control-Expose-Headers explizit freigibt.

Heißt: Der Header sw-context-token muss in dieser Liste stehen, sonst ist er aus dem fetch()-Response nicht lesbar — obwohl er technisch ankommt. Genau diese Stunde Debugging spart man sich, wenn man den Token aus dem Response-Body (contextToken) liest statt aus dem Header. Beim serverseitigen Proxy-Ansatz aus Abschnitt 4 entfällt das Problem ohnehin, weil dann keine Cross-Origin-Anfrage aus dem Browser läuft.

6. Eingeloggter State, Kundendaten und Logout

Ob ein Nutzer eingeloggt ist, fragst du nicht über ein Flag im Frontend ab, sondern über /store-api/account/customer. Mit gültigem Kunden-Token liefert der Endpunkt die Stammdaten, mit einem anonymen Token antwortet er mit einem Fehler. In einer Server Component lässt sich daraus sauber der Login-Status ableiten, bevor die Seite gerendert wird — ohne Client-seitiges Flackern zwischen „eingeloggt" und „nicht eingeloggt".

Wichtig im B2B-Kontext: Erst nach dem Login liefert die Store API kundenspezifische Preise und Kundengruppen-Rabatte. Solange die Session anonym ist, siehst du den Listenpreis. Wer Vertragspreise anzeigt, muss den Auth-State also vor dem Preis-Rendering kennen.

Der Logout geht über POST /store-api/account/logout. Danach den gespeicherten Token verwerfen — und dem Nutzer für die weitere Sitzung einen frischen anonymen Kontext geben. Beim Cookie-Ansatz reicht ein cookies().delete(), der nächste Request startet automatisch eine neue Session.

7. Eigener Auth-Layer — oder doch beim Standard bleiben?

Die Store-API-Authentifizierung deckt Login, Registrierung, Passwort-Reset und Gast-Checkout vollständig ab. Für die meisten Headless-Projekte ist das genug — du baust einen dünnen Proxy in Next.js, hältst den Token im httpOnly-Cookie und überlässt Shopware die eigentliche Identitätsverwaltung.

Ein eigener Auth-Layer (etwa NextAuth oder ein externer Identity-Provider) lohnt sich erst, wenn Anforderungen dazukommen, die Shopware nicht nativ bedient: Single Sign-on über mehrere Systeme, Social Login oder ein gemeinsamer Account über Shop und Kundenportal hinweg. Dann wird Shopware zu einem von mehreren Backends, und der Context-Token ist nur noch ein Teil eines größeren Session-Modells.

Für den klassischen Shop gilt: Halte es einfach. Der Context-Token, ein serverseitiger Proxy und ein sicher gesetztes Cookie tragen erstaunlich weit — und ersparen dir die Komplexität eines zweiten Identitätssystems, das mit Shopwares Kundenmodell synchron bleiben muss.

Headless-Auth sauber aufsetzen?

Ob Login-Flow, sichere Token-Speicherung oder B2B-Preise im entkoppelten Frontend — ich helfe dir, die Store-API-Authentifizierung robust und sicher in dein Next.js-Projekt zu bringen, als technischer Lead oder als Sparringspartner für dein Team.

Bereit für den nächsten Sprint? Lass uns sprechen.

Google Meet Termin buchen

Dieser Link führt zu Google Calendar (Google Ireland Ltd.). Es gelten die Datenschutzbestimmungen von Google.