redirect to user login first * 2. If user authenticated -> verify access and switch to organization * 3. Redirect to intended URL or main page */ public function directLogin(Request $request, $organizationId) { // Validate organization exists and load managers $organization = Organization::with('managers')->find($organizationId); if (!$organization) { abort(404, __('Organization not found')); } // Get optional intended destination after successful org switch $intendedUrl = $request->query('intended'); // Check if user is authenticated on web guard if (!Auth::guard('web')->check()) { // User not logged in - redirect to user login with return URL $returnUrl = route('organization.direct-login', ['organizationId' => $organizationId]); if ($intendedUrl) { $returnUrl .= '?intended=' . urlencode($intendedUrl); } // Store in session for Laravel to redirect after login session()->put('url.intended', $returnUrl); // Get the first manager's name to pre-populate the login form $firstManager = $organization->managers()->first(); \Log::info('OrganizationLoginController: Redirecting unauthenticated user', [ 'organization_id' => $organizationId, 'manager_found' => $firstManager ? 'yes' : 'no', 'manager_name' => $firstManager ? $firstManager->name : null, ]); if ($firstManager) { // Build the login URL with the name parameter and localization $loginRoute = route('login') . '?name=' . urlencode($firstManager->name); $loginUrl = \Mcamara\LaravelLocalization\Facades\LaravelLocalization::getLocalizedURL(null, $loginRoute); \Log::info('OrganizationLoginController: Redirect URL', [ 'url' => $loginUrl, ]); return redirect($loginUrl); } return redirect()->route('login'); } // User is authenticated - verify they own/manage this organization $user = Auth::guard('web')->user(); $userWithRelations = User::with('organizations')->find($user->id); if (!$userWithRelations || !$userWithRelations->organizations->contains('id', $organizationId)) { abort(403, __('You do not have access to this organization')); } // Switch to organization guard directly (no password required for organizations) $this->switchGuard('organization', $organization); // Set active profile session session([ 'activeProfileType' => get_class($organization), 'activeProfileId' => $organization->id, 'activeProfileName' => $organization->name, 'activeProfilePhoto' => $organization->profile_photo_path, 'last_activity' => now(), 'profile-switched-notification' => true, ]); // Re-activate profile if inactive if (timebank_config('profile_inactive.re-activate_at_login')) { if (!$organization->isActive()) { $organization->inactive_at = null; $organization->save(); info('Organization re-activated: ' . $organization->name); } } // Broadcast profile switch event event(new \App\Events\ProfileSwitchEvent($organization)); // Redirect to intended URL or main page if ($intendedUrl) { return redirect($intendedUrl); } return redirect()->route('main'); } public function showLoginForm() { $user = Auth::guard('web')->user(); $type = session('intended_profile_switch_type'); $id = session('intended_profile_switch_id'); $profile = $this->getTargetProfileByTypeAndId($user, $type, $id); return view('profile-organization.login', ['profile' => $profile]); } public function login(Request $request) { $request->validate([ 'password' => 'required', ]); $user = Auth::guard('web')->user(); $type = session('intended_profile_switch_type'); $id = session('intended_profile_switch_id'); $organization = $this->getTargetProfileByTypeAndId($user, $type, $id); if (!$organization) { return back()->withErrors(['index' => __('Organization not found')]); } // Legacy Cyclos password support if (!empty($organization->cyclos_salt)) { info('Auth attempt using original Cyclos password'); $concatenated = $organization->cyclos_salt . $request->password; $hashedInputPassword = hash("sha256", $concatenated); if (strtolower($hashedInputPassword) === strtolower($organization->password)) { info('Auth success: Password is verified'); // Rehash to Laravel hash and remove salt $organization->password = \Hash::make($request->password); $organization->cyclos_salt = null; $organization->save(); info('Auth success: Cyclos password has been rehashed for next login'); } } // Check if the provided password matches the hashed password in the database if (\Hash::check($request->password, $organization->password)) { $this->switchGuard('organization', $organization); // log in as organization // Remove intended switch from session session()->forget(['intended_profile_switch_type', 'intended_profile_switch_id']); // Set active profile session as before session([ 'activeProfileType' => get_class($organization), 'activeProfileId' => $organization->id, 'activeProfileName' => $organization->name, 'activeProfilePhoto' => $organization->profile_photo_path, 'last_activity' => now(), 'profile-switched-notification' => true, ]); // Re-activate profile if inactive if (timebank_config('profile_inactive.re-activate_at_login')) { if (!$organization->isActive()) { $organization->inactive_at = null; $organization->save(); info('Organization re-activated: ' . $organization->name); } } event(new \App\Events\ProfileSwitchEvent($organization)); // Check for intended URL from direct login flow $intendedUrl = session('organization_login_intended_url'); if ($intendedUrl) { session()->forget('organization_login_intended_url'); return redirect($intendedUrl); } return redirect()->route('main'); // Or your special organization main page } info('Auth failed: Input password does not match stored password'); return back()->withErrors(['password' => __('Invalid organization password')]); } //TODO: Move to Trait as suggested below. /** * Helper method to get the target profile model based on the index. * Note: This duplicates logic from ProfileSelect::mount. Consider refactoring * this logic into a service or trait if used in multiple places. */ private function getTargetProfileByTypeAndId($user, $type, $id) { if (!$type || !$id) { return null; } $userWithRelations = User::with(['organizations', 'banksManaged', 'admins'])->find($user->id); if (!$userWithRelations) return null; if (strtolower($type) === 'organization') { return $userWithRelations->organizations->firstWhere('id', $id); } elseif (strtolower($type) === 'bank') { return $userWithRelations->banksManaged->firstWhere('id', $id); } elseif (strtolower($type) === 'admin') { return $userWithRelations->admins->firstWhere('id', $id); } return null; } }