import { supabase } from './supabase-client.js'; import { cryptoService } from './crypto-service.js'; import { db } from '../db/database.js'; class AuthService { constructor() { this.user = null; this.session = null; this.onAuthStateChangeCallbacks = []; // Auto-setup auth listener supabase.auth.onAuthStateChange((event, session) => { this.session = session; this.user = session?.user || null; // If we log out, clear the crypto key if (event === 'SIGNED_OUT') { cryptoService.clearKey(); } this.notifyListeners(event, session); }); } async initialize() { const { data, error } = await supabase.auth.getSession(); if (data.session) { this.session = data.session; this.user = data.session.user; // We have a session, but we don't have the crypto key! // If the crypto key is null, the UI should prompt the user to "Unlock" their data // by entering their password again, OR we can try to store the derived key in sessionStorage // (not localStorage!) so it survives page reloads but dies when the tab closes. const restored = await cryptoService.restoreKey(); if (!restored) { // App will need to ask for password to unlock console.warn('Session active, but crypto key missing. Needs unlock.'); } } return { user: this.user, session: this.session }; } onAuthStateChange(callback) { this.onAuthStateChangeCallbacks.push(callback); } notifyListeners(event, session) { this.onAuthStateChangeCallbacks.forEach(cb => cb(event, session)); } async signUp(email, password) { // 1. Derive encryption key from password BEFORE signing up await cryptoService.deriveKey(password, email); sessionStorage.setItem('schoolmind_temp_key_marker', 'true'); // 2. Sign up with Supabase const { data, error } = await supabase.auth.signUp({ email, password, }); if (error) { cryptoService.clearKey(); sessionStorage.removeItem('schoolmind_temp_key_marker'); throw error; } return data; } async signIn(email, password) { // 1. Derive encryption key from password await cryptoService.deriveKey(password, email); sessionStorage.setItem('schoolmind_temp_key_marker', 'true'); // 2. Sign in with Supabase const { data, error } = await supabase.auth.signInWithPassword({ email, password, }); if (error) { cryptoService.clearKey(); sessionStorage.removeItem('schoolmind_temp_key_marker'); throw error; } return data; } async unlockCrypto(email, password) { // Used when returning to an already active session, to just derive the key await cryptoService.deriveKey(password, email); sessionStorage.setItem('schoolmind_temp_key_marker', 'true'); return true; } async signOut() { const { error } = await supabase.auth.signOut(); cryptoService.clearKey(); sessionStorage.removeItem('schoolmind_temp_key_marker'); if (error) throw error; } isCryptoUnlocked() { return cryptoService.key !== null; } } export const authService = new AuthService();