Fix guest join flow redirecting to Care marketing page.
Deploy Ladill Meet / deploy (push) Successful in 52s
Deploy Ladill Meet / deploy (push) Successful in 52s
Guests were sent through silent SSO after entering the meeting room because /room routes required auth; failed SSO fell back to leftover Care config. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -88,7 +88,11 @@ class SsoLoginController extends Controller
|
||||
if ($request->filled('error')) {
|
||||
if (in_array($request->query('error'), ['login_required', 'interaction_required', 'consent_required'], true)
|
||||
&& ! $request->boolean('interactive')) {
|
||||
return redirect()->away((string) config('ladill.marketing_url'));
|
||||
return redirect()->route('sso.connect', [
|
||||
'redirect' => $intended,
|
||||
'interactive' => 1,
|
||||
'fallback' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->finishCallback($request, $intended, (string) $request->query('error_description', $request->query('error')));
|
||||
@@ -285,6 +289,10 @@ class SsoLoginController extends Controller
|
||||
|
||||
private function resolveLanding(IdentityTeamClient $identity, User $user, string $intended): string
|
||||
{
|
||||
if ($this->isMeetJoinUrl($intended)) {
|
||||
return $intended;
|
||||
}
|
||||
|
||||
try {
|
||||
return $identity->postAuthRedirect($user->ownerRef(), $intended);
|
||||
} catch (\Throwable) {
|
||||
@@ -292,6 +300,14 @@ class SsoLoginController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
private function isMeetJoinUrl(string $url): bool
|
||||
{
|
||||
$meetHost = config('app.meet_domain', 'meet.ladill.com');
|
||||
|
||||
return str_starts_with($url, 'https://'.$meetHost.'/r/')
|
||||
|| str_starts_with($url, 'http://'.$meetHost.'/r/');
|
||||
}
|
||||
|
||||
private function safeReturnUrl(string $url): string
|
||||
{
|
||||
$host = parse_url($url, PHP_URL_HOST);
|
||||
|
||||
@@ -68,6 +68,7 @@ class EnsurePlatformSession
|
||||
|
||||
return redirect()->route('sso.connect', [
|
||||
'redirect' => $request->fullUrl(),
|
||||
'interactive' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -54,9 +54,15 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
},
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
$middleware->redirectGuestsTo(fn (Request $request) => route('sso.connect', [
|
||||
'redirect' => $request->fullUrl(),
|
||||
]));
|
||||
$middleware->redirectGuestsTo(function (Request $request) {
|
||||
$params = ['redirect' => $request->fullUrl()];
|
||||
|
||||
if ($request->is('r/*', 'room/*')) {
|
||||
$params['interactive'] = 1;
|
||||
}
|
||||
|
||||
return route('sso.connect', $params);
|
||||
});
|
||||
$middleware->web(append: [
|
||||
SetActingAccount::class,
|
||||
]);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'product_slug' => 'care',
|
||||
'marketing_url' => env('LADILL_MARKETING_URL', 'https://ladill.com/products/care'),
|
||||
'product_slug' => 'meet',
|
||||
'marketing_url' => env('LADILL_MARKETING_URL', 'https://ladill.com'),
|
||||
];
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="max-w-md rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-sm">
|
||||
<h1 class="text-xl font-semibold text-slate-900">Unable to join</h1>
|
||||
<p class="mt-2 text-sm text-slate-600">{{ $reason ?? 'You do not have access to this meeting.' }}</p>
|
||||
<a href="{{ route('sso.connect', ['redirect' => route('meet.join', $room)]) }}" class="btn-primary mt-6 inline-block">Sign in</a>
|
||||
<a href="{{ route('sso.connect', ['redirect' => route('meet.join', $room), 'interactive' => 1]) }}" class="btn-primary mt-6 inline-block">Sign in</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+39
-39
@@ -27,7 +27,7 @@ use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', fn () => auth()->check()
|
||||
? redirect()->route('meet.dashboard')
|
||||
: redirect()->route('sso.connect'))->name('meet.root');
|
||||
: redirect()->route('sso.connect', ['interactive' => 1]))->name('meet.root');
|
||||
|
||||
Route::get('/login', [SsoLoginController::class, 'connect'])->name('login');
|
||||
Route::get('/sso/connect', [SsoLoginController::class, 'connect'])->name('sso.connect');
|
||||
@@ -48,6 +48,44 @@ Route::match(['get', 'post'], '/invite/{invitation}/rsvp', [InvitationController
|
||||
Route::get('/w/{room}/register', [WebinarRegistrationController::class, 'show'])->name('meet.webinar.register');
|
||||
Route::post('/w/{room}/register', [WebinarRegistrationController::class, 'store'])->name('meet.webinar.register.store');
|
||||
|
||||
Route::get('/room/{session}', [MeetingRoomController::class, 'show'])->name('meet.room');
|
||||
Route::post('/room/{session}/end', [MeetingRoomController::class, 'end'])->name('meet.room.end');
|
||||
Route::post('/room/{session}/leave', [MeetingRoomController::class, 'leave'])->name('meet.room.leave');
|
||||
Route::post('/room/{session}/raise-hand', [MeetingRoomController::class, 'raiseHand'])->name('meet.room.raise-hand');
|
||||
Route::post('/room/{session}/chat', [MeetingRoomController::class, 'sendMessage'])->name('meet.room.chat');
|
||||
Route::post('/room/{session}/reaction', [MeetingRoomController::class, 'sendReaction'])->name('meet.room.reaction');
|
||||
Route::get('/room/{session}/poll', [MeetingRoomController::class, 'poll'])->name('meet.room.poll');
|
||||
Route::post('/room/{session}/recording/start', [MeetingRoomController::class, 'startRecording'])->name('meet.room.recording.start');
|
||||
Route::post('/room/{session}/recording/stop', [MeetingRoomController::class, 'stopRecording'])->name('meet.room.recording.stop');
|
||||
Route::post('/room/{session}/lock', [MeetingRoomController::class, 'lock'])->name('meet.room.lock');
|
||||
Route::post('/room/{session}/unlock', [MeetingRoomController::class, 'unlock'])->name('meet.room.unlock');
|
||||
Route::post('/room/{session}/caption', [MeetingRoomController::class, 'caption'])->name('meet.room.caption');
|
||||
|
||||
Route::post('/room/{session}/qa', [MeetingFeaturesController::class, 'submitQuestion'])->name('meet.room.qa.submit');
|
||||
Route::post('/room/{session}/qa/{question}/approve', [MeetingFeaturesController::class, 'approveQuestion'])->name('meet.room.qa.approve');
|
||||
Route::post('/room/{session}/qa/{question}/answer', [MeetingFeaturesController::class, 'answerQuestion'])->name('meet.room.qa.answer');
|
||||
Route::post('/room/{session}/qa/{question}/upvote', [MeetingFeaturesController::class, 'upvoteQuestion'])->name('meet.room.qa.upvote');
|
||||
Route::post('/room/{session}/polls', [MeetingFeaturesController::class, 'createPoll'])->name('meet.room.polls.create');
|
||||
Route::post('/room/{session}/polls/{poll}/vote', [MeetingFeaturesController::class, 'votePoll'])->name('meet.room.polls.vote');
|
||||
Route::post('/room/{session}/polls/{poll}/close', [MeetingFeaturesController::class, 'closePoll'])->name('meet.room.polls.close');
|
||||
Route::post('/room/{session}/breakouts', [MeetingFeaturesController::class, 'createBreakouts'])->name('meet.room.breakouts.create');
|
||||
Route::post('/room/{session}/breakouts/{breakout}/join', [MeetingFeaturesController::class, 'moveToBreakout'])->name('meet.room.breakouts.join');
|
||||
Route::post('/room/{session}/breakouts/return', [MeetingFeaturesController::class, 'returnFromBreakout'])->name('meet.room.breakouts.return');
|
||||
Route::post('/room/{session}/breakouts/broadcast', [MeetingFeaturesController::class, 'broadcastBreakout'])->name('meet.room.breakouts.broadcast');
|
||||
Route::post('/room/{session}/breakouts/close', [MeetingFeaturesController::class, 'closeBreakouts'])->name('meet.room.breakouts.close');
|
||||
Route::post('/room/{session}/files', [MeetingFeaturesController::class, 'uploadFile'])->name('meet.room.files.upload');
|
||||
Route::get('/room/{session}/files/{file}', [MeetingFeaturesController::class, 'downloadFile'])->name('meet.room.files.download');
|
||||
Route::get('/room/{session}/whiteboard', [MeetingFeaturesController::class, 'showWhiteboard'])->name('meet.room.whiteboard.show');
|
||||
Route::post('/room/{session}/whiteboard', [MeetingFeaturesController::class, 'saveWhiteboard'])->name('meet.room.whiteboard.save');
|
||||
Route::post('/room/{session}/whiteboard/export', [MeetingFeaturesController::class, 'exportWhiteboard'])->name('meet.room.whiteboard.export');
|
||||
|
||||
Route::post('/room/{session}/live-stream', [MeetingFeaturesController::class, 'configureLiveStream'])->name('meet.room.livestream.configure');
|
||||
Route::post('/room/{session}/live-stream/start', [MeetingFeaturesController::class, 'startLiveStream'])->name('meet.room.livestream.start');
|
||||
Route::post('/room/{session}/live-stream/stop', [MeetingFeaturesController::class, 'stopLiveStream'])->name('meet.room.livestream.stop');
|
||||
Route::post('/room/{session}/town-hall/green-room', [MeetingFeaturesController::class, 'startGreenRoom'])->name('meet.room.townhall.green');
|
||||
Route::post('/room/{session}/town-hall/go-live', [MeetingFeaturesController::class, 'goLiveTownHall'])->name('meet.room.townhall.live');
|
||||
Route::post('/room/{session}/town-hall/handoff', [MeetingFeaturesController::class, 'handoffCoHost'])->name('meet.room.townhall.handoff');
|
||||
|
||||
Route::middleware(['auth', 'platform.session'])->group(function () {
|
||||
Route::get('/notifications', [NotificationController::class, 'index'])->name('notifications.index');
|
||||
Route::get('/notifications/unread', [NotificationController::class, 'unread'])->name('notifications.unread');
|
||||
@@ -131,44 +169,6 @@ Route::middleware(['auth', 'platform.session'])->group(function () {
|
||||
Route::get('/settings', [SettingsController::class, 'edit'])->name('meet.settings');
|
||||
Route::put('/settings', [SettingsController::class, 'update'])->name('meet.settings.update');
|
||||
|
||||
Route::get('/room/{session}', [MeetingRoomController::class, 'show'])->name('meet.room');
|
||||
Route::post('/room/{session}/end', [MeetingRoomController::class, 'end'])->name('meet.room.end');
|
||||
Route::post('/room/{session}/leave', [MeetingRoomController::class, 'leave'])->name('meet.room.leave');
|
||||
Route::post('/room/{session}/raise-hand', [MeetingRoomController::class, 'raiseHand'])->name('meet.room.raise-hand');
|
||||
Route::post('/room/{session}/chat', [MeetingRoomController::class, 'sendMessage'])->name('meet.room.chat');
|
||||
Route::post('/room/{session}/reaction', [MeetingRoomController::class, 'sendReaction'])->name('meet.room.reaction');
|
||||
Route::get('/room/{session}/poll', [MeetingRoomController::class, 'poll'])->name('meet.room.poll');
|
||||
Route::post('/room/{session}/recording/start', [MeetingRoomController::class, 'startRecording'])->name('meet.room.recording.start');
|
||||
Route::post('/room/{session}/recording/stop', [MeetingRoomController::class, 'stopRecording'])->name('meet.room.recording.stop');
|
||||
Route::post('/room/{session}/lock', [MeetingRoomController::class, 'lock'])->name('meet.room.lock');
|
||||
Route::post('/room/{session}/unlock', [MeetingRoomController::class, 'unlock'])->name('meet.room.unlock');
|
||||
Route::post('/room/{session}/caption', [MeetingRoomController::class, 'caption'])->name('meet.room.caption');
|
||||
|
||||
Route::post('/room/{session}/qa', [MeetingFeaturesController::class, 'submitQuestion'])->name('meet.room.qa.submit');
|
||||
Route::post('/room/{session}/qa/{question}/approve', [MeetingFeaturesController::class, 'approveQuestion'])->name('meet.room.qa.approve');
|
||||
Route::post('/room/{session}/qa/{question}/answer', [MeetingFeaturesController::class, 'answerQuestion'])->name('meet.room.qa.answer');
|
||||
Route::post('/room/{session}/qa/{question}/upvote', [MeetingFeaturesController::class, 'upvoteQuestion'])->name('meet.room.qa.upvote');
|
||||
Route::post('/room/{session}/polls', [MeetingFeaturesController::class, 'createPoll'])->name('meet.room.polls.create');
|
||||
Route::post('/room/{session}/polls/{poll}/vote', [MeetingFeaturesController::class, 'votePoll'])->name('meet.room.polls.vote');
|
||||
Route::post('/room/{session}/polls/{poll}/close', [MeetingFeaturesController::class, 'closePoll'])->name('meet.room.polls.close');
|
||||
Route::post('/room/{session}/breakouts', [MeetingFeaturesController::class, 'createBreakouts'])->name('meet.room.breakouts.create');
|
||||
Route::post('/room/{session}/breakouts/{breakout}/join', [MeetingFeaturesController::class, 'moveToBreakout'])->name('meet.room.breakouts.join');
|
||||
Route::post('/room/{session}/breakouts/return', [MeetingFeaturesController::class, 'returnFromBreakout'])->name('meet.room.breakouts.return');
|
||||
Route::post('/room/{session}/breakouts/broadcast', [MeetingFeaturesController::class, 'broadcastBreakout'])->name('meet.room.breakouts.broadcast');
|
||||
Route::post('/room/{session}/breakouts/close', [MeetingFeaturesController::class, 'closeBreakouts'])->name('meet.room.breakouts.close');
|
||||
Route::post('/room/{session}/files', [MeetingFeaturesController::class, 'uploadFile'])->name('meet.room.files.upload');
|
||||
Route::get('/room/{session}/files/{file}', [MeetingFeaturesController::class, 'downloadFile'])->name('meet.room.files.download');
|
||||
Route::get('/room/{session}/whiteboard', [MeetingFeaturesController::class, 'showWhiteboard'])->name('meet.room.whiteboard.show');
|
||||
Route::post('/room/{session}/whiteboard', [MeetingFeaturesController::class, 'saveWhiteboard'])->name('meet.room.whiteboard.save');
|
||||
Route::post('/room/{session}/whiteboard/export', [MeetingFeaturesController::class, 'exportWhiteboard'])->name('meet.room.whiteboard.export');
|
||||
|
||||
Route::post('/room/{session}/live-stream', [MeetingFeaturesController::class, 'configureLiveStream'])->name('meet.room.livestream.configure');
|
||||
Route::post('/room/{session}/live-stream/start', [MeetingFeaturesController::class, 'startLiveStream'])->name('meet.room.livestream.start');
|
||||
Route::post('/room/{session}/live-stream/stop', [MeetingFeaturesController::class, 'stopLiveStream'])->name('meet.room.livestream.stop');
|
||||
Route::post('/room/{session}/town-hall/green-room', [MeetingFeaturesController::class, 'startGreenRoom'])->name('meet.room.townhall.green');
|
||||
Route::post('/room/{session}/town-hall/go-live', [MeetingFeaturesController::class, 'goLiveTownHall'])->name('meet.room.townhall.live');
|
||||
Route::post('/room/{session}/town-hall/handoff', [MeetingFeaturesController::class, 'handoffCoHost'])->name('meet.room.townhall.handoff');
|
||||
|
||||
Route::get('/members', [MemberController::class, 'index'])->name('meet.members.index');
|
||||
Route::get('/members/create', [MemberController::class, 'create'])->name('meet.members.create');
|
||||
Route::post('/members', [MemberController::class, 'store'])->name('meet.members.store');
|
||||
|
||||
@@ -126,6 +126,32 @@ class MeetWebTest extends TestCase
|
||||
->assertSessionHasErrors('join');
|
||||
}
|
||||
|
||||
public function test_guest_can_join_live_meeting_and_enter_room(): void
|
||||
{
|
||||
$room = $this->createRoom([
|
||||
'settings' => array_merge(config('meet.default_settings'), [
|
||||
'join_before_host' => true,
|
||||
]),
|
||||
]);
|
||||
$session = Session::create([
|
||||
'owner_ref' => $this->user->public_id,
|
||||
'room_id' => $room->id,
|
||||
'media_room_name' => 'room-'.$room->uuid,
|
||||
'status' => 'live',
|
||||
'started_at' => now(),
|
||||
]);
|
||||
$room->update(['status' => 'live']);
|
||||
|
||||
$this->post('/r/'.$room->uuid.'/enter', [
|
||||
'display_name' => 'Guest User',
|
||||
])
|
||||
->assertRedirect(route('meet.room', $session));
|
||||
|
||||
$this->get('/room/'.$session->uuid)
|
||||
->assertOk()
|
||||
->assertSee('data-display-name="Guest User"', false);
|
||||
}
|
||||
|
||||
public function test_authenticated_user_can_join_live_meeting_without_display_name(): void
|
||||
{
|
||||
$room = $this->createRoom();
|
||||
|
||||
Reference in New Issue
Block a user