From 0efd443a5339db45438504964cbce99411a2b3b9 Mon Sep 17 00:00:00 2001 From: subhajit Date: Thu, 16 Jul 2026 17:42:04 +0530 Subject: [PATCH] implement crm login --- app/Http/Controllers/DashboardController.php | 47 +++++++++++++++++- resources/views/dashboard.blade.php | 4 +- routes/web.php | 1 + tests/Feature/SecurityAndAdminTest.php | 50 ++++++++++++++++++++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 69277b4..e5ad7a7 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -40,10 +40,14 @@ public function launchSSO($id) return redirect()->route('dashboard')->with('error', 'Access denied. You do not have permission to access this service.'); } + $url = $app->url; + if (str_contains(strtolower($url), 'convexcrm.convexsol.co')) { + $url = rtrim($url, '/') . '/admin/authentication/microsoft_login'; + } if ($user->microsoft_id) { // Save target app URL in session so callback knows where to redirect - session(['sso_target_url' => $app->url]); + session(['sso_target_url' => $url]); try { // Determine domain hint based on user's email domain to skip account type prompt @@ -242,4 +246,45 @@ public function destroyPersonalApp($id) return redirect()->route('dashboard')->with('success', 'Custom app deleted successfully.'); } + + /** + * Launch a personal app using SSO. + */ + public function launchPersonalSSO($id) + { + $user = Auth::user(); + $pApp = $user->personalApps()->findOrFail($id); + + $url = $pApp->url; + if (str_contains(strtolower($url), 'convexcrm.convexsol.co')) { + $url = rtrim($url, '/') . '/admin/authentication/microsoft_login'; + } + + if ($user->microsoft_id) { + // Save target app URL in session so callback knows where to redirect + session(['sso_target_url' => $url]); + + try { + // Determine domain hint based on user's email domain to skip account type prompt + $domainHint = (str_ends_with($user->email, '.onmicrosoft.com') || (str_contains($user->email, '@') && !str_ends_with($user->email, 'outlook.com') && !str_ends_with($user->email, 'hotmail.com') && !str_ends_with($user->email, 'live.com'))) + ? 'organizations' + : 'consumers'; + + // Redirect to Microsoft OAuth authorize page with login_hint, domain_hint, prompt=none and custom callback + return Socialite::driver('microsoft') + ->redirectUrl(route('sso.callback')) + ->with([ + 'login_hint' => $user->email, + 'domain_hint' => $domainHint, + 'prompt' => 'none' + ]) + ->redirect(); + } catch (\Exception $e) { + return redirect($url); + } + } + + // For non-Microsoft or non-Microsoft-linked users, redirect directly + return redirect($url); + } } diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 8f1556f..9c9c38b 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -862,7 +862,7 @@ -
+
@if($pApp->logo) {{ $pApp->name }} @else @@ -874,7 +874,7 @@
-

{{ strtoupper($pApp->name) }}

+

{{ strtoupper($pApp->name) }}

@if($pApp->tag)
{{ $pApp->tag }}
@endif diff --git a/routes/web.php b/routes/web.php index b2e9c16..6ad5465 100644 --- a/routes/web.php +++ b/routes/web.php @@ -25,6 +25,7 @@ Route::middleware(['auth', 'role:user'])->group(function () { Route::get('/sso/launch/{id}', [DashboardController::class, 'launchSSO'])->name('sso.launch'); + Route::get('/sso/personal-launch/{id}', [DashboardController::class, 'launchPersonalSSO'])->name('sso.personal-launch'); Route::get('/sso/callback', [DashboardController::class, 'handleSSOCallback'])->name('sso.callback'); Route::get('/sso/launch-app', [DashboardController::class, 'launchApp'])->name('sso.launch-app'); diff --git a/tests/Feature/SecurityAndAdminTest.php b/tests/Feature/SecurityAndAdminTest.php index 47883bf..7f2e697 100644 --- a/tests/Feature/SecurityAndAdminTest.php +++ b/tests/Feature/SecurityAndAdminTest.php @@ -517,4 +517,54 @@ public function test_user_personal_custom_apps(): void 'id' => $personalApp->id ]); } + + /** + * Test silent Microsoft SSO redirection for Convex CRM. + */ + public function test_convexcrm_silent_sso_redirection(): void + { + $user = User::create([ + 'name' => 'Convex CRM User', + 'email' => 'convexcrm@sentientgeeks.com', + 'role' => 'user', + 'microsoft_id' => 'mock-ms-id-777', + ]); + + $app = \App\Models\App::create([ + 'name' => 'Convex CRM', + 'url' => 'https://demo-convexcrm.convexsol.co/', + 'color' => '#123456', + 'tag' => 'CRM' + ]); + + // Enable access to this app + $devRole = Role::firstOrCreate(['name' => 'Developer'], ['description' => 'Developer role']); + $devRole->apps()->syncWithoutDetaching([$app->id]); + $user->roles()->syncWithoutDetaching([$devRole->id]); + + // Trigger SSO launch for corporate app + $response = $this->actingAs($user) + ->get(route('sso.launch', $app->id)); + + // It should initiate Microsoft OAuth redirect to refresh the session + // And save rewritten target URL (Convex CRM Microsoft login redirect endpoint) in session + $this->assertEquals( + 'https://demo-convexcrm.convexsol.co/admin/authentication/microsoft_login', + session('sso_target_url') + ); + + // Test personal custom app version + $pApp = $user->personalApps()->create([ + 'name' => 'My Custom Convex', + 'url' => 'https://demo-convexcrm.convexsol.co', + ]); + + $responsePersonal = $this->actingAs($user) + ->get(route('sso.personal-launch', $pApp->id)); + + $this->assertEquals( + 'https://demo-convexcrm.convexsol.co/admin/authentication/microsoft_login', + session('sso_target_url') + ); + } }