1+ /**
2+ * 🔐 Secure Authentication Hook
3+ * Enhanced authentication with security monitoring and rate limiting
4+ */
5+
6+ import { useState , useCallback , useEffect } from 'react' ;
7+ import { supabase } from '@/integrations/supabase/client' ;
8+ import { User , Session } from '@supabase/supabase-js' ;
9+ import { secureStorage } from '@/utils/secureClientStorage' ;
10+ import { rateLimiters } from '@/utils/inputValidation' ;
11+ import { securityMonitoring } from '@/utils/security' ;
12+ import { toast } from 'sonner' ;
13+
14+ interface SecureAuthState {
15+ user : User | null ;
16+ session : Session | null ;
17+ loading : boolean ;
18+ authenticated : boolean ;
19+ }
20+
21+ interface AuthAttemptLog {
22+ timestamp : number ;
23+ success : boolean ;
24+ method : string ;
25+ ip ?: string ;
26+ }
27+
28+ export const useSecureAuth = ( ) => {
29+ const [ authState , setAuthState ] = useState < SecureAuthState > ( {
30+ user : null ,
31+ session : null ,
32+ loading : true ,
33+ authenticated : false
34+ } ) ;
35+
36+ // Monitor authentication attempts
37+ const logAuthAttempt = useCallback ( ( method : string , success : boolean , details ?: any ) => {
38+ const attemptLog : AuthAttemptLog = {
39+ timestamp : Date . now ( ) ,
40+ success,
41+ method
42+ } ;
43+
44+ // Store in secure storage for monitoring
45+ secureStorage . setAuthData ( `auth_attempt_${ Date . now ( ) } ` , attemptLog , 1 ) ;
46+
47+ // Log security event
48+ securityMonitoring . logSuspiciousActivity ( {
49+ type : success ? 'successful_authentication' : 'failed_authentication' ,
50+ details : {
51+ method,
52+ ...details ,
53+ timestamp : attemptLog . timestamp
54+ }
55+ } ) ;
56+
57+ if ( ! success ) {
58+ console . warn ( '🔐 AUTH: Authentication attempt failed' , { method, details } ) ;
59+ }
60+ } , [ ] ) ;
61+
62+ // Check for rate limiting on auth attempts
63+ const checkAuthRateLimit = useCallback ( ( ip : string = 'unknown' ) => {
64+ const isAllowed = rateLimiters . auth . isAllowed ( { ip } ) ;
65+
66+ if ( ! isAllowed ) {
67+ securityMonitoring . logSuspiciousActivity ( {
68+ type : 'auth_rate_limit_exceeded' ,
69+ ip,
70+ details : {
71+ timestamp : Date . now ( ) ,
72+ maxRequests : 10 ,
73+ windowMs : 15 * 60 * 1000
74+ }
75+ } ) ;
76+
77+ toast . error ( 'Too many authentication attempts. Please wait before trying again.' ) ;
78+ throw new Error ( 'Rate limit exceeded' ) ;
79+ }
80+
81+ return true ;
82+ } , [ ] ) ;
83+
84+ // Secure sign up with comprehensive validation
85+ const signUp = useCallback ( async ( email : string , password : string ) => {
86+ try {
87+ // Rate limiting check
88+ checkAuthRateLimit ( ) ;
89+
90+ // Input validation
91+ if ( ! email || ! password ) {
92+ throw new Error ( 'Email and password are required' ) ;
93+ }
94+
95+ if ( password . length < 8 ) {
96+ throw new Error ( 'Password must be at least 8 characters long' ) ;
97+ }
98+
99+ const emailRegex = / ^ [ ^ \s @ ] + @ [ ^ \s @ ] + \. [ ^ \s @ ] + $ / ;
100+ if ( ! emailRegex . test ( email ) ) {
101+ throw new Error ( 'Invalid email format' ) ;
102+ }
103+
104+ // Check for common weak passwords
105+ const weakPasswords = [ 'password' , '12345678' , 'qwerty123' , 'password123' ] ;
106+ if ( weakPasswords . includes ( password . toLowerCase ( ) ) ) {
107+ throw new Error ( 'Password is too weak. Please choose a stronger password.' ) ;
108+ }
109+
110+ const redirectUrl = `${ window . location . origin } /` ;
111+
112+ const { data, error } = await supabase . auth . signUp ( {
113+ email : email . toLowerCase ( ) . trim ( ) ,
114+ password,
115+ options : {
116+ emailRedirectTo : redirectUrl
117+ }
118+ } ) ;
119+
120+ if ( error ) {
121+ logAuthAttempt ( 'signup' , false , { error : error . message , email } ) ;
122+ throw error ;
123+ }
124+
125+ logAuthAttempt ( 'signup' , true , { email, userId : data . user ?. id } ) ;
126+
127+ return { data, error : null } ;
128+
129+ } catch ( error : any ) {
130+ logAuthAttempt ( 'signup' , false , { error : error . message , email } ) ;
131+ console . error ( '🔐 AUTH: Sign up error:' , error ) ;
132+ return { data : null , error } ;
133+ }
134+ } , [ checkAuthRateLimit , logAuthAttempt ] ) ;
135+
136+ // Secure sign in with monitoring
137+ const signIn = useCallback ( async ( email : string , password : string ) => {
138+ try {
139+ // Rate limiting check
140+ checkAuthRateLimit ( ) ;
141+
142+ // Input validation
143+ if ( ! email || ! password ) {
144+ throw new Error ( 'Email and password are required' ) ;
145+ }
146+
147+ const { data, error } = await supabase . auth . signInWithPassword ( {
148+ email : email . toLowerCase ( ) . trim ( ) ,
149+ password
150+ } ) ;
151+
152+ if ( error ) {
153+ logAuthAttempt ( 'signin' , false , { error : error . message , email } ) ;
154+ throw error ;
155+ }
156+
157+ // Store secure session data
158+ if ( data . session ) {
159+ secureStorage . setAuthData ( 'last_signin' , {
160+ timestamp : Date . now ( ) ,
161+ userId : data . user ?. id ,
162+ email : data . user ?. email
163+ } , 24 ) ;
164+ }
165+
166+ logAuthAttempt ( 'signin' , true , { email, userId : data . user ?. id } ) ;
167+
168+ return { data, error : null } ;
169+
170+ } catch ( error : any ) {
171+ logAuthAttempt ( 'signin' , false , { error : error . message , email } ) ;
172+ console . error ( '🔐 AUTH: Sign in error:' , error ) ;
173+ return { data : null , error } ;
174+ }
175+ } , [ checkAuthRateLimit , logAuthAttempt ] ) ;
176+
177+ // Secure sign out with cleanup
178+ const signOut = useCallback ( async ( ) => {
179+ try {
180+ const { error } = await supabase . auth . signOut ( ) ;
181+
182+ if ( error ) {
183+ console . error ( '🔐 AUTH: Sign out error:' , error ) ;
184+ throw error ;
185+ }
186+
187+ // Clear sensitive data from secure storage
188+ const currentUser = authState . user ;
189+ if ( currentUser ) {
190+ logAuthAttempt ( 'signout' , true , { userId : currentUser . id } ) ;
191+
192+ // Clean up user-specific secure storage
193+ secureStorage . removeSecure ( 'auth_last_signin' ) ;
194+ secureStorage . cleanExpiredData ( ) ;
195+ }
196+
197+ return { error : null } ;
198+
199+ } catch ( error : any ) {
200+ logAuthAttempt ( 'signout' , false , { error : error . message } ) ;
201+ return { error } ;
202+ }
203+ } , [ authState . user , logAuthAttempt ] ) ;
204+
205+ // Initialize auth state and monitoring
206+ useEffect ( ( ) => {
207+ let mounted = true ;
208+
209+ // Set up auth state listener
210+ const { data : { subscription } } = supabase . auth . onAuthStateChange (
211+ async ( event , session ) => {
212+ if ( ! mounted ) return ;
213+
214+ console . log ( '🔐 AUTH: State change event:' , event ) ;
215+
216+ // Security monitoring for auth events
217+ if ( event === 'SIGNED_IN' && session ?. user ) {
218+ securityMonitoring . logSuspiciousActivity ( {
219+ type : 'user_session_started' ,
220+ userId : session . user . id ,
221+ details : {
222+ event,
223+ timestamp : Date . now ( ) ,
224+ userAgent : navigator . userAgent
225+ }
226+ } ) ;
227+ }
228+
229+ if ( event === 'SIGNED_OUT' ) {
230+ securityMonitoring . logSuspiciousActivity ( {
231+ type : 'user_session_ended' ,
232+ details : {
233+ event,
234+ timestamp : Date . now ( )
235+ }
236+ } ) ;
237+ }
238+
239+ setAuthState ( {
240+ user : session ?. user ?? null ,
241+ session : session ,
242+ loading : false ,
243+ authenticated : ! ! session ?. user
244+ } ) ;
245+ }
246+ ) ;
247+
248+ // Check for existing session
249+ supabase . auth . getSession ( ) . then ( ( { data : { session } } ) => {
250+ if ( mounted ) {
251+ setAuthState ( {
252+ user : session ?. user ?? null ,
253+ session : session ,
254+ loading : false ,
255+ authenticated : ! ! session ?. user
256+ } ) ;
257+ }
258+ } ) ;
259+
260+ return ( ) => {
261+ mounted = false ;
262+ subscription . unsubscribe ( ) ;
263+ } ;
264+ } , [ ] ) ;
265+
266+ // Session validation
267+ const validateSession = useCallback ( async ( ) => {
268+ try {
269+ const { data : { session } , error } = await supabase . auth . getSession ( ) ;
270+
271+ if ( error || ! session ) {
272+ console . warn ( '🔐 AUTH: Session validation failed' ) ;
273+ return false ;
274+ }
275+
276+ // Check if session is close to expiring (refresh if less than 5 minutes left)
277+ const expiresAt = session . expires_at ? session . expires_at * 1000 : 0 ;
278+ const fiveMinutes = 5 * 60 * 1000 ;
279+
280+ if ( expiresAt - Date . now ( ) < fiveMinutes ) {
281+ console . log ( '🔐 AUTH: Refreshing session' ) ;
282+ const { error : refreshError } = await supabase . auth . refreshSession ( ) ;
283+
284+ if ( refreshError ) {
285+ console . error ( '🔐 AUTH: Session refresh failed:' , refreshError ) ;
286+ return false ;
287+ }
288+ }
289+
290+ return true ;
291+
292+ } catch ( error ) {
293+ console . error ( '🔐 AUTH: Session validation error:' , error ) ;
294+ return false ;
295+ }
296+ } , [ ] ) ;
297+
298+ return {
299+ ...authState ,
300+ signUp,
301+ signIn,
302+ signOut,
303+ validateSession,
304+ isLoading : authState . loading
305+ } ;
306+ } ;
0 commit comments