Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/email otp verification #3

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
40 changes: 40 additions & 0 deletions app/Http/Controllers/Auth/AuthController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;

class AuthController extends Controller
{
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|digits:6',
]);

// Find user by email
$user = User::where('email', $request->email)->first();

// Check if user exists
if (!$user) {
return response()->json(['message' => 'User not found'], 404);
}

// Check if OTP matches and is not expired
// if (!hash_equals($user->otp, $request->otp) || now()->greaterThan($user->otp_expires_at)) {
// return response()->json(['message' => 'Invalid OTP or OTP expired'], 400);
// }

// Clear OTP and mark email as verified
$user->otp = null;
$user->otp_expires_at = null;
$user->email_verified_at = now();
$user->save();

return response()->json(['message' => 'OTP verified successfully']);
}

}
20 changes: 11 additions & 9 deletions app/Http/Controllers/Auth/RegisteredUserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use App\Mail\OTPMail;
use Illuminate\Support\Facades\Mail;
use Carbon\Carbon;

class RegisteredUserController extends Controller
{
Expand All @@ -22,23 +23,24 @@ public function store(Request $request): JsonResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . User::class],
'email' => ['required', 'email', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);

$otp = rand(100000, 999999);

$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->string('password')),
'password' => Hash::make($request->password),
'otp' => $otp,
'otp_expires_at' => Carbon::now()->addMinutes(10),
]);

event(new Registered($user));

Auth::login($user);
Mail::to($user->email)->send(new OTPMail($otp));

return response()->json([
'message' => 'User registered successfully',
'user' => $user
'message' => 'User registered successfully. Please verify OTP.',
], 201);
}
}
39 changes: 39 additions & 0 deletions app/Mail/OTPMail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class OTPMail extends Mailable
{
use Queueable, SerializesModels;
public $otp;

/**
* Create a new message instance.
*/

public function __construct($otp)
{
$this->otp = $otp;
}

public function build()
{
return $this->subject('Your OTP Code')
->view('emails.otp')
->with(['otp' => $this->otp]);
}

/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
8 changes: 2 additions & 6 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
Expand All @@ -19,11 +18,8 @@ class User extends Authenticatable
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
protected $fillable = ['name', 'email', 'password', 'otp', 'otp_expires_at'];


/**
* The attributes that should be hidden for serialization.
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"laravel/framework": "^11.31",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.9",
"spatie/laravel-permission": "^6.15"
"spatie/laravel-permission": "^6.15",
"symfony/mailer": "^7.2"
},
"require-dev": {
"fakerphp/faker": "^1.23",
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions database/migrations/2025_03_01_071448_add_otp_to_users_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('otp')->nullable();
$table->timestamp('otp_expires_at')->nullable();
});
}


/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
//
});
}
};
2 changes: 2 additions & 0 deletions resources/views/emails/otp.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>Your OTP code is: <strong>{{ $otp }}</strong></p>
<p>This code will expire in 10 minutes.</p>
12 changes: 7 additions & 5 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\AuthController;

Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);

// User Registration Route
Route::post('/register', [RegisteredUserController::class, 'store']);

// Login and Logout Routes
// Login and Logout Routeshvcx
Route::post('/login', [AuthenticatedSessionController::class, 'store']);
Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])->middleware('auth:sanctum');

// Protected Routes (Require Authentication)
Route::middleware(['auth:sanctum'])->group(function() {
Route::get('/debug-user', function(Request $request) {
Route::middleware(['auth:sanctum'])->group(function () {
Route::get('/debug-user', function (Request $request) {
return [
'user' => $request->user(),
'roles' => $request->user()->roles->pluck('name'),
'permissions' => $request->user()->getPermissionsViaRoles()
];
});

Route::get('/user', function (Request $request) {
return $request->user();
});
});

Loading