From fa299bcd7c16ea5c4ca48d3a8931ccb0835e0d76 Mon Sep 17 00:00:00 2001 From: imadhigp Date: Tue, 28 Jul 2026 11:56:20 +0530 Subject: [PATCH] set forget password backend --- .../auth/ForgotPasswordController.php | 119 +++- app/Mail/OtpEmail.php | 56 ++ app/Models/User.php | 1 + ...28_030733_add_email_otp_to_users_table.php | 28 + resources/views/forgotPassword.blade.php | 586 +++++++++++------- resources/views/mail/otp.blade.php | 83 +++ resources/views/signin.blade.php | 2 +- routes/web.php | 34 +- 8 files changed, 622 insertions(+), 287 deletions(-) create mode 100644 app/Mail/OtpEmail.php create mode 100644 database/migrations/2026_07_28_030733_add_email_otp_to_users_table.php create mode 100644 resources/views/mail/otp.blade.php diff --git a/app/Http/Controllers/auth/ForgotPasswordController.php b/app/Http/Controllers/auth/ForgotPasswordController.php index 747b02f..fade723 100644 --- a/app/Http/Controllers/auth/ForgotPasswordController.php +++ b/app/Http/Controllers/auth/ForgotPasswordController.php @@ -1,41 +1,112 @@ validate([ - 'email' => ['required', 'email'], + 'email' => 'required|email|exists:users,email' + ], [ + 'email.exists' => 'The provided email is not registered in our system.' ]); - // 2. Attempt to send the password reset link via Laravel Broker - $status = Password::sendResetLink( - $request->only('email') - ); + $email = $request->input('email'); + $user = User::where('email', $email)->first(); - // 3. Redirect back with status message or validation error - return $status === Password::RESET_LINK_SENT - ? back()->with('status', __($status)) - : back()->withErrors(['email' => __($status)]); + $otp = random_int(100000, 999999); + + Session::put('otp', $otp); + Session::put('email', $email); + + $user->email_otp = $otp; + $user->save(); + + Mail::to($email)->send(new OtpEmail($otp)); + + return response()->json([ + 'success' => true, + 'message' => 'OTP has been sent to your email.' + ]); + } + + + + public function verifyOtpEmail(Request $request) + { + $request->validate([ + 'otp' => 'required|numeric' + ]); + + $otp = $request->input('otp'); + $email = Session::get('email'); + + if (!$email) { + return response()->json([ + 'success' => false, + 'message' => 'Session expired. Please request a new OTP.' + ], 400); + } + + $user = User::where('email', $email) + ->where('email_otp', $otp) + ->first(); + + if ($user) { + Session::put('otp_verified', true); + + return response()->json([ + 'success' => true, + 'message' => 'OTP is correct.', + 'email' => $email + ]); + } + + return response()->json([ + 'success' => false, + 'message' => 'Invalid OTP code. Please try again.' + ], 422); + } + + public function updatePassword(Request $request) + { + $request->validate([ + 'email' => 'required|email|exists:users,email', + 'password_mo' => 'required|string|min:8|confirmed', + ]); + + $email = $request->input('email'); + + if (!Session::get('otp_verified') || Session::get('email') !== $email) { + return redirect()->back()->with('error', 'Unauthorized access or session expired.'); + } + + $user = User::where('email', $email)->first(); + + if ($user) { + $user->password = Hash::make($request->input('password_mo')); + $user->email_otp = null; + $user->save(); + + Session::forget(['otp', 'email', 'otp_verified']); + + return redirect('/signin')->with('success', 'Password updated successfully. Please login.'); + } + + return redirect()->back()->with('error', 'User not found.'); } } \ No newline at end of file diff --git a/app/Mail/OtpEmail.php b/app/Mail/OtpEmail.php new file mode 100644 index 0000000..e908faa --- /dev/null +++ b/app/Mail/OtpEmail.php @@ -0,0 +1,56 @@ +otp = $otp; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'Academy - Password Reset OTP', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + view: 'Mail.otp', + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index 414254f..eba8b77 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -24,6 +24,7 @@ class User extends Authenticatable 'email', 'phone', 'password', + 'email_otp', ]; /** diff --git a/database/migrations/2026_07_28_030733_add_email_otp_to_users_table.php b/database/migrations/2026_07_28_030733_add_email_otp_to_users_table.php new file mode 100644 index 0000000..958c33d --- /dev/null +++ b/database/migrations/2026_07_28_030733_add_email_otp_to_users_table.php @@ -0,0 +1,28 @@ +string('email_otp')->nullable()->after('password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('email_otp'); + }); + } +}; diff --git a/resources/views/forgotPassword.blade.php b/resources/views/forgotPassword.blade.php index ee30fe0..4545ea9 100644 --- a/resources/views/forgotPassword.blade.php +++ b/resources/views/forgotPassword.blade.php @@ -3,33 +3,32 @@ - {{ session('step') == 'otp' ? 'Verify OTP' : 'Forgot Password' }} - -
- - {{-- SUCCESS & ERROR ALERTS --}} - @if (session('status')) -
{{ session('status') }}
- @endif - @if ($errors->has('otp')) -
{{ $errors->first('otp') }}
- @endif - - {{-- SECTION 2: VERIFY OTP (Shows when session step is 'otp') --}} - @if (session('step') === 'otp') -
- - - -
- -

