feat: wire register to wrapped-DEK model + recovery phrase UI
Some checks failed
Lint and Build / format (push) Successful in 38s
Lint and Build / clippy (push) Successful in 1m35s
Lint and Build / build (push) Has been cancelled
Lint and Build / test (push) Has been cancelled

Register now generates a DEK, wraps it under the password KEK and recovery KEK,
and sends all wrapped forms to the server (via setupEncryption). RegisterPage
has an optional recovery phrase field with a warning. Login unwraps the DEK
from the response via unlockWithPassword. This completes the full ZK recovery
flow end-to-end from the UI.

Verified: npm build clean, 20 tests pass.
This commit is contained in:
goose 2026-06-29 08:45:56 -03:00
parent 4a63b1c972
commit 8a538cbbb8
2 changed files with 44 additions and 58 deletions

View file

@ -35,7 +35,7 @@ interface AuthState {
// Actions
login: (email: string, password: string) => Promise<void>;
register: (username: string, email: string, password: string) => Promise<void>;
register: (username: string, email: string, password: string, recoveryPhrase?: string) => Promise<void>;
recover: (email: string, recoveryPhrase: string, newPassword: string) => Promise<void>;
logout: () => Promise<void>;
clearError: () => void;
@ -162,19 +162,25 @@ export const useAuthStore = create<AuthState>()(
}
},
register: async (username: string, email: string, password: string) => {
register: async (username: string, email: string, password: string, recoveryPhrase?: string) => {
set({ isLoading: true, error: null });
try {
const { authSecret, encKey } = await deriveAuthAndEncKeys(password);
setEncKey(encKey);
// For now register uses the Phase 1 key derivation (no recovery phrase
// in the basic register flow). The wrapped-DEK recovery fields are
// optional on the register request.
// Zero-knowledge: generate a DEK, wrap it under the password KEK
// and (if provided) the recovery KEK. Send the wrapped forms + the
// recovery proof to the server; the server stores them verbatim.
const setup = await setupEncryption(password, recoveryPhrase);
setEncKey(setup.dek);
const response = await apiService.register({
username,
email,
password: authSecret,
});
password: setup.authSecret,
wrapped_dek: setup.passwordWrappedDek.data,
wrapped_dek_iv: setup.passwordWrappedDek.iv,
recovery_wrapped_dek: setup.recoveryWrappedDek?.data,
recovery_wrapped_dek_iv: setup.recoveryWrappedDek?.iv,
recovery_phrase: setup.recoveryKekHash,
} as any);
set({
user: {
user_id: response.user_id,