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

feature: 2FA #40

Merged
merged 20 commits into from
Jan 22, 2024
Merged
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
42 changes: 36 additions & 6 deletions src/Http/Controllers/Internal/v1/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
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;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
use Laravel\Sanctum\PersonalAccessToken;

class AuthController extends Controller
{
Expand All @@ -32,14 +34,43 @@ class AuthController extends Controller
*/
public function login(LoginRequest $request)
{
$email = $request->input('email');
$password = $request->input('password');
$user = User::where('email', $email)->first();
$identity = $request->input('identity');
$password = $request->input('password');
$authToken = $request->input('authToken');

// if attempting to authenticate with auth token validate it first against database and respond with it
if ($authToken) {
$personalAccessToken = PersonalAccessToken::findToken($authToken);

if ($personalAccessToken) {
return response()->json(['token' => $authToken]);
}
}

// Find the user using the identity provided
$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, ['code' => 'no_user']);
return response()->error('No user found by the provided identity.', 401, ['code' => 'no_user']);
}

// Check if 2FA enabled
if (TwoFactorAuth::isEnabled($user)) {
$twoFaSession = TwoFactorAuth::start($user);

return response()->json([
'twoFaSession' => $twoFaSession,
'isEnabled' => true,
]);
}

// Create token
$token = $user->createToken($user->uuid);

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

if (Auth::isInvalidPassword($password, $user->password)) {
return response()->error('Authentication failed using password provided.', 401, ['code' => 'invalid_password']);
}
Expand Down Expand Up @@ -418,8 +449,7 @@ public function signUp(SignUpRequest $request)
$companyDetails = $request->input('company');

$newUser = Auth::register($userDetails, $companyDetails);

$token = $newUser->createToken($request->ip());
$token = $newUser->createToken($newUser->uuid);

return response()->json(['token' => $token->plainTextToken]);
}
Expand Down
45 changes: 45 additions & 0 deletions src/Http/Controllers/Internal/v1/CompanyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
use Fleetbase\Http\Resources\Organization;
use Fleetbase\Models\Company;
use Fleetbase\Models\Invite;
use Fleetbase\Support\Auth;
use Fleetbase\Support\TwoFactorAuth;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class CompanyController extends FleetbaseController
Expand Down Expand Up @@ -39,4 +42,46 @@ public function findCompany(string $id)

return new Organization($company);
}

/**
* Get the current organization's two factor authentication settings.
*
* @return \Illuminate\Http\Response
*/
public function getTwoFactorSettings()
{
$company = Auth::getCompany();

if (!$company) {
return response()->error('No company session found', 401);
}

$twoFaSettings = TwoFactorAuth::getTwoFaSettingsForCompany($company);

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


/**
* Save the two factor authentication settings for the current company.
*
* @param \Illuminate\Http\Request $request The HTTP request.
*
* @return \Illuminate\Http\Response
*/
public function saveTwoFactorSettings(Request $request)
{
$twoFaSettings = $request->array('twoFaSettings');
$company = Auth::getCompany();

if (!$company) {
return response()->error('No company session found', 401);
}
if (isset($twoFaSettings['enabled']) && $twoFaSettings['enabled'] === false) {
$twoFaSettings['enforced'] = false;
}
TwoFactorAuth::saveTwoFaSettingsForCompany($company, $twoFaSettings);

return response()->json(['message' => 'Two-Factor Authentication saved successfully']);
}
}
166 changes: 166 additions & 0 deletions src/Http/Controllers/Internal/v1/TwoFaController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

namespace Fleetbase\Http\Controllers\Internal\v1;

use Fleetbase\Http\Controllers\Controller;
use Fleetbase\Http\Requests\TwoFaValidationRequest;
use Fleetbase\Models\Company;
use Fleetbase\Support\TwoFactorAuth;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

/**
* Class TwoFaController.
*/
class TwoFaController extends Controller
{
/**
* Save Two-Factor Authentication system wide settings.
*
* @return \Illuminate\Http\Response
*/
public function saveSystemConfig(Request $request)
{
$twoFaSettings = $request->array('twoFaSettings');
if (isset($twoFaSettings['enabled']) && $twoFaSettings['enabled'] === false) {
$twoFaSettings['enforced'] = false;
}
$settings = TwoFactorAuth::configureTwoFaSettings($twoFaSettings);

return response()->json($settings->value);
}

/**
* Get Two-Factor Authentication system wide settings.
*
* @return \Illuminate\Http\Response
*/
public function getSystemConfig()
{
$settings = TwoFactorAuth::getTwoFaConfiguration();

return response()->json($settings->value);
}

/**
* Check Two-Factor Authentication status for a given user identity.
*
* @return \Illuminate\Http\Response
*/
public function checkTwoFactor(Request $request)
{
$identity = $request->input('identity');
$twoFaSession = TwoFactorAuth::createTwoFaSessionIfEnabled($identity);
$isTwoFaEnabled = $twoFaSession !== null;

return response()->json([
'twoFaSession' => $twoFaSession,
'isTwoFaEnabled' => $isTwoFaEnabled,
]);
}

/**
* Verify Two-Factor Authentication code.
*
* @return \Illuminate\Http\Response
*/
public function validateSession(TwoFaValidationRequest $request)
{
$token = $request->input('token');
$identity = $request->input('identity');
$clientToken = $request->input('clientToken');

try {
$validClientToken = TwoFactorAuth::getClientSessionTokenFromTwoFaSession($token, $identity, $clientToken);

return response()->json([
'clientToken' => $validClientToken,
'expired' => false,
]);
} catch (\Exception $e) {
$errorMessage = $e->getMessage();

if (Str::contains($errorMessage, ['2FA Verification', 'expired'])) {
return response()->json([
'expired' => true,
]);
}

return response()->error($errorMessage);
}
}

/**
* Verify Two-Factor Authentication code.
*
* @return \Illuminate\Http\Response
*/
public function verifyCode(Request $request)
{
$code = $request->input('code');
$token = $request->input('token');
$clientToken = $request->input('clientToken');

try {
$authToken = TwoFactorAuth::verifyCode($code, $token, $clientToken);

return response()->json([
'authToken' => $authToken,
]);
} catch (\Exception $e) {
return response()->error($e->getMessage());
}
}

/**
* Resend Two-Factor Authentication verification code.
*
* @return \Illuminate\Http\Response
*/
public function resendCode(Request $request)
{
$identity = $request->input('identity');
$token = $request->input('token');

try {
$clientToken = TwoFactorAuth::resendCode($identity, $token);

return response()->json([
'clientToken' => $clientToken,
]);
} catch (\Exception $e) {
return response()->error($e->getMessage());
}
}

/**
* Invalidate the current two-factor session.
*
* @return \Illuminate\Http\Response
*/
public function invalidateSession(Request $request)
{
$identity = $request->input('identity');
$token = $request->input('token');

try {
$ok = TwoFactorAuth::forgetTwoFaSession($token, $identity);

return response()->json([
'ok' => $ok,
]);
} catch (\Exception $e) {
return response()->json(['ok' => false]);
}
}

public function shouldEnforce(Request $request)
{
$user = $request->user();
$enforceTwoFa = TwoFactorAuth::shouldEnforce($user);

return response()->json([
'shouldEnforce' => $enforceTwoFa,
]);
}
}
Loading