One-Time Password (OTP) Verification

One-Time Password (OTP) Verification

🔐 2FA Flow (Recommended)

Registration

  1. User registers
  2. OTP is generated & sent to email
  3. User verifies OTP
  4. Account becomes verified

Login

  1. Email + password correct
  2. Generate OTP
  3. Send OTP to email
  4. Return requires_2fa = true
  5. Verify OTP → issue token

1️⃣ Database Changes

Add columns to users table

Schema::table('users', function (Blueprint $table) {
    $table->string('otp_code')->nullable();
    $table->timestamp('otp_expires_at')->nullable();
    $table->boolean('is_2fa_verified')->default(false);
});

2️⃣ Helper: Generate OTP

private function generateOtp()
{
    return rand(100000, 999999);
}

3️⃣ Update User Register (Send OTP)

public function user_register(Request $request)
{
    $validated = Validator::make($request->all(), [
        'email' => 'required|email|unique:users',
        'password' => 'required|min:6',
    ]);

    if ($validated->fails()) {
        return response([
            'message' => $validated->errors(),
            'status' => 'error',
        ], 422);
    }

    DB::beginTransaction();
    try {

        $otp = $this->generateOtp();

        $user = User::create([
            'full_name' => $request->input('full_name', ''),
            'user_type' => 1,
            'email' => $request->email,
            'password' => Hash::make($request->password),
            'otp_code' => $otp,
            'otp_expires_at' => now()->addMinutes(5),
        ]);

        // Send OTP via email (simple example)
        Mail::raw("Your OTP is: {$otp}", function ($message) use ($user) {
            $message->to($user->email)
                    ->subject('Verify Your Account');
        });

        DB::commit();

        return response([
            'message' => 'Registration successful. OTP sent to email.',
            'status' => 'success',
        ], 201);

    } catch (\Exception $e) {
        DB::rollback();
        return response([
            'message' => $e->getMessage(),
            'status' => 'error',
        ], 500);
    }
}

4️⃣ Update User Login (Trigger 2FA)

public function user_login(Request $request)
{
    $validated = Validator::make($request->all(), [
        'email' => 'required|email',
        'password' => 'required',
    ]);

    if ($validated->fails()) {
        return response([
            'message' => $validated->errors(),
            'status' => 'error',
        ], 422);
    }

    $user = User::where('email', $request->email)
        ->where('status', 1)
        ->first();

    if (!$user || !Hash::check($request->password, $user->password)) {
        return response([
            'message' => 'Invalid credentials',
            'status' => 'error',
        ], 401);
    }

    // Generate OTP
    $otp = $this->generateOtp();

    $user->update([
        'otp_code' => $otp,
        'otp_expires_at' => now()->addMinutes(5),
        'is_2fa_verified' => false,
    ]);

    // Send OTP
    Mail::raw("Your login OTP is: {$otp}", function ($message) use ($user) {
        $message->to($user->email)
                ->subject('Login Verification OTP');
    });

    return response([
        'message' => 'OTP sent to email',
        'requires_2fa' => true,
        'status' => 'success',
    ], 200);
}

5️⃣ OTP Verification API (FINAL LOGIN)

public function verifyOtp(Request $request)
{
    $request->validate([
        'email' => 'required|email',
        'otp' => 'required',
    ]);

    $user = User::where('email', $request->email)
        ->where('otp_code', $request->otp)
        ->where('otp_expires_at', '>=', now())
        ->first();

    if (!$user) {
        return response([
            'message' => 'Invalid or expired OTP',
            'status' => 'error',
        ], 401);
    }

    $user->update([
        'otp_code' => null,
        'otp_expires_at' => null,
        'is_2fa_verified' => true,
    ]);

    $token = $user->createToken($user->email)->plainTextToken;

    return response([
        'user' => $user,
        'token' => $token,
        'message' => 'Login successful',
        'status' => 'success',
    ], 200);
}

6️⃣ API Flow Summary

POST /register → OTP sent
POST /login → OTP sent
POST /verify-otp → Token issued