security: K2 fix — registration token flow
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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.')
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user