diff --git a/app/Http/Controllers/Auth/SsoLoginController.php b/app/Http/Controllers/Auth/SsoLoginController.php
index 1b658ed..283d450 100644
--- a/app/Http/Controllers/Auth/SsoLoginController.php
+++ b/app/Http/Controllers/Auth/SsoLoginController.php
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\QrTeamMember;
use App\Models\User;
+use Illuminate\Http\Client\Response as HttpResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
@@ -26,6 +27,10 @@ class SsoLoginController extends Controller
return $this->safeRedirect($intended, route('qr.dashboard'));
}
+ if ($this->attemptSilentRefresh($request, $intended)) {
+ return $this->safeRedirect($intended, route('qr.dashboard'));
+ }
+
$verifier = Str::random(64);
$state = Str::random(40);
$request->session()->put('sso.verifier', $verifier);
@@ -44,6 +49,11 @@ class SsoLoginController extends Controller
'code_challenge_method' => 'S256',
];
+ $loginHint = (string) $request->session()->get('sso.login_hint', '');
+ if ($loginHint !== '') {
+ $query['login_hint'] = $loginHint;
+ }
+
if (! $request->boolean('interactive')) {
$query['prompt'] = 'none';
}
@@ -95,23 +105,14 @@ class SsoLoginController extends Controller
]);
}
- $claims = Http::withToken((string) $tokenRes->json('access_token'))->acceptJson()->get($issuer.'/oauth/userinfo');
- if ($claims->failed() || ! $claims->json('sub')) {
+ $user = $this->loginFromTokenResponse($request, $tokenRes);
+ if (! $user) {
return redirect()->route('sso.connect', [
'redirect' => $intended,
'interactive' => 1,
]);
}
- $user = User::updateOrCreate(
- ['public_id' => (string) $claims->json('sub')],
- [
- 'name' => $claims->json('name'),
- 'email' => $claims->json('email') ?: (string) $claims->json('sub').'@users.ladill.com',
- 'avatar_url' => $claims->json('picture'),
- ],
- );
-
QrTeamMember::linkPendingInvitesFor($user);
Auth::login($user, remember: true);
@@ -150,6 +151,68 @@ class SsoLoginController extends Controller
return response('', 204);
}
+ private function attemptSilentRefresh(Request $request, string $intended): bool
+ {
+ $refreshToken = (string) $request->session()->get('sso.refresh_token', '');
+ if ($refreshToken === '') {
+ return false;
+ }
+
+ $issuer = rtrim((string) config('services.ladill_sso.issuer'), '/');
+ $tokenRes = Http::asForm()->post($issuer.'/oauth/token', [
+ 'grant_type' => 'refresh_token',
+ 'refresh_token' => $refreshToken,
+ 'client_id' => (string) config('services.ladill_sso.client_id'),
+ 'client_secret' => (string) config('services.ladill_sso.client_secret'),
+ 'scope' => 'openid profile email',
+ ]);
+
+ if ($tokenRes->failed()) {
+ $request->session()->forget('sso.refresh_token');
+
+ return false;
+ }
+
+ $user = $this->loginFromTokenResponse($request, $tokenRes);
+
+ if (! $user) {
+ return false;
+ }
+
+ Auth::login($user, remember: true);
+ $request->session()->put('sso.intended', $intended);
+
+ return true;
+ }
+
+ private function loginFromTokenResponse(Request $request, HttpResponse $tokenRes): ?User
+ {
+ $refreshToken = (string) $tokenRes->json('refresh_token', '');
+ if ($refreshToken !== '') {
+ $request->session()->put('sso.refresh_token', $refreshToken);
+ }
+
+ $issuer = rtrim((string) config('services.ladill_sso.issuer'), '/');
+ $claims = Http::withToken((string) $tokenRes->json('access_token'))->acceptJson()->get($issuer.'/oauth/userinfo');
+ if ($claims->failed() || ! $claims->json('sub')) {
+ return null;
+ }
+
+ $email = (string) ($claims->json('email') ?: '');
+ if ($email !== '') {
+ $request->session()->put('sso.login_hint', $email);
+ }
+
+ return User::updateOrCreate(
+ ['public_id' => (string) $claims->json('sub')],
+ [
+ 'name' => $claims->json('name'),
+ 'email' => $email !== '' ? $email : (string) $claims->json('sub').'@users.ladill.com',
+ 'avatar_url' => $claims->json('picture'),
+ ],
+ );
+ }
+
private function shouldLogoutForMailbox(Request $request): bool
{
$mailbox = strtolower(trim((string) $request->query('mailbox', '')));
diff --git a/config/ladill_launcher.php b/config/ladill_launcher.php
index cac9c15..38c9c0f 100644
--- a/config/ladill_launcher.php
+++ b/config/ladill_launcher.php
@@ -30,7 +30,7 @@ return [
['name' => 'Domains', 'url' => 'https://domains.'.$root, 'icon' => 'domains.svg'],
['name' => 'Servers', 'url' => 'https://servers.'.$root, 'icon' => 'servers.svg'],
['name' => 'Hosting', 'url' => 'https://hosting.'.$root, 'icon' => 'hosting.svg'],
- ['name' => 'QR Plus', 'url' => 'https://qrplus.'.$root, 'icon' => 'qrplus.svg'],
- ['name' => 'Events', 'url' => 'https://events.'.$root, 'icon' => 'events.svg'],
+ ['name' => 'QR Plus', 'url' => 'https://qrplus.'.$root.'/sso/connect?redirect='.urlencode('https://qrplus.'.$root.'/dashboard'), 'icon' => 'qrplus.svg'],
+ ['name' => 'Events', 'url' => 'https://events.'.$root.'/sso/connect?redirect='.urlencode('https://events.'.$root.'/dashboard'), 'icon' => 'events.svg'],
],
];
diff --git a/resources/views/partials/sso-keepalive.blade.php b/resources/views/partials/sso-keepalive.blade.php
index ecd5d4a..bc14950 100644
--- a/resources/views/partials/sso-keepalive.blade.php
+++ b/resources/views/partials/sso-keepalive.blade.php
@@ -1,4 +1,13 @@
@if (auth()->check())
- {{-- Same-site iframe keeps the shared auth.ladill.com session warm while using this app. --}}
-
+ @php $authPing = 'https://'.config('app.auth_domain').'/sso/ping'; @endphp
+ {{-- Same-site pings keep the shared auth.ladill.com session warm while using this app. --}}
+
+
@endif