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)
 }})
@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')
+ );
+ }
}