mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-17 09:05:08 +00:00
6e47d76ba6
Client file sharing, rebuilt from the ground up: a private area per client, resumable uploads, folders, groups and categories, sharing with expiry dates and download limits, comments, file versions, an activity log, a REST API, and sixteen languages. This repository begins here. ProjectSend 2 was developed privately, and that development history is not published — the previous generation remains available, with its own history, at projectsend/legacy. Free software under the GNU General Public License v2, or (at your option) any later version.
96 lines
2.9 KiB
PHP
96 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Identity\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\User;
|
|
use App\Modules\Identity\SignIn;
|
|
use App\Modules\Identity\TwoFactor\TwoFactorService;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
/**
|
|
* Second step of login for accounts with 2FA: the password was already
|
|
* verified, the pending user id waits in the session until a valid TOTP
|
|
* or recovery code arrives.
|
|
*/
|
|
class TwoFactorChallengeController extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly TwoFactorService $twoFactor,
|
|
) {}
|
|
|
|
public function create(Request $request): Response|RedirectResponse
|
|
{
|
|
if ($this->pendingUser($request) === null) {
|
|
return redirect()->route('login');
|
|
}
|
|
|
|
return Inertia::render('auth/two-factor-challenge');
|
|
}
|
|
|
|
public function store(Request $request): RedirectResponse
|
|
{
|
|
$user = $this->pendingUser($request);
|
|
|
|
if ($user === null) {
|
|
return redirect()->route('login');
|
|
}
|
|
|
|
$request->validate([
|
|
'code' => ['nullable', 'string'],
|
|
'recovery_code' => ['nullable', 'string'],
|
|
]);
|
|
|
|
$throttleKey = 'two-factor.challenge.'.$user->id;
|
|
|
|
if (RateLimiter::tooManyAttempts($throttleKey, 5)) {
|
|
throw ValidationException::withMessages([
|
|
'code' => __('auth.throttle', ['seconds' => (string) RateLimiter::availableIn($throttleKey)]),
|
|
]);
|
|
}
|
|
|
|
$valid = $request->filled('code')
|
|
? $this->twoFactor->verify($user, (string) $request->string('code'))
|
|
: ($request->filled('recovery_code')
|
|
&& $this->twoFactor->consumeRecoveryCode($user, trim((string) $request->string('recovery_code'))));
|
|
|
|
if (! $valid) {
|
|
RateLimiter::hit($throttleKey);
|
|
|
|
throw ValidationException::withMessages([
|
|
'code' => __('The provided two-factor authentication code was invalid.'),
|
|
]);
|
|
}
|
|
|
|
RateLimiter::clear($throttleKey);
|
|
|
|
Auth::login($user, (bool) $request->session()->pull(SignIn::TWO_FACTOR_REMEMBER, false));
|
|
|
|
$request->session()->forget(SignIn::TWO_FACTOR_ID);
|
|
$request->session()->regenerate();
|
|
|
|
return redirect()->intended(route('dashboard', absolute: false));
|
|
}
|
|
|
|
private function pendingUser(Request $request): ?User
|
|
{
|
|
$id = $request->session()->get(SignIn::TWO_FACTOR_ID);
|
|
|
|
if (! is_int($id) && ! is_string($id)) {
|
|
return null;
|
|
}
|
|
|
|
$user = User::query()->find($id);
|
|
|
|
return $user instanceof User && $user->active && $user->hasTwoFactorEnabled() ? $user : null;
|
|
}
|
|
}
|