Files
projectsend/app/Modules/Identity/Http/Controllers/TwoFactorEnrollmentController.php
T
ignacionelson 6e47d76ba6 ProjectSend 2.0.0
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.
2026-08-14 01:38:12 -03:00

125 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Modules\Identity\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Modules\Audit\Action;
use App\Modules\Audit\ActivityLogger;
use App\Modules\Identity\TwoFactor\TwoFactorService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;
class TwoFactorEnrollmentController extends Controller
{
public function __construct(
private readonly TwoFactorService $twoFactor,
private readonly ActivityLogger $activity,
) {}
public function show(Request $request): Response
{
$user = $request->user();
assert($user !== null);
$pendingSecret = $user->two_factor_confirmed_at === null ? $user->two_factor_secret : null;
return Inertia::render('settings/two-factor', [
'enabled' => $user->hasTwoFactorEnabled(),
'pending' => $pendingSecret !== null,
'qr_code_svg' => $pendingSecret !== null ? $this->twoFactor->qrCodeSvg($user, $pendingSecret) : null,
'secret' => $pendingSecret,
'recovery_codes' => $request->session()->get('two_factor_recovery_codes'),
'enforced' => (bool) $request->session()->get('two_factor_enforced_notice', false),
]);
}
/**
* Start enrollment: generate a secret awaiting confirmation.
*/
public function store(Request $request): RedirectResponse
{
$user = $request->user();
assert($user !== null);
if (! $user->hasTwoFactorEnabled()) {
$user->forceFill([
'two_factor_secret' => $this->twoFactor->generateSecret(),
'two_factor_recovery_codes' => null,
'two_factor_confirmed_at' => null,
])->save();
}
return back();
}
/**
* Confirm enrollment with a code from the authenticator app.
*/
public function confirm(Request $request): RedirectResponse
{
$user = $request->user();
assert($user !== null);
$request->validate(['code' => ['required', 'string']]);
if ($user->hasTwoFactorEnabled() || $user->two_factor_secret === null) {
return back();
}
if (! $this->twoFactor->verify($user, (string) $request->string('code'))) {
throw ValidationException::withMessages([
'code' => __('The provided two-factor authentication code was invalid.'),
]);
}
$recoveryCodes = $this->twoFactor->generateRecoveryCodes();
$user->forceFill([
'two_factor_recovery_codes' => $recoveryCodes,
'two_factor_confirmed_at' => now(),
])->save();
$this->activity->log(Action::TwoFactorEnabled, $user);
return back()->with('two_factor_recovery_codes', $recoveryCodes);
}
/**
* Replace the recovery codes.
*/
public function regenerateRecoveryCodes(Request $request): RedirectResponse
{
$user = $request->user();
assert($user !== null);
if (! $user->hasTwoFactorEnabled()) {
return back();
}
$recoveryCodes = $this->twoFactor->generateRecoveryCodes();
$user->forceFill(['two_factor_recovery_codes' => $recoveryCodes])->save();
$this->activity->log(Action::TwoFactorRecoveryCodesRegenerated, $user);
return back()->with('two_factor_recovery_codes', $recoveryCodes);
}
public function destroy(Request $request): RedirectResponse
{
$user = $request->user();
assert($user !== null);
if ($this->twoFactor->clear($user)) {
$this->activity->log(Action::TwoFactorDisabled, $user);
}
return back();
}
}