diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 3090538c3..c723d811a 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -98,7 +98,7 @@ public function forgot_password(Request $request) public function link() { $token = request()->get('token'); - if ($token) { + if (is_string($token) && $token !== '') { try { $decrypted = Crypt::decryptString($token); } catch (DecryptException) { @@ -126,9 +126,8 @@ public function link() $invitation = TeamInvitation::query() ->where('email', $email) ->when($invitationUuid, fn ($query) => $query->where('uuid', $invitationUuid)) - ->where('link', request()->fullUrl()) ->first(); - if (! $invitation || ! $invitation->isValid()) { + if (! $invitation || ! $this->invitationLinkMatchesToken($invitation, $token) || ! $invitation->isValid()) { return redirect()->route('login')->with('error', 'Invitation has expired or been revoked.'); } @@ -139,10 +138,11 @@ public function link() } $invitation->delete(); - Auth::login($user); $user->forceFill([ 'password' => Hash::make(Str::random(64)), ])->save(); + + Auth::login($user); session(['currentTeam' => $team]); return redirect()->route('dashboard'); @@ -152,6 +152,19 @@ public function link() return redirect()->route('login')->with('error', 'Invalid credentials.'); } + private function invitationLinkMatchesToken(TeamInvitation $invitation, string $token): bool + { + $query = parse_url($invitation->link, PHP_URL_QUERY); + if (! is_string($query)) { + return false; + } + + parse_str($query, $parameters); + $storedToken = $parameters['token'] ?? null; + + return is_string($storedToken) && hash_equals($storedToken, $token); + } + public function showInvitation() { $invitationUuid = request()->route('uuid'); diff --git a/app/Livewire/Team/InviteLink.php b/app/Livewire/Team/InviteLink.php index 5b040db71..a93bf8dd9 100644 --- a/app/Livewire/Team/InviteLink.php +++ b/app/Livewire/Team/InviteLink.php @@ -39,6 +39,16 @@ public function viaLink() $this->generateInviteLink(sendEmail: false); } + private function invitationUrl(string $routeName, array $parameters): string + { + $fqdn = instanceSettings()->fqdn; + if (filled($fqdn)) { + return rtrim($fqdn, '/').route($routeName, $parameters, false); + } + + return route($routeName, $parameters); + } + private function generateInviteLink(bool $sendEmail = false) { try { @@ -61,7 +71,7 @@ private function generateInviteLink(bool $sendEmail = false) return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of ".currentTeam()->name.'.'); } $uuid = new_public_id(32); - $link = url('/').config('constants.invitation.link.base_url').$uuid; + $link = $this->invitationUrl('team.invitation.show', ['uuid' => $uuid]); $user = User::whereEmail($this->email)->first(); if (is_null($user)) { @@ -73,7 +83,7 @@ private function generateInviteLink(bool $sendEmail = false) 'force_password_reset' => true, ]); $token = Crypt::encryptString("{$user->email}@@@{$uuid}@@@{$password}"); - $link = route('auth.link', ['token' => $token]); + $link = $this->invitationUrl('auth.link', ['token' => $token]); } $invitation = TeamInvitation::whereEmail($this->email)->first(); if (! is_null($invitation)) { diff --git a/config/constants.php b/config/constants.php index 15a6cc8f8..b9e3d600f 100644 --- a/config/constants.php +++ b/config/constants.php @@ -86,7 +86,6 @@ 'invitation' => [ 'link' => [ - 'base_url' => '/invitations/', 'expiration_days' => 3, ], ], diff --git a/resources/views/livewire/team/invite-link.blade.php b/resources/views/livewire/team/invite-link.blade.php index a6c2fb9ec..7960443fe 100644 --- a/resources/views/livewire/team/invite-link.blade.php +++ b/resources/views/livewire/team/invite-link.blade.php @@ -1,20 +1,22 @@ -@can('manageInvitations', currentTeam()) -