Files
bakery-ia/frontend/src/api/hooks/useAuth.ts

201 lines
5.8 KiB
TypeScript
Raw Normal View History

2025-08-03 17:48:34 +02:00
// frontend/src/api/hooks/useAuth.ts
/**
* Authentication Hooks
* React hooks for authentication operations
*/
import { useState, useEffect, useCallback } from 'react';
import { authService } from '../services';
import type {
LoginRequest,
LoginResponse,
RegisterRequest,
UserResponse,
PasswordResetRequest,
} from '../types';
// Token management
const TOKEN_KEY = 'auth_token';
const REFRESH_TOKEN_KEY = 'refresh_token';
const USER_KEY = 'user_data';
export const useAuth = () => {
const [user, setUser] = useState<UserResponse | null>(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Initialize auth state from localStorage
useEffect(() => {
const initializeAuth = async () => {
try {
const token = localStorage.getItem(TOKEN_KEY);
const userData = localStorage.getItem(USER_KEY);
if (token && userData) {
setUser(JSON.parse(userData));
setIsAuthenticated(true);
// Verify token is still valid
try {
const currentUser = await authService.getCurrentUser();
setUser(currentUser);
} catch (error) {
2025-08-08 23:06:54 +02:00
// Token might be expired - let interceptors handle refresh
// Only logout if refresh also fails (handled by ErrorRecoveryInterceptor)
console.log('Token verification failed, interceptors will handle refresh if possible');
// Check if we have a refresh token - if not, logout immediately
const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY);
if (!refreshToken) {
console.log('No refresh token available, logging out');
logout();
}
2025-08-03 17:48:34 +02:00
}
}
} catch (error) {
console.error('Auth initialization error:', error);
logout();
} finally {
setIsLoading(false);
}
};
initializeAuth();
}, []);
const login = useCallback(async (credentials: LoginRequest): Promise<void> => {
try {
setIsLoading(true);
setError(null);
const response = await authService.login(credentials);
// Store tokens and user data
localStorage.setItem(TOKEN_KEY, response.access_token);
if (response.refresh_token) {
localStorage.setItem(REFRESH_TOKEN_KEY, response.refresh_token);
}
if (response.user) {
localStorage.setItem(USER_KEY, JSON.stringify(response.user));
setUser(response.user);
}
setIsAuthenticated(true);
} catch (error) {
const message = error instanceof Error ? error.message : 'Login failed';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const register = useCallback(async (data: RegisterRequest): Promise<void> => {
try {
setIsLoading(true);
setError(null);
const response = await authService.register(data);
// Auto-login after successful registration
if (response.user) {
await login({ email: data.email, password: data.password });
}
} catch (error) {
const message = error instanceof Error ? error.message : 'Registration failed';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, [login]);
const logout = useCallback(async (): Promise<void> => {
try {
// Call logout endpoint if authenticated
if (isAuthenticated) {
await authService.logout();
}
} catch (error) {
console.error('Logout error:', error);
} finally {
// Clear local state regardless of API call success
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem(REFRESH_TOKEN_KEY);
localStorage.removeItem(USER_KEY);
setUser(null);
setIsAuthenticated(false);
setError(null);
}
}, [isAuthenticated]);
const updateProfile = useCallback(async (data: Partial<UserResponse>): Promise<void> => {
try {
setIsLoading(true);
setError(null);
const updatedUser = await authService.updateProfile(data);
setUser(updatedUser);
localStorage.setItem(USER_KEY, JSON.stringify(updatedUser));
} catch (error) {
const message = error instanceof Error ? error.message : 'Profile update failed';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const requestPasswordReset = useCallback(async (data: PasswordResetRequest): Promise<void> => {
try {
setIsLoading(true);
setError(null);
await authService.requestPasswordReset(data);
} catch (error) {
const message = error instanceof Error ? error.message : 'Password reset request failed';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
const changePassword = useCallback(async (currentPassword: string, newPassword: string): Promise<void> => {
try {
setIsLoading(true);
setError(null);
await authService.changePassword(currentPassword, newPassword);
} catch (error) {
const message = error instanceof Error ? error.message : 'Password change failed';
setError(message);
throw error;
} finally {
setIsLoading(false);
}
}, []);
return {
user,
isAuthenticated,
isLoading,
error,
login,
register,
logout,
updateProfile,
requestPasswordReset,
changePassword,
clearError: () => setError(null),
};
};
// Hook for getting authentication headers
export const useAuthHeaders = () => {
const getAuthHeaders = useCallback(() => {
const token = localStorage.getItem(TOKEN_KEY);
return token ? { Authorization: `Bearer ${token}` } : {};
}, []);
return { getAuthHeaders };
};