user(); if ($this->kekaOutlook->isConnected($user)) { try { // Ensures token is fresh; throws if refresh token is invalid/revoked. $this->kekaOutlook->getValidToken($user); return redirect()->away($this->kekaWebsiteUrl()); } catch (\RuntimeException $e) { // Refresh failed -> fall through to re-login below. } } return redirect()->away($this->kekaOutlook->getAuthUrl()); } /** * GET /keka/callback * * Microsoft redirects here after login with ?code=... or ?error=... */ public function callback(Request $request): RedirectResponse { if ($request->filled('error')) { return redirect() ->route('dashboard') ->with('error', 'Microsoft login was cancelled or failed: '.$request->get('error_description', $request->get('error'))); } if (! $this->kekaOutlook->isValidState($request->get('state'))) { return redirect() ->route('dashboard') ->with('error', 'Invalid login session. Please try connecting again.'); } $code = $request->get('code'); if (! $code) { return redirect() ->route('dashboard') ->with('error', 'No authorization code received from Microsoft.'); } $result = $this->kekaOutlook->handleCallback($request->user(), $code); if (! $result['success']) { return redirect() ->route('dashboard') ->with('error', 'Failed to connect: '.($result['message'] ?? 'Unknown error')); } return redirect()->away($this->kekaWebsiteUrl()); } /** * POST /keka/disconnect * * Wipes stored tokens. User must go through Microsoft login again next time. */ public function disconnect(Request $request): RedirectResponse { $this->kekaOutlook->disconnect($request->user()); return redirect() ->route('dashboard') ->with('success', 'Keka has been disconnected.'); } /** * The Keka website URL the user lands on after a successful (silent) connection. */ private function kekaWebsiteUrl(): string { return (string) config('microsoft.keka_website_url', 'https://app.keka.com'); } }