* @created 2025-08-08 * @location Made in Germany 🇩🇪 */ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Http\Requests\Auth\LoginRequest; use App\Http\Requests\Auth\RegisterRequest; use App\Http\Requests\Auth\UpdateProfileRequest; use App\Interfaces\UserRepositoryInterface; use App\Services\AuthService; use App\Services\ProfileCompletionService; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Illuminate\Http\JsonResponse; use Illuminate\Validation\ValidationException; use Exception; /** * Authentication Controller * Handles user authentication, registration, and session management with repository pattern */ class AuthController extends Controller { /** * User repository instance */ private UserRepositoryInterface $userRepository; /** * AuthService instance */ private AuthService $authService; /** * Profile completion service instance */ private ProfileCompletionService $profileCompletionService; /** * Maximum login attempts before lockout */ private const MAX_LOGIN_ATTEMPTS = 5; /** * Account lockout duration in minutes */ private const LOCKOUT_DURATION = 15; /** * Create a new controller instance with dependency injection */ public function __construct( UserRepositoryInterface $userRepository, AuthService $authService, ProfileCompletionService $profileCompletionService ) { $this->userRepository = $userRepository; $this->authService = $authService; $this->profileCompletionService = $profileCompletionService; $this->middleware('guest')->except(['logout', 'profile', 'updateProfile', 'activity']); $this->middleware('auth')->only(['logout', 'profile', 'updateProfile', 'activity']); } /** * Show the login form */ public function showLoginForm(): View { return view('auth.login', [ 'title' => 'Sign In - Professional Resume Builder', 'description' => 'Access your professional resume builder account' ]); } /** * Handle a login request with enhanced maintainability * * @param LoginRequest $request * @return RedirectResponse */ public function login(LoginRequest $request): RedirectResponse { try { $credentials = $request->validated(); $loginResult = $this->authService->attemptLogin($credentials); return $this->handleLoginResult($loginResult, $request); } catch (ValidationException $e) { return $this->handleLoginValidationError($e, $request); } catch (Exception $e) { return $this->handleLoginSystemError($e, $request); } } /** * Handle the result of a login attempt * * @param array $loginResult * @param LoginRequest $request * @return RedirectResponse */ private function handleLoginResult(array $loginResult, LoginRequest $request): RedirectResponse { if ($loginResult['success']) { return $this->handleSuccessfulLogin($loginResult, $request); } return $this->handleFailedLogin($loginResult, $request); } /** * Handle successful login response * * @param array $loginResult * @param LoginRequest $request * @return RedirectResponse */ private function handleSuccessfulLogin(array $loginResult, LoginRequest $request): RedirectResponse { $request->session()->regenerate(); Log::info('User logged in successfully', [ 'user_id' => $loginResult['user']->id, 'email' => $loginResult['user']->email, 'ip_address' => $request->ip(), 'user_agent' => $request->userAgent(), 'login_method' => 'standard' ]); $welcomeMessage = $this->getWelcomeMessage($loginResult['user']); $redirectUrl = $this->getPostLoginRedirectUrl($loginResult['user']); return redirect()->intended($redirectUrl)->with('success', $welcomeMessage); } /** * Handle failed login response * * @param array $loginResult * @param LoginRequest $request * @return RedirectResponse */ private function handleFailedLogin(array $loginResult, LoginRequest $request): RedirectResponse { $errorMessage = $this->getLoginErrorMessage($loginResult['reason']); Log::warning('Login attempt failed', [ 'email' => $request->input('email'), 'reason' => $loginResult['reason'], 'ip_address' => $request->ip(), 'user_agent' => $request->userAgent() ]); return back() ->withErrors(['email' => $errorMessage]) ->onlyInput('email'); } /** * Handle validation errors during login * * @param ValidationException $e * @param LoginRequest $request * @return RedirectResponse */ private function handleLoginValidationError(ValidationException $e, LoginRequest $request): RedirectResponse { Log::warning('Login validation error', [ 'email' => $request->input('email'), 'errors' => $e->errors(), 'ip_address' => $request->ip() ]); return back() ->withErrors($e->errors()) ->onlyInput('email'); } /** * Handle system errors during login * * @param Exception $e * @param LoginRequest $request * @return RedirectResponse */ private function handleLoginSystemError(Exception $e, LoginRequest $request): RedirectResponse { Log::error('Login system error', [ 'error_message' => $e->getMessage(), 'error_trace' => $e->getTraceAsString(), 'email' => $request->input('email'), 'ip_address' => $request->ip(), 'timestamp' => now() ]); return back() ->withErrors(['email' => 'A system error occurred. Please try again or contact support.']) ->onlyInput('email'); } /** * Get personalized welcome message * * @param User $user * @return string */ private function getWelcomeMessage(User $user): string { $timeOfDay = $this->getTimeOfDay(); $firstName = $user->first_name ?? 'there'; return "Good {$timeOfDay}, {$firstName}! Welcome back to your professional resume builder."; } /** * Get post-login redirect URL based on user profile and activity state * * Intelligently determines the most appropriate landing page based on: * - Profile completion percentage (incomplete profiles → profile page) * - Resume creation status (no resumes → dashboard for guidance) * - Default → main dashboard * * @param User $user The authenticated user * @return string The route name for redirection */ private function getPostLoginRedirectUrl(User $user): string { $profileCompletion = $this->profileCompletionService->calculateCompletion($user); if ($profileCompletion < 50) { return route('profile'); } $userStats = $this->userRepository->getUserStatistics($user->id); if (($userStats['resumes_count'] ?? 0) === 0) { return route('dashboard'); } return route('dashboard'); } /** * Get appropriate error message based on failure reason * * @param string $reason * @return string */ private function getLoginErrorMessage(string $reason): string { switch ($reason) { case 'account_locked': return 'Account is temporarily locked due to too many failed attempts. Please try again later.'; case 'account_inactive': return 'Your account has been deactivated. Please contact support.'; case 'invalid_credentials': return 'The provided credentials do not match our records.'; case 'too_many_attempts': return 'Too many failed attempts. Account has been locked for security.'; case 'rate_limited': return 'Too many login attempts. Please wait before trying again.'; default: return 'Login failed. Please check your credentials and try again.'; } } /** * Get time of day greeting * * @return string */ private function getTimeOfDay(): string { $hour = now()->hour; if ($hour < 12) { return 'morning'; } elseif ($hour < 17) { return 'afternoon'; } else { return 'evening'; } } /** * Show the registration form */ public function showRegistrationForm(): View { return view('auth.register', [ 'title' => 'Create Account - Professional Resume Builder', 'description' => 'Join thousands of professionals creating outstanding resumes' ]); } /** * Handle a registration request */ public function register(RegisterRequest $request): RedirectResponse { try { $userData = $request->validated(); $user = $this->authService->createUser($userData); if ($user) { Auth::login($user); // Log successful registration logger()->info('User registered successfully', [ 'user_id' => $user->id, 'email' => $user->email, 'ip' => $request->ip(), 'user_agent' => $request->userAgent() ]); return redirect()->route('dashboard') ->with('success', 'Welcome! Your account has been created successfully.'); } return back()->withErrors([ 'email' => 'Unable to create account. Please try again.', ])->withInput(); } catch (\Exception $e) { logger()->error('Registration error', [ 'error' => $e->getMessage(), 'email' => $request->input('email'), 'ip' => $request->ip() ]); return back()->withErrors([ 'email' => 'An error occurred during registration. Please try again.', ])->withInput(); } } /** * Handle logout request */ public function logout(Request $request): RedirectResponse { // Log the logout logger()->info('User logged out', [ 'user_id' => Auth::id(), 'ip' => $request->ip() ]); Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect()->route('login') ->with('success', 'You have been successfully logged out.'); } /** * Show user profile with comprehensive analytics */ public function profile(): View { try { $user = Auth::user(); // Get user statistics from repository $userStats = $this->userRepository->getUserStatistics($user->id); // Get profile completion analysis from service $profileCompletion = $this->profileCompletionService->calculateCompletion($user); $profileAnalysis = $this->profileCompletionService->getDetailedAnalysis($user); // Prepare view data $viewData = [ 'title' => 'Profile Settings - Professional Resume Builder', 'user' => $user, 'userStats' => $userStats, 'profileCompletion' => $profileCompletion, 'profileAnalysis' => $profileAnalysis, 'accountSecurity' => [ 'two_factor_enabled' => $user->two_factor_secret !== null, 'email_verified' => $user->email_verified_at !== null, 'is_locked' => $this->userRepository->isAccountLocked($user->email) ] ]; // Log profile view for analytics Log::info('User profile viewed', [ 'user_id' => $user->id, 'profile_completion' => $profileCompletion, 'ip_address' => request()->ip(), 'timestamp' => now() ]); return view('auth.profile', $viewData); } catch (Exception $e) { Log::error('Error loading user profile', [ 'error_message' => $e->getMessage(), 'user_id' => Auth::id(), 'ip_address' => request()->ip(), 'timestamp' => now() ]); // Fallback with minimal data return view('auth.profile', [ 'title' => 'Profile Settings', 'user' => Auth::user(), 'userStats' => [], 'profileCompletion' => 0, 'error' => 'Unable to load profile data completely' ]); } } /** * Update user profile using professional form request validation * * Handles comprehensive profile updates with: * - Repository pattern for data persistence * - Structured logging for security audit trails * - Real-time profile completion calculation * - User-friendly completion status messaging * * @param UpdateProfileRequest $request Validated profile update data * @return RedirectResponse Response with success message or error state * @throws Exception When profile update fails due to system errors */ public function updateProfile(UpdateProfileRequest $request): RedirectResponse { try { $user = Auth::user(); $updateData = $request->validated(); $updatedUser = $this->userRepository->update($user->id, $updateData); Log::info('User profile updated successfully', [ 'user_id' => $user->id, 'updated_fields' => array_keys($updateData), 'ip_address' => $request->ip(), 'user_agent' => $request->userAgent(), 'timestamp' => now() ]); $completionPercentage = $this->profileCompletionService ->calculateCompletion($updatedUser); $message = "Profile updated successfully! "; if ($completionPercentage < 100) { $message .= "Your profile is {$completionPercentage}% complete."; } else { $message .= "Your profile is now 100% complete!"; } return back()->with('success', $message); } catch (Exception $e) { Log::error('Profile update failed', [ 'error_message' => $e->getMessage(), 'error_trace' => $e->getTraceAsString(), 'user_id' => Auth::id(), 'request_data' => $request->except(['password', 'password_confirmation']), 'ip_address' => $request->ip(), 'timestamp' => now() ]); return back() ->withErrors(['error' => 'Unable to update profile. Please try again.']) ->withInput(); } } /** * Retrieve comprehensive user activity and analytics data * * Provides detailed activity analytics including: * - User profile and authentication information * - Login activity patterns and security metrics * - Profile completion analysis with detailed breakdown * - Account security status and recommendations * - Usage statistics (resumes, templates, exports) * * @return JsonResponse Structured activity data with metadata * @throws Exception When analytics data retrieval fails */ public function activity(): JsonResponse { try { $user = Auth::user(); $userStats = $this->userRepository->getUserStatistics($user->id); $profileCompletion = $this->profileCompletionService ->calculateCompletion($user); $profileAnalysis = $this->profileCompletionService ->getDetailedAnalysis($user); $activityData = [ 'user_info' => [ 'id' => $user->id, 'name' => $user->first_name . ' ' . $user->last_name, 'email' => $user->email, 'member_since' => $user->created_at->format('F Y'), 'account_status' => $user->status ?? 'active' ], 'login_activity' => [ 'last_login' => $user->last_login_at ? $user->last_login_at->format('Y-m-d H:i:s') : null, 'last_login_ip' => $user->last_login_ip, 'total_logins' => $userStats['total_logins'] ?? 0, 'failed_attempts' => $user->login_attempts ?? 0 ], 'profile_metrics' => [ 'completion_percentage' => $profileCompletion, 'completed_fields' => $profileAnalysis['completed_fields'], 'missing_fields' => $profileAnalysis['missing_fields'], 'completion_score' => $profileAnalysis['weighted_score'] ], 'account_security' => [ 'two_factor_enabled' => $user->two_factor_secret !== null, 'email_verified' => $user->email_verified_at !== null, 'password_updated' => $user->password_updated_at ? $user->password_updated_at->format('Y-m-d') : null, 'is_locked' => $this->userRepository->isAccountLocked($user->email) ], 'activity_summary' => [ 'resumes_created' => $userStats['resumes_count'] ?? 0, 'templates_used' => $userStats['templates_used'] ?? 0, 'exports_generated' => $userStats['exports_count'] ?? 0, 'profile_views' => $userStats['profile_views'] ?? 0 ] ]; Log::info('User activity data requested', [ 'user_id' => $user->id, 'ip_address' => request()->ip(), 'timestamp' => now() ]); return response()->json([ 'success' => true, 'data' => $activityData, 'meta' => [ 'generated_at' => now()->toISOString(), 'version' => '1.0' ] ]); } catch (Exception $e) { Log::error('Failed to retrieve user activity data', [ 'error_message' => $e->getMessage(), 'user_id' => Auth::id(), 'ip_address' => request()->ip(), 'timestamp' => now() ]); return response()->json([ 'success' => false, 'message' => 'Unable to retrieve activity data', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal server error' ], 500); } } /** * Show forgot password form */ public function showForgotPasswordForm(): View { return view('auth.forgot-password', [ 'title' => 'Reset Password - Professional Resume Builder', 'description' => 'Enter your email to receive password reset instructions' ]); } }