Enter OTP Code

-

We've sent a 6-digit verification code to
{{ session('email', old('email')) }}

- -
- @csrf - - - -
- - - - - - -
- - -
- -
- Didn't receive the code? -
- @csrf - - -
-
- - {{-- SECTION 1: FORGOT PASSWORD (Default view) --}} - @else -
- - - -
- -

Forgot Password?

-

No worries! Enter your email and we'll send you a link to reset your password.

- -
- @csrf -
- - - @error('email') -
{{ $message }}
- @enderror -
- - -
- @endif - - ← Back to Login +
+
+
- @if (session('step') === 'otp') - - @endif + function updateHiddenOtp() { + let fullOtp = ""; + inputs.forEach(input => fullOtp += input.value); + hiddenOtpInput.value = fullOtp; + } + }); + + \ No newline at end of file diff --git a/resources/views/mail/otp.blade.php b/resources/views/mail/otp.blade.php new file mode 100644 index 0000000..cbbc2fe --- /dev/null +++ b/resources/views/mail/otp.blade.php @@ -0,0 +1,83 @@ + + + + + + Password Reset OTP - Academy + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +

+ ACADEMY +

+
+

+ Reset Your Password +

+ +

+ Hello,
+ We received a request to reset the password for your Academy account. Use the Verification Code (OTP) below to proceed. +

+ + +
+ + {{ $otp }} + +
+ +

+ This OTP is valid for 10 minutes. If you did not request a password reset, please ignore this email. +

+
+
+
+

+ Regards,
+ Academy Learning Team +

+

+ © {{ date('Y') }} Academy. All rights reserved. +

+
+
+ + + \ No newline at end of file diff --git a/resources/views/signin.blade.php b/resources/views/signin.blade.php index 89e8707..bcc9953 100644 --- a/resources/views/signin.blade.php +++ b/resources/views/signin.blade.php @@ -151,7 +151,7 @@ } .btn-primary:hover, .btn-primary:focus { - background: var(--theme-red-hover) !important; + background: #822424 !important; box-shadow: 0 4px 12px rgba(130, 36, 36, 0.3); } diff --git a/routes/web.php b/routes/web.php index 2e0349a..2a3706f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -22,37 +22,23 @@ Route::get('/', function () { }); + -Route::get('forgot-password', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request'); -Route::post('forgot-password', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email'); +Route::middleware('guest')->group(function () { + Route::get('/password/reset', [ForgotPasswordController::class, 'index'])->name('password.request'); + Route::post('/resetEmail', [ForgotPasswordController::class, 'resetEmail'])->name('password.email'); -// Route::get('password/reset', [ForgotPasswordController::class, 'showLinkRequestForm']) -// ->name('password.request'); + Route::post('/verify-otp', [ForgotPasswordController::class, 'verifyOtpEmail'])->name('password.verify.otp'); -// Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail']) -// ->name('password.email'); + + Route::post('/update-password', [ForgotPasswordController::class, 'updatePassword'])->name('password.update'); +}); -// Route::get('password/reset/{token}', [ForgotPasswordController::class, 'showResetForm']) -// ->name('password.reset'); - -// Route::post('password/reset', [ForgotPasswordController::class, 'reset']) -// ->name('password.update'); + - -// Password Reset Routes -Route::get('password/reset', [ForgotPasswordController::class, 'showLinkRequestForm']) - ->name('password.request'); - -Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail']) - ->name('password.email'); - -Route::get('password/reset/{token}', [ForgotPasswordController::class, 'showResetForm']) - ->name('password.reset'); - -Route::post('password/reset', [ForgotPasswordController::class, 'reset']) - ->name('password.update'); + Route::post('/studentlogout', [studentportalnavController::class, 'logout'])->name('student.logout'); Route::get('/StudentProfile', [StudentPortalController::class, 'showProfile'])->name('StudentProfile');