Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Ein Auth-Context und Login-Formular

Ein Auth-Context und Login-Formular

~16 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Der Token aus Kapitel 49 muss ZENTRAL verwaltet werden, ZUGÄNGLICH für JEDE Komponente, die wissen muss, OB und ALS WER der Nutzer eingeloggt ist – GENAU der Anwendungsfall aus Kapitel 73 für React Context.

Den AuthContext erstellen

src/context/AuthContext.tsx
import { createContext, useContext, useState, type ReactNode } from 'react';

interface AuthContextValue {
  token: string | null;
  login: (token: string) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextValue | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [token, setToken] = useState<string | null>(
    () => localStorage.getItem('token'),
  );

  function login(newToken: string) {
    localStorage.setItem('token', newToken);
    setToken(newToken);
  }

  function logout() {
    localStorage.removeItem('token');
    setToken(null);
  }

  return (
    <AuthContext.Provider value={{ token, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);

  if (!context) {
    throw new Error('useAuth muss innerhalb von AuthProvider verwendet werden');
  }

  return context;
}

Achtung: localStorage ist die EINFACHSTE Lösung, aber ANFÄLLIG für XSS (Kapitel 56 kündigte diese Abwägung an) – für UNSER Lernprojekt AUSREICHEND, für ein PRODUKTIONSSYSTEM würde ein HTTP-only-Cookie (ausgestellt vom Backend) das SICHERERE Muster sein, dann ALLERDINGS mit zusätzlichem CSRF-Schutz.

Den Provider einbinden

// main.tsx - AuthProvider UMSCHLIESST App, INNERHALB von QueryClientProvider
<QueryClientProvider client={queryClient}>
  <AuthProvider>
    <App />
  </AuthProvider>
</QueryClientProvider>

Das Login-Formular

src/pages/LoginPage.tsx
import { useState, type FormEvent } from 'react';
import { useMutation } from '@tanstack/react-query';
import { apiClient } from '../api/client';
import { useAuth } from '../context/AuthContext';

function LoginPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { login } = useAuth();

  const loginMutation = useMutation({
    mutationFn: async () => {
      const response = await apiClient.post<{ token: string }>('/login', {
        email,
        password,
      });

      return response.data;
    },
    onSuccess: (data) => {
      login(data.token);
    },
  });

  function handleSubmit(event: FormEvent) {
    event.preventDefault();
    loginMutation.mutate();
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type="submit" disabled={loginMutation.isPending}>
        Anmelden
      </button>
      {loginMutation.isError && <p>Anmeldung fehlgeschlagen</p>}
    </form>
  );
}

export default LoginPage;

useMutation statt useQuery – GENAU wie in der Symfony-Schulung Formulare zwischen ANZEIGEN (GET) und ÄNDERN (POST) unterscheiden, unterscheidet TanStack Query zwischen LESENDEN (useQuery) und SCHREIBENDEN (useMutation) Operationen.

Tipp: apiClient.post('/login', ...) nutzt den Content-Type: application/ld+json-Header aus Kapitel 74 – der Login-Endpunkt (Kapitel 49) verlangt aber SCHLICHTES JSON. Praktisch funktioniert das MEISTENS trotzdem (Symfony ist TOLERANT), sauberer wäre ein SEPARATER Content-Type-Override NUR für diesen Aufruf.