Files
3dealer/app/Http/Controllers/Auth/LoginController.php
T

130 lines
4.1 KiB
PHP

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Modules\Core\Auth\Exceptions\OtpThrottledException;
use Modules\Core\Auth\Services\UserOtpService;
/**
* Passwordless customer login: email → emailed 6-digit code → logged in.
* Login and registration are the same flow — UserOtpService::generateAndSend()
* find-or-creates the user (and its Customer).
*
* All the security lives in UserOtpService: per-email code-request throttling
* (OtpThrottledException), wrong-guess lockout, the Auth::login() itself (which
* also regenerates the session and merges the guest cart via Lunar's Login
* listener) and the session-registry record. This controller only moves the
* shopper between the two steps, carrying the email in the session rather
* than the URL.
*/
class LoginController extends Controller
{
/**
* `?redirect=/el/checkout` (e.g. from the checkout's login tab) becomes the
* intended URL that verify() returns to. Only a same-site path is accepted:
* no scheme, no protocol-relative `//host`, so it can't redirect off-site.
*/
public function create(string $locale, Request $request): View
{
$redirect = (string) $request->query('redirect', '');
if (preg_match('#^/(?![/\\\\])#', $redirect)) {
$request->session()->put('url.intended', url($redirect));
}
return view('auth.login');
}
public function send(string $locale, Request $request, UserOtpService $otp): RedirectResponse
{
$validated = $request->validate([
'email' => ['required', 'email', 'max:255'],
]);
$email = Str::lower(trim($validated['email']));
try {
$otp->generateAndSend($email);
} catch (OtpThrottledException) {
return back()->withInput()->withErrors([
'email' => __('storefront.auth.too_many_codes'),
]);
}
$request->session()->put('login.email', $email);
return redirect()->route('login.code');
}
public function code(string $locale, Request $request): View|RedirectResponse
{
$email = $request->session()->get('login.email');
if (! $email) {
return redirect()->route('login');
}
return view('auth.login-code', ['email' => $email]);
}
public function resend(string $locale, Request $request, UserOtpService $otp): RedirectResponse
{
$email = $request->session()->get('login.email');
if (! $email) {
return redirect()->route('login');
}
try {
$otp->generateAndSend($email);
} catch (OtpThrottledException) {
return back()->withErrors([
'code' => __('storefront.auth.too_many_codes'),
]);
}
return back()->with('status', __('storefront.auth.code_resent'));
}
public function verify(string $locale, Request $request, UserOtpService $otp): RedirectResponse
{
$email = $request->session()->get('login.email');
if (! $email) {
return redirect()->route('login');
}
$validated = $request->validate([
'code' => ['required', 'digits:6'],
]);
// Wrong, expired, or locked out after too many guesses — core doesn't
// say which, so neither do we; the page offers "resend code" for all three.
if (! $otp->validate($email, $validated['code'], $request)) {
return back()->withErrors([
'code' => __('storefront.auth.invalid_code'),
]);
}
$request->session()->forget('login.email');
return redirect()->intended(route('home'));
}
public function destroy(string $locale, Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('home');
}
}