Skip to content

Commit

Permalink
Created support class for 2FA
Browse files Browse the repository at this point in the history
  • Loading branch information
TemuulenBM committed Jan 3, 2024
1 parent a494bb6 commit ec8afef
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 121 deletions.
63 changes: 37 additions & 26 deletions src/Http/Controllers/Internal/v1/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Fleetbase\Models\VerificationCode;
use Fleetbase\Notifications\UserForgotPassword;
use Fleetbase\Support\Auth;
use Fleetbase\Support\TwoFactorAuth;
use Fleetbase\Support\Utils;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
Expand All @@ -30,48 +31,58 @@ class AuthController extends Controller
*
* @return \Illuminate\Http\Response
*/
// public function login(LoginRequest $request)
// {
// $ip = $request->ip();
// $identity = $request->input('identity');
// $password = $request->input('password');
// $user = User::where(function ($query) use ($identity) {
// $query->where('email', $identity)->orWhere('phone', $identity);
// })->first();
// if (!$user) {
// return response()->error('No user found by this phone number.', 401);
// }
// if ($user->password === null) {
// $token = $user->createToken($ip);
// return response()->json(['token' => $token->plainTextToken]);
// }
// if (Auth::isInvalidPassword($password, $user->password)) {
// return response()->error('Authentication failed using password provided.', 401);
// }
// $token = $user->createToken($ip);
// return response()->json(['token' => $token->plainTextToken]);
// }

public function login(LoginRequest $request)
{
$ip = $request->ip();
$email = $request->input('email');
$identity = $request->input('identity');
$password = $request->input('password');
$user = User::where('email', $email)->first();

$user = User::where(function ($query) use ($identity) {
$query->where('email', $identity)->orWhere('phone', $identity);
})->first();

if (!$user) {
return response()->error('No user found by this email.', 401);
return response()->error('No user found by this phone number.', 401);
}

$token = $user->createToken($ip);

// Check if 2FA enabled
if (TwoFactorAuth::isEnabled()) {
$twoFaSession = TwoFactorAuth::start();
return response()->json(['two_fa_session' => $twoFaSession]);
}

return response()->json(['token' => $token->plainTextToken]);

if (Auth::isInvalidPassword($password, $user->password)) {
return response()->error('Authentication failed using password provided.', 401);
}

$token = $user->createToken($ip);

return response()->json(['token' => $token->plainTextToken]);
}

// public function login(LoginRequest $request)
// {
// $ip = $request->ip();
// $email = $request->input('email');
// $password = $request->input('password');
// $user = User::where('email', $email)->first();

// if (!$user) {
// return response()->error('No user found by this email.', 401);
// }

// if (Auth::isInvalidPassword($password, $user->password)) {
// return response()->error('Authentication failed using password provided.', 401);
// }

// $token = $user->createToken($ip);

// return response()->json(['token' => $token->plainTextToken]);
// }

/**
* Takes a request username/ or email and password and attempts to authenticate user
* will return the user model if the authentication was successful, else will 400.
Expand Down
64 changes: 64 additions & 0 deletions src/Http/Controllers/Internal/v1/TwoFaController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Fleetbase\Http\Controllers\Internal\v1;

use Fleetbase\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Fleetbase\Support\TwoFactorAuth;

/**
* Class TwoFaController
*
* @package Fleetbase\Http\Controllers\Internal\v1
*/
class TwoFaController extends Controller
{
/**
* TwoFactorAuth instance.
*
* @var \Fleetbase\Support\TwoFactorAuth
*/
protected $twoFactorAuth;

/**
* TwoFaController constructor.
*
* @param \Fleetbase\Support\TwoFactorAuth $twoFactorAuth
*/
public function __construct(TwoFactorAuth $twoFactorAuth)
{
$this->twoFactorAuth = $twoFactorAuth;
}

/**
* Save Two-Factor Authentication settings.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function saveSettings(Request $request)
{
return TwoFactorAuth::saveSettings($request);
}

/**
* Get Two-Factor Authentication settings.
*
* @return \Illuminate\Http\JsonResponse
*/
public function getSettings()
{
return TwoFactorAuth::getSettings();
}

/**
* Verify Two-Factor Authentication code.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function verifyTwoFactor(Request $request)
{
return TwoFactorAuth::verifyTwoFactor($request);
}
}
91 changes: 0 additions & 91 deletions src/Http/Controllers/Internal/v1/TwoFaSettingController.php

This file was deleted.

141 changes: 141 additions & 0 deletions src/Support/TwoFactorAuth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

namespace Fleetbase\Support;

use Fleetbase\Models\VerificationCode;
use Aloha\Twilio\Support\Laravel\Facade as Twilio;
use Fleetbase\Models\Setting;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Validation\ValidationException;

/**
* Class TwoFactorAuth
*
* @package Fleetbase\Support
*/
class TwoFactorAuth
{
/**
* Save Two-Factor Authentication settings.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
* @throws \Exception
*/
public static function saveSettings($request)
{
$twoFaSettings = $request->input('twoFaSettings');
if (!is_array($twoFaSettings)) {
throw new \Exception('Invalid 2FA settings data.');
}
Setting::configure('2fa', $twoFaSettings);

return response()->json([
'status' => 'ok',
'message' => '2Fa settings successfully saved.',
]);
}

/**
* Get Two-Factor Authentication settings.
*
* @return \Illuminate\Http\JsonResponse
*/
public static function getSettings()
{
$twoFaSettings = Setting::lookup('2fa', ['enabled' => false, 'method' => 'authenticator_app']);

return response()->json($twoFaSettings);
}

/**
* Verify Two-Factor Authentication code.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public static function verifyTwoFactor($request)
{
if (!RateLimiter::attempt(self::throttleKey($request), self::throttleMaxAttempts(), self::throttleDecayMinutes())) {
throw ValidationException::withMessages([
'code' => ['Too many verification attempts. Please try again later.'],
])->status(429);
}

$user = auth()->user();
$codeToVerify = $request->input('code');

$latestCode = VerificationCode::where('subject_uuid', $user->uuid)
->where('subject_type', get_class($user))
->where('for', 'phone_verification')
->latest()
->first();

if (!$latestCode || $latestCode->code !== $codeToVerify || $latestCode->isExpired()) {
RateLimiter::hit(self::throttleKey($request));

return response()->json([
'status' => 'error',
'message' => 'Invalid or expired verification code.',
], 401);
}

self::sendVerificationSuccessSms($user);

return response()->json([
'status' => 'success',
'message' => 'Verification Successful',
]);
}

/**
* Get the throttle key based on the request's IP.
*
* @param \Illuminate\Http\Request $request
* @return string
*/
protected static function throttleKey($request)
{
return 'verify_two_factor_' . $request->ip();
}

/**
* Get the maximum number of attempts allowed in the throttle.
*
* @return int
*/
protected static function throttleMaxAttempts()
{
return 5;
}

/**
* Get the decay time in minutes for the throttle.
*
* @return int
*/
protected static function throttleDecayMinutes()
{
return 2;
}

/**
* Send a success SMS after successful verification.
*
* @param mixed $user
*/
private static function sendVerificationSuccessSms($user)
{
Twilio::message($user->phone, 'Your Fleetbase verification was successful. Welcome!');
}

public static function isEnabled()
{
return Setting::lookup('2fa', ['enabled']);
}

public static function start()
{
return true;
}
}
Loading

0 comments on commit ec8afef

Please sign in to comment.