From 520d0d139cd528bbb1f0da3de39cb21a9677f987 Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 14 May 2026 22:55:53 +0200 Subject: [PATCH] =?UTF-8?q?security:=20K2=20fix=20=E2=80=94=20registration?= =?UTF-8?q?=20token=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit registerUser() returns { registrationToken } (10-Min JWT) createProfile() sends it as X-Registration-Token header userId nie mehr aus Browser-Body — kommt aus signiertem Token Co-Authored-By: Claude Sonnet 4.6 --- src/api/directus.js | 12 +++++++----- src/components/auth/RegisterStep1.jsx | 4 ++-- src/components/auth/RegisterStep2.jsx | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/api/directus.js b/src/api/directus.js index aaa8919..086625f 100644 --- a/src/api/directus.js +++ b/src/api/directus.js @@ -28,7 +28,7 @@ export async function getMe(userToken) { return data // bereits vom API-Server entpackt } -// Registriert den Directus-User; gibt { userId } zurück +// Registriert den Directus-User; gibt { registrationToken } zurück (10 Min gültig) export async function registerUser(email, password) { const res = await fetch(`${BASE}/languparent/auth/register`, { method: 'POST', headers: json, @@ -36,7 +36,7 @@ export async function registerUser(email, password) { }) const data = await res.json() if (!res.ok) throw new Error(data.error || 'Registrierung fehlgeschlagen.') - return data // { userId } + return data // { registrationToken } } export async function checkUsername(username /*, _userToken — nicht mehr nötig */) { @@ -49,11 +49,13 @@ export async function checkUsername(username /*, _userToken — nicht mehr nöti } // Erstellt Profil über API-Server (Admin-Token bleibt server-seitig) +// userToken ist das kurzlebige Registration-Token aus registerUser() // Gibt { token, expiresIn } zurück — muss via saveToken gespeichert werden -export async function createProfile({ userId, username, nativeLang, targetLang /*, userToken — ignoriert */ }) { +export async function createProfile({ username, nativeLang, targetLang, userToken }) { const res = await fetch(`${BASE}/languparent/auth/profile`, { - method: 'POST', headers: json, - body: JSON.stringify({ userId, username, nativeLang, targetLang }), + method: 'POST', + headers: { ...json, 'X-Registration-Token': userToken }, + body: JSON.stringify({ username, nativeLang, targetLang }), }) const data = await res.json() if (!res.ok) throw new Error(data.error || 'Profilerstellung fehlgeschlagen.') diff --git a/src/components/auth/RegisterStep1.jsx b/src/components/auth/RegisterStep1.jsx index 6ff183e..31594fc 100644 --- a/src/components/auth/RegisterStep1.jsx +++ b/src/components/auth/RegisterStep1.jsx @@ -14,8 +14,8 @@ export default function RegisterStep1({ onSuccess }) { if (pw.length < 8) { setError('Passwort muss mindestens 8 Zeichen haben.'); return } setError(''); setLoading(true) try { - const { userId } = await registerUser(email, pw) - onSuccess(userId, null) + const { registrationToken } = await registerUser(email, pw) + onSuccess(null, registrationToken) // Token statt userId — AuthScreen speichert es als pendingToken } catch (err) { setError(err.message) } finally { diff --git a/src/components/auth/RegisterStep2.jsx b/src/components/auth/RegisterStep2.jsx index c4e8f22..7aaad29 100644 --- a/src/components/auth/RegisterStep2.jsx +++ b/src/components/auth/RegisterStep2.jsx @@ -35,7 +35,8 @@ export default function RegisterStep2({ userId, userToken, onSuccess }) { if (!available) { setError('Dieser Username ist bereits vergeben.'); setLoading(false); return } - const { token } = await createProfile({ userId, username, nativeLang, targetLang }) + // userToken ist das kurzlebige Registration-Token aus Schritt 1 + const { token } = await createProfile({ username, nativeLang, targetLang, userToken }) saveToken(token) setUser({ id: userId, username, language_native: nativeLang, language_target: targetLang }) onSuccess(username)