mitai-jinkendo/frontend/src/context/AuthContext.jsx
Lars ca9112ebc0
All checks were successful
Deploy Development / deploy (push) Successful in 37s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
fix: email verification auto-login and user experience
AuthContext:
- Added setAuthFromToken() for direct token/profile set
- Used for email verification auto-login (no /login request)
- Properly initializes session with token and profile

Verify.jsx:
- Fixed auto-login: now uses setAuthFromToken() instead of login()
- Added "already_verified" status for better UX
- Auto-redirect to /login after 3s if already verified
- Shows friendly message instead of error

This fixes:
- 422 Unprocessable Entity error during auto-login
- Empty dashboard page after verification (now redirects correctly)
- "Ungültiger Link" error on second click (now shows "bereits bestätigt")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 10:32:24 +01:00

134 lines
3.8 KiB
JavaScript

import { createContext, useContext, useState, useEffect } from 'react'
const AuthContext = createContext(null)
const TOKEN_KEY = 'bodytrack_token'
const PROFILE_KEY = 'bodytrack_active_profile'
export function AuthProvider({ children }) {
const [session, setSession] = useState(null) // {token, profile_id, role}
const [loading, setLoading] = useState(true)
const [needsSetup, setNeedsSetup] = useState(false)
useEffect(() => {
checkStatus()
}, [])
const checkStatus = async () => {
try {
const r = await fetch('/api/auth/status')
const status = await r.json()
if (status.needs_setup) {
setNeedsSetup(true)
setLoading(false)
return
}
// Try existing token
const token = localStorage.getItem(TOKEN_KEY)
if (token) {
const me = await fetch('/api/auth/me', {
headers: { 'X-Auth-Token': token }
})
if (me.ok) {
const profile = await me.json()
setSession({ token, profile_id: profile.id, role: profile.role, profile })
setLoading(false)
return
}
// Token expired
localStorage.removeItem(TOKEN_KEY)
}
} catch(e) {
console.error('Auth check failed', e)
}
setLoading(false)
}
const login = async (credentials) => {
// Support both new {email, pin} and legacy {profile_id, pin}
const body = typeof credentials === 'object' ? credentials : { profile_id: credentials }
const r = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})
if (!r.ok) {
const err = await r.json()
throw new Error(err.detail || 'Login fehlgeschlagen')
}
const data = await r.json()
localStorage.setItem(TOKEN_KEY, data.token)
localStorage.setItem(PROFILE_KEY, data.profile_id)
// Fetch full profile
const me = await fetch('/api/auth/me', { headers: { 'X-Auth-Token': data.token } })
const profile = await me.json()
setSession({ token: data.token, profile_id: data.profile_id, role: data.role, profile })
return data
}
const setup = async (formData) => {
const r = await fetch('/api/auth/setup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
})
if (!r.ok) {
const err = await r.json()
throw new Error(err.detail || 'Setup fehlgeschlagen')
}
const data = await r.json()
localStorage.setItem(TOKEN_KEY, data.token)
localStorage.setItem(PROFILE_KEY, data.profile_id)
setNeedsSetup(false)
await checkStatus()
return data
}
const setAuthFromToken = (token, profile) => {
// Direct token/profile set (for email verification auto-login)
localStorage.setItem(TOKEN_KEY, token)
localStorage.setItem(PROFILE_KEY, profile.id)
setSession({
token,
profile_id: profile.id,
role: profile.role || 'user',
profile
})
}
const logout = async () => {
const token = localStorage.getItem(TOKEN_KEY)
if (token) {
await fetch('/api/auth/logout', { method: 'POST', headers: { 'X-Auth-Token': token } })
}
localStorage.removeItem(TOKEN_KEY)
setSession(null)
}
const isAdmin = session?.role === 'admin'
const canUseAI = session?.profile?.ai_enabled !== 0
const canExport = session?.profile?.export_enabled !== 0
return (
<AuthContext.Provider value={{
session, loading, needsSetup,
login, setup, logout, setAuthFromToken,
isAdmin, canUseAI, canExport,
token: session?.token,
profileId: session?.profile_id,
}}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
return useContext(AuthContext)
}
export function getToken() {
return localStorage.getItem(TOKEN_KEY)
}