mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-22 19:43:24 +00:00
a45eae315c
password.confirm redirected every write to the confirm-password screen. A redirect cannot carry a POST body, and Redirector::guest() only remembers the exact URL of a GET, so after confirming, the user landed back on an empty form and the action never ran. On the API token forms that meant typing the name, the scopes and the expiry again. An Inertia request now gets a 423 marked X-Password-Confirmation. A dialog mounted around every page catches it, asks for the password over the current page, and sends the refused request again with the same data and callbacks, so the form finishes as if nothing happened. The check itself is still the framework's. Plain form posts and JSON clients are answered as before, and accounts with no local password are offered a way to set one, as the confirm screen does.
70 lines
2.5 KiB
PHP
70 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Auth;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Modules\Identity\AuthSource;
|
|
use App\Modules\Identity\PasswordVerification;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response as HttpResponse;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class ConfirmablePasswordController extends Controller
|
|
{
|
|
/**
|
|
* Show the confirm password page.
|
|
*/
|
|
public function show(Request $request): Response
|
|
{
|
|
$user = $request->user();
|
|
assert($user !== null);
|
|
|
|
return Inertia::render('auth/confirm-password', [
|
|
// An account provisioned by a provider has no password to
|
|
// confirm with — its stored hash is a generated string nobody
|
|
// has seen. The screen offers to set one instead of asking for
|
|
// it, which is the only way past this for those accounts, and
|
|
// this screen stands in front of two-factor enrolment.
|
|
'has_local_password' => $user->auth_source === AuthSource::Local,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Confirm the user's password.
|
|
*
|
|
* Through PasswordVerification, so this asks the same question the
|
|
* sign-in form asks: is this the account's password, from wherever
|
|
* that account's password lives. Checking only the local hash refused
|
|
* every directory-provisioned account the password it actually has --
|
|
* their local hash is a Str::password(64) nobody has ever seen -- and
|
|
* this screen stands in front of enrolling in two-factor, so those
|
|
* accounts could not enrol at all.
|
|
*
|
|
* Asked for JSON, it answers with a bare 204: that is the password
|
|
* dialog (RequirePasswordConfirmation), which stays on the page and
|
|
* sends the refused request again itself, so there is nowhere to go.
|
|
*/
|
|
public function store(Request $request, PasswordVerification $passwords): RedirectResponse|HttpResponse
|
|
{
|
|
$user = $request->user();
|
|
assert($user !== null);
|
|
|
|
if (! $passwords->verify($user, (string) $request->string('password'))) {
|
|
throw ValidationException::withMessages([
|
|
'password' => __('auth.password'),
|
|
]);
|
|
}
|
|
|
|
$request->session()->put('auth.password_confirmed_at', time());
|
|
|
|
if ($request->expectsJson()) {
|
|
return response()->noContent();
|
|
}
|
|
|
|
return redirect()->intended(route('dashboard', absolute: false));
|
|
}
|
|
}
|