FlowGames Auto-Login Prompt

Erstelle ein Auto-Login-System. Wenn jemand die URL /auto-login?token=TOKEN besucht, soll er automatisch eingeloggt werden, OHNE das Login-Formular zu sehen.

WICHTIG: Die Route /auto-login muss eine öffentliche Route sein — sie darf NICHT durch die Auth-Prüfung blockiert werden. Sie muss auch funktionieren, wenn der Nutzer nicht eingeloggt ist. Prüfe in App.jsx, ob es einen Auth-Guard gibt, der unangemeldete Nutzer auf die Login-Seite umleitet, und stelle sicher, dass /auto-login davon ausgenommen ist (genau wie die Login-Seite selbst).

Erstelle diese 3 Dateien und modifiziere 2 bestehende:

1. functions/validateFlowToken.js — Backend-Funktion die einen JWT verifiziert:

const SHARED_SECRET = Deno.env.get('DeutschFlow_Cooperativ_API_KEY');
async function verify(message, secret, signature) {
  const enc = new TextEncoder();
  const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
  const sig = await crypto.subtle.sign('HMAC', key, enc.encode(message));
  const expected = btoa(String.fromCharCode(...new Uint8Array(sig))).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
  return expected === signature;
}
Deno.serve(async (req) => {
  try {
    const { token } = await req.json();
    const [header, payload, signature] = token.split('.');
    const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
    if (Date.now() > decoded.exp) return Response.json({ valid: false }, { status: 401 });
    const isValid = await verify(header + '.' + payload, SHARED_SECRET, signature);
    if (!isValid) return Response.json({ valid: false }, { status: 401 });
    return Response.json({ valid: true, email: decoded.email, name: decoded.name });
  } catch (e) {
    return Response.json({ valid: false, error: e.message }, { status: 500 });
  }
});

2. lib/flowAuth.js:

export function getFlowUser() {
  const match = document.cookie.match(/(?:^|;\s*)flow_user=([^;]+)/);
  if (!match) return null;
  try {
    const email = decodeURIComponent(match[1]);
    return { email, full_name: email.split("@")[0], role: "flow_member" };
  } catch { return null; }
}

3. pages/AutoLogin.jsx:

import { useEffect } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { base44 } from "@/api/base44Client";
export default function AutoLogin() {
  const [params] = useSearchParams();
  const navigate = useNavigate();
  useEffect(() => {
    const token = params.get("token");
    if (!token) { navigate("/"); return; }
    base44.functions.invoke("validateFlowToken", { token })
      .then(res => {
        if (res.data?.valid) {
          document.cookie = "flow_user=" + encodeURIComponent(res.data.email) + "; path=/; max-age=2592000; SameSite=Lax";
          window.location.href = "/";
        } else { navigate("/"); }
      })
      .catch(() => navigate("/"));
  }, []);
  return <div className="flex items-center justify-center min-h-screen text-lg">Anmeldung läuft…</div>;
}

4. lib/AuthContext.jsx — füge ganz am Anfang des useEffect (bevor base44.auth.me() aufgerufen wird) diesen Cookie-Check ein:

import { getFlowUser } from "@/lib/flowAuth";
// ... im useEffect:
const flowUser = getFlowUser();
if (flowUser) {
  setUser(flowUser);
  setIsLoadingAuth(false);
  return;
}
// ... dann erst base44.auth.me()

5. App.jsx — füge /auto-login als öffentliche Route hinzu (gleiche Ebene wie Login, NICHT hinter dem Auth-Guard):

import AutoLogin from './pages/AutoLogin';
// ... in den öffentlichen Routes:
<Route path="/auto-login" element={<AutoLogin />} />