Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions app/Http/Controllers/Auth/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,21 +135,18 @@ protected function sendLoginResponse(Request $request)
$user->update(['last_ip' => $request->ip()]);

if (! $user->email_verified_at) {
$this->guard()->logout();

$request->session()->invalidate();
$request->session()->regenerateToken();
// Send verification code - user stays logged in
\App\Http\Controllers\EmailVerificationController::sendVerificationCode($user, $request);

if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'redirect' => url('/auth/verify-email'),
'message' => 'You need to verify your email.',
], 403);
'message' => 'Please verify your email to continue.',
'verification_sent' => true,
]);
}

return redirect()->route('login')->withErrors([
$this->username() => 'You need to verify your email.',
]);
return redirect('/auth/verify-email');
}

if ((int) $user->status === 6) {
Expand Down
117 changes: 86 additions & 31 deletions app/Http/Controllers/EmailVerificationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,54 @@

class EmailVerificationController extends Controller
{
/**
* Send verification code for a user (can be called from other controllers)
*
* Checks if there's already a pending (unverified) code for this user.
* If true, resends the same code (in case original was lost/spam).
* If false, creates a new code and sends the verification email.
*
* @return bool True if a new code was created, false if existing code was resent
*/
public static function sendVerificationCode(User $user, Request $request): bool
{
// Check for existing pending verification code
$existing = UserRegisterVerify::whereEmail($user->email)
->whereNull('verified_at')
->latest()
->first();

if ($existing) {
// Resend existing code (in case it was lost or caught in spam)
$request->session()->put('user:reg:session_key', $existing->session_key);
$request->session()->put('user:reg:email', $existing->email);
$request->session()->put('user:reg:id', $existing->id);

NewAccountEmailVerifyJob::dispatch($existing);

return false;
}

// Create new verification code and send email
$sessionKey = Str::random(32);
$reg = new UserRegisterVerify;
$reg->session_key = $sessionKey;
$reg->email = $user->email;
$reg->verify_code = (string) app(VerificationCode::class)->generate();
$reg->ip_address = $request->ip();
$reg->user_agent = $request->userAgent();
$reg->email_last_sent_at = now();
$reg->save();

$request->session()->put('user:reg:session_key', $sessionKey);
$request->session()->put('user:reg:email', $reg->email);
$request->session()->put('user:reg:id', $reg->id);

NewAccountEmailVerifyJob::dispatch($reg);

return true;
}

/**
* Initiate email verification - send code
*/
Expand Down Expand Up @@ -48,37 +96,7 @@ public function initiate(EmailManualVerificationRequest $request)
], 400);
}

$existing = UserRegisterVerify::whereEmail($user->email)
->whereNull('verified_at')
->latest()
->first();

if ($existing) {
$request->session()->put('user:reg:session_key', $existing->session_key);
$request->session()->put('user:reg:email', $existing->email);
$request->session()->put('user:reg:id', $existing->id);

return response()->json([
'message' => 'We have already sent a verification code to your email.',
'expires_in' => 900,
]);
}

$sessionKey = Str::random(32);
$reg = new UserRegisterVerify;
$reg->session_key = $sessionKey;
$reg->email = $user->email;
$reg->verify_code = (string) app(VerificationCode::class)->generate();
$reg->ip_address = $request->ip();
$reg->user_agent = $request->userAgent();
$reg->email_last_sent_at = now();
$reg->save();

$request->session()->put('user:reg:session_key', $sessionKey);
$request->session()->put('user:reg:email', $reg->email);
$request->session()->put('user:reg:id', $reg->id);

NewAccountEmailVerifyJob::dispatch($reg);
self::sendVerificationCode($user, $request);

return response()->json([
'message' => 'Verification code sent to your email.',
Expand Down Expand Up @@ -217,4 +235,41 @@ public function resend(Request $request)
'message' => 'New verification code sent to your email.',
]);
}

/**
* Check if there's a pending email verification session
*/
public function status(Request $request)
{
$sEmail = $request->session()->get('user:reg:email');
$sKey = $request->session()->get('user:reg:session_key');
$sId = $request->session()->get('user:reg:id');

if (! $sEmail || ! $sKey || ! $sId) {
return response()->json([
'has_pending_verification' => false,
]);
}

// Verify the session is still valid
$verify = UserRegisterVerify::where('session_key', $sKey)
->whereNull('verified_at')
->find($sId);

if (! $verify) {
return response()->json([
'has_pending_verification' => false,
]);
}

// Mask the email for privacy (show first 2 chars and domain)
$emailParts = explode('@', $sEmail);
$maskedEmail = substr($emailParts[0], 0, 2).'***@'.$emailParts[1];

return response()->json([
'has_pending_verification' => true,
'email' => $sEmail,
'masked_email' => $maskedEmail,
]);
}
}
2 changes: 2 additions & 0 deletions resources/js/components/AuthModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,8 @@ const handleLogin = async () => {
if (res.data.has_2fa) {
currentMode.value = 'two-factor'
setSuccess(t('common.pleaseEnterYour2FACode'))
} else if (res.data.verification_sent) {
window.location.href = '/auth/verify-email'
} else if (res.data.redirect) {
window.location.href = res.data.redirect
} else {
Expand Down
Loading