diff --git a/web/normogen-web/src/App.tsx b/web/normogen-web/src/App.tsx index 9b9fae1..652fa2a 100644 --- a/web/normogen-web/src/App.tsx +++ b/web/normogen-web/src/App.tsx @@ -2,7 +2,6 @@ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import { LoginPage } from './pages/LoginPage'; import { RegisterPage } from './pages/RegisterPage'; import { RecoveryPage } from './pages/RecoveryPage'; -import { UnlockPage } from './pages/UnlockPage'; import { Dashboard } from './pages/Dashboard'; import { ProtectedRoute } from './components/common/ProtectedRoute'; @@ -14,7 +13,6 @@ function App() { } /> } /> } /> - } /> {/* Protected routes */} = ({ children }) => { if (isLoading) { return ( -

Loading...

@@ -27,12 +26,5 @@ export const ProtectedRoute: React.FC = ({ children }) => { return ; } - // Zero-knowledge: the user is authenticated (JWT valid) but the in-memory - // encryption key is gone (page reload). Redirect to the unlock screen to - // re-derive it without a full re-login. - if (!hasEncKey()) { - return ; - } - return <>{children}; }; diff --git a/web/normogen-web/src/pages/UnlockPage.tsx b/web/normogen-web/src/pages/UnlockPage.tsx deleted file mode 100644 index 2752138..0000000 --- a/web/normogen-web/src/pages/UnlockPage.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { useState, type FC } from 'react'; -import { useNavigate, Link } from 'react-router-dom'; -import { - Container, - Paper, - TextField, - Button, - Typography, - Box, - Alert, - CircularProgress, -} from '@mui/material'; -import { Lock as LockIcon } from '@mui/icons-material'; -import { useAuthStore } from '../store/useStore'; -import { unlockWithPassword, deriveAuthAndEncKeys, setEncKey } from '../crypto'; - -export const UnlockPage: FC = () => { - const navigate = useNavigate(); - const { wrapped_dek, wrapped_dek_iv, user } = useAuthStore(); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [unlocking, setUnlocking] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(''); - setUnlocking(true); - - try { - if (wrapped_dek && wrapped_dek_iv) { - // Wrapped-DEK model: unwrap the DEK using the password. - const { dek } = await unlockWithPassword(password, { - data: wrapped_dek, - iv: wrapped_dek_iv, - }); - setEncKey(dek); - } else { - // Phase 1 compat: derive the key directly from the password. - const { encKey } = await deriveAuthAndEncKeys(password); - setEncKey(encKey); - } - navigate('/', { replace: true }); - } catch { - setError('Incorrect password. Please try again.'); - } finally { - setUnlocking(false); - } - }; - - return ( - - - - - - Unlock - - - {user?.username - ? `Welcome back, ${user.username}. Enter your password to decrypt your data.` - : 'Enter your password to decrypt your data.'} - - - {error && ( - - {error} - - )} - - - setPassword(e.target.value)} - disabled={unlocking} - /> - - - - Sign in with a different account - - - - Forgot password? - - - - - - - - ); -}; - -export default UnlockPage; diff --git a/web/normogen-web/src/store/useStore.ts b/web/normogen-web/src/store/useStore.ts index 1bcfda5..32b1fe2 100644 --- a/web/normogen-web/src/store/useStore.ts +++ b/web/normogen-web/src/store/useStore.ts @@ -32,12 +32,7 @@ interface AuthState { isAuthenticated: boolean; isLoading: boolean; error: string | null; - // Persisted wrapped DEK — safe to store (AES-GCM ciphertext, useless without - // the password). Used by the unlock screen to re-derive the in-memory DEK - // on page reload without a full re-login. - wrapped_dek: string | null; - wrapped_dek_iv: string | null; - + // Actions login: (email: string, password: string) => Promise; register: (username: string, email: string, password: string, recoveryPhrase?: string) => Promise; @@ -125,8 +120,6 @@ export const useAuthStore = create()( isAuthenticated: false, isLoading: false, error: null, - wrapped_dek: null, - wrapped_dek_iv: null, login: async (email: string, password: string) => { set({ isLoading: true, error: null }); @@ -157,8 +150,6 @@ export const useAuthStore = create()( token: response.token, isAuthenticated: true, isLoading: false, - wrapped_dek: response.wrapped_dek ?? null, - wrapped_dek_iv: response.wrapped_dek_iv ?? null, }); } catch (error: any) { clearEncKey(); @@ -200,8 +191,6 @@ export const useAuthStore = create()( token: response.token, isAuthenticated: true, isLoading: false, - wrapped_dek: setup.passwordWrappedDek.data, - wrapped_dek_iv: setup.passwordWrappedDek.iv, }); } catch (error: any) { set({ @@ -239,8 +228,7 @@ export const useAuthStore = create()( newWrapped.data, newWrapped.iv, ); - // Persist the new wrapped DEK so unlock works with the new password. - set({ isLoading: false, wrapped_dek: newWrapped.data, wrapped_dek_iv: newWrapped.iv }); + set({ isLoading: false }); } catch (error: any) { clearEncKey(); set({ @@ -259,8 +247,6 @@ export const useAuthStore = create()( token: null, isAuthenticated: false, error: null, - wrapped_dek: null, - wrapped_dek_iv: null, }); }, @@ -293,8 +279,6 @@ export const useAuthStore = create()( token: state.token, user: state.user, isAuthenticated: state.isAuthenticated, - wrapped_dek: state.wrapped_dek, - wrapped_dek_iv: state.wrapped_dek_iv, }), } )