added encryption

This commit is contained in:
2026-03-09 10:54:07 +05:30
parent 6e184dc590
commit 6720e28d08
27 changed files with 2093 additions and 709 deletions

View File

@@ -15,11 +15,25 @@ import {
} from 'firebase/auth'
import { auth, googleProvider } from '../lib/firebase'
import { registerUser, getUserByEmail } from '../lib/api'
import {
deriveSecretKey,
generateDeviceKey,
generateSalt,
getSalt,
saveSalt,
getDeviceKey,
saveDeviceKey,
encryptSecretKey,
decryptSecretKey,
saveEncryptedSecretKey,
getEncryptedSecretKey,
} from '../lib/crypto'
type AuthContextValue = {
user: User | null
userId: string | null
loading: boolean
secretKey: Uint8Array | null
signInWithGoogle: () => Promise<void>
signOut: () => Promise<void>
}
@@ -29,17 +43,78 @@ const AuthContext = createContext<AuthContextValue | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [userId, setUserId] = useState<string | null>(null)
const [secretKey, setSecretKey] = useState<Uint8Array | null>(null)
const [loading, setLoading] = useState(true)
// Initialize encryption keys on login
async function initializeEncryption(authUser: User, token: string) {
try {
const firebaseUID = authUser.uid
const firebaseIDToken = token
// Get or create salt
let salt = getSalt()
if (!salt) {
salt = generateSalt()
saveSalt(salt)
}
// Derive master key from Firebase credentials
const derivedKey = await deriveSecretKey(firebaseUID, firebaseIDToken, salt)
// Check if device key exists
let deviceKey = await getDeviceKey()
if (!deviceKey) {
// First login on this device: generate device key
deviceKey = await generateDeviceKey()
await saveDeviceKey(deviceKey)
}
// Check if encrypted key exists in IndexedDB
const cachedEncrypted = await getEncryptedSecretKey()
if (!cachedEncrypted) {
// First login (or IndexedDB cleared): encrypt and cache the key
const encrypted = await encryptSecretKey(derivedKey, deviceKey)
await saveEncryptedSecretKey(encrypted.ciphertext, encrypted.nonce)
} else {
// Subsequent login on same device: verify we can decrypt
// (This ensures device key is correct)
try {
await decryptSecretKey(
cachedEncrypted.ciphertext,
cachedEncrypted.nonce,
deviceKey
)
} catch (error) {
console.warn('Device key mismatch, regenerating...', error)
// Device key doesn't match - regenerate
deviceKey = await generateDeviceKey()
await saveDeviceKey(deviceKey)
const encrypted = await encryptSecretKey(derivedKey, deviceKey)
await saveEncryptedSecretKey(encrypted.ciphertext, encrypted.nonce)
}
}
// Keep secret key in memory for session
setSecretKey(derivedKey)
} catch (error) {
console.error('Error initializing encryption:', error)
throw error
}
}
// Register or fetch user from MongoDB
async function syncUserWithDatabase(authUser: User) {
try {
const token = await authUser.getIdToken()
const email = authUser.email!
// Initialize encryption before syncing user
await initializeEncryption(authUser, token)
// Try to get existing user
try {
const existingUser = await getUserByEmail(email, token)
const existingUser = await getUserByEmail(email, token) as { id: string }
setUserId(existingUser.id)
} catch (error) {
// User doesn't exist, register them
@@ -50,11 +125,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
photoURL: authUser.photoURL || undefined,
},
token
)
) as { id: string }
setUserId(newUser.id)
}
} catch (error) {
console.error('Error syncing user with database:', error)
throw error
}
}
@@ -62,9 +138,14 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const unsubscribe = onAuthStateChanged(auth, async (u) => {
setUser(u)
if (u) {
await syncUserWithDatabase(u)
try {
await syncUserWithDatabase(u)
} catch (error) {
console.error('Auth sync failed:', error)
}
} else {
setUserId(null)
setSecretKey(null)
}
setLoading(false)
})
@@ -77,6 +158,10 @@ export function AuthProvider({ children }: { children: ReactNode }) {
}
async function signOut() {
// Clear secret key from memory
setSecretKey(null)
// Keep device key and encrypted key for next login
// Do NOT clear localStorage or IndexedDB
await firebaseSignOut(auth)
setUserId(null)
}
@@ -84,6 +169,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const value: AuthContextValue = {
user,
userId,
secretKey,
loading,
signInWithGoogle,
signOut,