keka intregation
This commit is contained in:
parent
cb86f8e362
commit
5ebda68b68
@ -10,16 +10,19 @@
|
||||
|
||||
final class KekaOutlookController extends Controller
|
||||
{
|
||||
public function __construct(private KekaOutlookService $kekaOutlook) {}
|
||||
public function __construct(private readonly KekaOutlookService $kekaOutlook) {}
|
||||
|
||||
/**
|
||||
* GET /keka/connect
|
||||
*
|
||||
* If the user already has a stored refresh token, we don't show
|
||||
* Microsoft's login screen at all — just confirm/refresh silently
|
||||
* and send them straight back to Keka.
|
||||
* FIRST TIME:
|
||||
* → Your Microsoft OAuth (stores token in oauth_tokens)
|
||||
* → callback stores token → redirect to sentientgeeks.keka.com
|
||||
* (Keka's SSO kicks in automatically since Microsoft session exists)
|
||||
*
|
||||
* If not connected yet, redirect to Microsoft login (one-time).
|
||||
* ALREADY CONNECTED:
|
||||
* → Silent token refresh if near expiry
|
||||
* → Straight to sentientgeeks.keka.com
|
||||
*/
|
||||
public function connect(Request $request): RedirectResponse
|
||||
{
|
||||
@ -27,75 +30,64 @@ public function connect(Request $request): RedirectResponse
|
||||
|
||||
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.
|
||||
}
|
||||
} catch (\RuntimeException) {
|
||||
// Token fully expired/revoked → re-authenticate
|
||||
return redirect()->away($this->kekaOutlook->getAuthUrl());
|
||||
}
|
||||
|
||||
// Already connected and token valid → go to Keka
|
||||
return redirect()->away((string) config('microsoft.keka_dashboard_url'));
|
||||
}
|
||||
|
||||
// Not connected yet → your Microsoft OAuth first
|
||||
return redirect()->away($this->kekaOutlook->getAuthUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /keka/callback
|
||||
*
|
||||
* Microsoft redirects here after login with ?code=... or ?error=...
|
||||
* Microsoft posts auth code here → store token → redirect to Keka.
|
||||
* Keka's own SSO will auto-login since Microsoft session is now active.
|
||||
*/
|
||||
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')));
|
||||
return redirect()->route('dashboard')
|
||||
->with('error', 'Microsoft login 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.');
|
||||
return redirect()->route('dashboard')
|
||||
->with('error', 'Invalid session state. Please try connecting again.');
|
||||
}
|
||||
|
||||
$code = $request->get('code');
|
||||
|
||||
if (! $code) {
|
||||
return redirect()
|
||||
->route('dashboard')
|
||||
->with('error', 'No authorization code received from Microsoft.');
|
||||
return redirect()->route('dashboard')
|
||||
->with('error', 'No authorization code received.');
|
||||
}
|
||||
|
||||
$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()->route('dashboard')
|
||||
->with('error', 'Connection failed: ' . ($result['message'] ?? 'Unknown error'));
|
||||
}
|
||||
|
||||
return redirect()->away($this->kekaWebsiteUrl());
|
||||
// Token stored ✓ → Keka SSO will auto-login via active Microsoft session
|
||||
return redirect()->away((string) config('microsoft.keka_dashboard_url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
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');
|
||||
}
|
||||
}
|
||||
@ -4,13 +4,23 @@
|
||||
'client_id' => env('MICROSOFT_GRAPH_CLIENT_ID'),
|
||||
'client_secret' => env('MICROSOFT_GRAPH_CLIENT_SECRET'),
|
||||
'tenant_id' => env('MICROSOFT_GRAPH_TENANT_ID'),
|
||||
|
||||
// Your app's redirect URI for Keka OAuth flow
|
||||
'keka_redirect_uri' => env('KEKA_MS_REDIRECT_URI', 'http://localhost:8000/keka/callback'),
|
||||
'keka_scopes' => ['openid',
|
||||
|
||||
// After first-time login: Keka's own Microsoft SSO URL
|
||||
// After already connected: Keka dashboard
|
||||
'keka_login_url' => env('KEKA_LOGIN_URL', 'https://sentientgeeks.keka.com/'),
|
||||
'keka_dashboard_url'=> env('KEKA_DASHBOARD_URL', 'https://sentientgeeks.keka.com/'),
|
||||
|
||||
'keka_scopes' => [
|
||||
'openid',
|
||||
'profile',
|
||||
'email',
|
||||
'offline_access',
|
||||
'User.Read',
|
||||
'Mail.Read',
|
||||
'Mail.ReadWrite',
|
||||
'Mail.Send']
|
||||
'Mail.Send',
|
||||
],
|
||||
];
|
||||
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('keka_connected')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('keka_connected');
|
||||
});
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user