diff --git a/apps/web/src/lib/auth/dev-stub.svelte.ts b/apps/web/src/lib/auth/dev-stub.svelte.ts index 96fa783..6df061b 100644 --- a/apps/web/src/lib/auth/dev-stub.svelte.ts +++ b/apps/web/src/lib/auth/dev-stub.svelte.ts @@ -99,34 +99,49 @@ class Session { return this.user?.id ?? this.stubId ?? null; } - /** Login gegen mana-auth, speichert accessToken + Profil. */ + /** + * Login gegen mana-auth — zwei Calls in einem Flow: + * + * 1. Better-Auth-Native `POST /api/auth/sign-in/email` setzt das + * SSO-Cookie (__Secure-mana.session_token) auf Domain `.mana.how` + * und liefert das User-Profil. Dieser Endpoint erzeugt im + * Gegensatz zu `/api/v1/auth/login` ein gültiges Set-Cookie auf + * der Response. + * 2. `POST /api/v1/auth/refresh` mit Cookie-Auth liefert den + * EdDSA-JWT-accessToken aus der frischen Session. + * + * Damit funktioniert auch der spätere Background-Refresh (gleicher + * Endpoint) — und User-Sessions überleben einen Tab-Close, wenn das + * localStorage geleert wurde aber das HttpOnly-Cookie noch lebt. + */ async login(email: string, password: string): Promise { - const r = await fetch(`${authBaseUrl()}/api/v1/auth/login`, { + const signIn = await fetch(`${authBaseUrl()}/api/auth/sign-in/email`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ email, password }), }); - if (!r.ok) { - const body = await r.text().catch(() => ''); - throw new Error(`Login fehlgeschlagen (${r.status}): ${body.slice(0, 120)}`); + if (!signIn.ok) { + const body = await signIn.text().catch(() => ''); + throw new Error(`Login fehlgeschlagen (${signIn.status}): ${body.slice(0, 120)}`); } - const data = (await r.json()) as { - accessToken: string; + const signInData = (await signIn.json()) as { user: { id: string; email: string; name?: string; accessTier?: string }; }; - const claims = decodeJwt(data.accessToken); - if (!claims) throw new Error('Auth-Server lieferte ein ungültiges Token zurück.'); - this.token = data.accessToken; + // JWT aus der Cookie-Session ziehen. + const refreshOk = await this.tryRefresh(); + if (!refreshOk || !this.token) { + throw new Error('Auth-Server lieferte kein gültiges Token nach Login.'); + } + this.user = { - id: data.user.id, - email: data.user.email, - name: data.user.name ?? null, - tier: data.user.accessTier ?? 'public', + id: signInData.user.id, + email: signInData.user.email, + name: signInData.user.name ?? null, + tier: signInData.user.accessTier ?? 'public', }; if (typeof window !== 'undefined') { - window.localStorage.setItem(TOKEN_KEY, data.accessToken); window.localStorage.setItem(USER_KEY, JSON.stringify(this.user)); } }