193 lines
7.1 KiB
PHP
193 lines
7.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
use Laravel\Socialite\Facades\Socialite;
|
|
|
|
class DashboardController extends Controller
|
|
{
|
|
/**
|
|
* Show the user dashboard.
|
|
*/
|
|
public function index()
|
|
{
|
|
$user = Auth::user();
|
|
|
|
// Get authorized apps for this user
|
|
$services = $user->authorizedApps();
|
|
|
|
return view('dashboard', compact('user', 'services'));
|
|
}
|
|
|
|
/**
|
|
* Launch a service app using SSO.
|
|
*/
|
|
public function launchSSO($id)
|
|
{
|
|
$user = Auth::user();
|
|
$app = \App\Models\App::findOrFail($id);
|
|
|
|
// Check if user is authorized to access this app
|
|
$authorizedApps = $user->authorizedApps();
|
|
if (!$authorizedApps->contains('id', $app->id)) {
|
|
return redirect()->route('dashboard')->with('error', 'Access denied. You do not have permission to access this service.');
|
|
}
|
|
|
|
// If user is logged in via Microsoft, route all service launches (including Keka)
|
|
// through the Microsoft SSO flow to ensure their browser has an active Microsoft session.
|
|
if ($user->microsoft_id) {
|
|
// Save target app URL in session so callback knows where to redirect
|
|
session(['sso_target_url' => $app->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) {
|
|
\Illuminate\Support\Facades\Log::error('Microsoft SSO launch failed', [
|
|
'message' => $e->getMessage()
|
|
]);
|
|
// Fallback: direct redirect
|
|
return redirect($app->url);
|
|
}
|
|
}
|
|
|
|
// For non-Microsoft or non-Microsoft-linked users, redirect directly
|
|
return redirect($app->url);
|
|
}
|
|
|
|
/**
|
|
* Handle the callback for Microsoft SSO.
|
|
*/
|
|
public function handleSSOCallback(Request $request)
|
|
{
|
|
// If Microsoft returns an error (user logged out or session expired)
|
|
if ($request->has('error')) {
|
|
Auth::logout();
|
|
$request->session()->invalidate();
|
|
$request->session()->regenerateToken();
|
|
return redirect()->route('login')->with('error', 'You have been signed out because your Microsoft session is no longer active.');
|
|
}
|
|
|
|
$user = Auth::user();
|
|
|
|
try {
|
|
$microsoftUser = Socialite::driver('microsoft')
|
|
->redirectUrl(route('sso.callback'))
|
|
->user();
|
|
|
|
if ($microsoftUser && $user) {
|
|
// Update tokens in db
|
|
$user->update([
|
|
'microsoft_token' => $microsoftUser->token,
|
|
'microsoft_refresh_token' => $microsoftUser->refreshToken ?? $user->microsoft_refresh_token,
|
|
]);
|
|
|
|
// Update verification timestamp
|
|
session(['ms_session_last_checked' => now()]);
|
|
}
|
|
} catch (\Exception $e) {
|
|
\Illuminate\Support\Facades\Log::error('Microsoft SSO callback failed', [
|
|
'message' => $e->getMessage()
|
|
]);
|
|
|
|
// Treat token exchange failure as an expired session
|
|
Auth::logout();
|
|
$request->session()->invalidate();
|
|
$request->session()->regenerateToken();
|
|
return redirect()->route('login')->with('error', 'Your Microsoft session has expired. Please log in again.');
|
|
}
|
|
|
|
$targetUrl = session('sso_target_url', 'https://outlook.office.com/mail/');
|
|
session()->forget('sso_target_url');
|
|
|
|
// If launching Microsoft Teams, redirect to the deep link launcher page
|
|
if (str_contains($targetUrl, 'teams.microsoft.com')) {
|
|
return redirect()->route('sso.launch-app', [
|
|
'name' => 'Microsoft Teams',
|
|
'url' => $targetUrl,
|
|
'protocol' => 'msteams://'
|
|
]);
|
|
}
|
|
|
|
return redirect($targetUrl);
|
|
}
|
|
|
|
/**
|
|
* Handle silent session verification callback from Microsoft.
|
|
*/
|
|
public function handleSessionCheckCallback(Request $request)
|
|
{
|
|
// If Microsoft returns an error (user logged out or session expired)
|
|
if ($request->has('error')) {
|
|
Auth::logout();
|
|
$request->session()->invalidate();
|
|
$request->session()->regenerateToken();
|
|
return redirect()->route('login')->with('error', 'You have been signed out because you signed out of your Microsoft account.');
|
|
}
|
|
|
|
try {
|
|
$microsoftUser = Socialite::driver('microsoft')
|
|
->redirectUrl(route('sso.callback-check'))
|
|
->user();
|
|
|
|
$user = Auth::user();
|
|
if ($microsoftUser && $user) {
|
|
// Update active tokens in DB
|
|
$user->update([
|
|
'microsoft_token' => $microsoftUser->token,
|
|
'microsoft_refresh_token' => $microsoftUser->refreshToken ?? $user->microsoft_refresh_token,
|
|
]);
|
|
|
|
// Update verification timestamp
|
|
session(['ms_session_last_checked' => now()]);
|
|
}
|
|
} catch (\Exception $e) {
|
|
\Illuminate\Support\Facades\Log::error('Microsoft session check callback failed', [
|
|
'message' => $e->getMessage()
|
|
]);
|
|
|
|
// Treat token exchange failure as an expired session
|
|
Auth::logout();
|
|
$request->session()->invalidate();
|
|
$request->session()->regenerateToken();
|
|
return redirect()->route('login')->with('error', 'Your Microsoft session has expired. Please log in again.');
|
|
}
|
|
|
|
// Redirect back to the page they were trying to access
|
|
$origin = session('sso_check_origin', route('dashboard'));
|
|
session()->forget('sso_check_origin');
|
|
return redirect($origin);
|
|
}
|
|
|
|
/**
|
|
* Render the desktop application launcher page with web fallback.
|
|
*/
|
|
public function launchApp(Request $request)
|
|
{
|
|
$name = $request->query('name', 'Application');
|
|
$url = $request->query('url');
|
|
$protocol = $request->query('protocol');
|
|
|
|
if (!$url || !$protocol) {
|
|
return redirect()->route('dashboard');
|
|
}
|
|
|
|
return view('sso.launch-app', compact('name', 'url', 'protocol'));
|
|
}
|
|
}
|