Created
April 26, 2018 08:38
-
-
Save BitBy73Bit/347ea04946aaabcaaca721e3f83b3bdc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Http\Controllers\API; | |
| use App\User; | |
| use App\User2fa; | |
| use App\Helpers\Notification; | |
| use App\Helpers\AuthHelper; | |
| use App\Http\Controllers\Controller; | |
| use Illuminate\Http\Request; | |
| use Illuminate\Support\Facades\Log; | |
| use App\Helpers\UserAudit; | |
| use Twilio; | |
| class AuthController extends Controller | |
| { | |
| /** | |
| * Show login form | |
| */ | |
| public function showLogin(Request $request) { | |
| UserAudit::track("viewed"); | |
| // Redirect if already logged in | |
| if(strlen(AuthHelper::id() != 0) && $request->is('accounts/login')) { | |
| Log::debug('User already logged in, redirecting from login to dashboard'); | |
| return redirect('/'); | |
| } | |
| return view('templates/accounts/login'); | |
| } | |
| /** | |
| * Login | |
| * | |
| * @param Request | |
| * @return Response | |
| */ | |
| public function login(Request $request) | |
| { | |
| $data = $request->only('initials', 'dob'); | |
| Log::debug('Attempting to login', [$data]); | |
| // Check we have data | |
| if(sizeof($data) == 0) { | |
| Log::debug('Attempting to login but no data supplied'); | |
| abort(400, 'Bad data supplied'); | |
| } | |
| // Check credentials | |
| $user = $this->checkCredentials($data); | |
| // Send 2fa | |
| $this->send2faToken($user); | |
| Log::info('User logged in', ['id' => $user['id']]); | |
| UserAudit::track("auth_login_verify", "Login verify", $user['id']); | |
| // Return response body | |
| return response()->api([ | |
| 'initials' => $data['initials'], | |
| 'dob' => $data['dob'], | |
| '2fa_method' => $user['2fa_method'], | |
| ], 200); | |
| } | |
| /** | |
| * LoginAs | |
| * | |
| * @param Request | |
| * @return Response | |
| */ | |
| public function loginAs($id) | |
| { | |
| $user = User::find($id); | |
| // Ensure we are a DA and that we own the user | |
| if(!AuthHelper::isDA() || !AuthHelper::isTheOwner($user)) { | |
| abort(403, "You do not have permission to access this user"); | |
| } | |
| // Move DA token another cookie | |
| $daCookie = cookie('da_token', $_COOKIE['token'], 90000); | |
| // Generate new user token if one doesn't exist | |
| if(strlen($user['token']) == 0 || $user['token'] == NULL) { | |
| $userToken = bin2hex(random_bytes(24)); | |
| // Store token in DB | |
| $user->token = $userToken; | |
| $user->save(); | |
| } else { | |
| $userToken = $user['token']; | |
| } | |
| // Store token in cookie | |
| $cookie = cookie('token', $userToken, 90000); | |
| Log::info('DA simulating user', ['user_id' => $id]); | |
| UserAudit::track("auth_login", "DA simulating: ".$id); | |
| // Redirect | |
| return redirect('/')->withCookie($daCookie)->withCookie($cookie); | |
| } | |
| /** | |
| * Logout | |
| * | |
| * @param Request | |
| * @return Response | |
| */ | |
| public function logout(Request $request) | |
| { | |
| // Check to see if we have a da_token, if so then log the DA back in | |
| if(isset($_COOKIE['da_token']) && strlen($_COOKIE['da_token']) > 0) { | |
| $user = User::where('token', $_COOKIE['token'])->first(); | |
| Log::info('DA logged out of simulating a user', ['user_id' => $user['id']]); | |
| UserAudit::track("auth_logout", "DA logged out simulating: ".$user['id']); | |
| $cookie = cookie('token', $_COOKIE['da_token'], 90000); | |
| return redirect('accounts/login')->withCookie(\Cookie::forget('da_token'))->withCookie(\Cookie::forget('token'))->withCookie($cookie); | |
| // Otherwise logout normally | |
| } else { | |
| if(isset($_COOKIE['token']) && strlen($_COOKIE['token']) > 0) { | |
| // Find user based on token | |
| $authToken = $_COOKIE['token']; | |
| // Delete token from row | |
| $user = User::where('token', $authToken)->first(); | |
| if(sizeof($user) > 0) { | |
| $user->token = ""; | |
| $user->save(); | |
| } | |
| Log::info('User logged out', ['user_id' => $user['id']]); | |
| UserAudit::track("auth_logout", "Logout", $user['id']); | |
| // Return response body | |
| if(\Request::wantsJson() || \Request::ajax() || \Request::is("api/*")) { | |
| return response()->api([], 200)->withCookie(\Cookie::forget('da_token')); | |
| } | |
| return redirect('accounts/login')->withCookie(\Cookie::forget('da_token')); | |
| } | |
| } | |
| return redirect('accounts/login'); | |
| } | |
| /** | |
| * Verify 2fa code | |
| * | |
| * @param Request | |
| * @return Response | |
| */ | |
| public function verify(Request $request) | |
| { | |
| $data = $request->only('initials', 'dob', '2fa_method', '2fa_code'); | |
| Log::debug('Attempting to verify login', [$data]); | |
| // Check we have data | |
| if(sizeof($data) == 0) { | |
| Log::debug('Attempting to login but no data supplied'); | |
| abort(400, 'Bad data supplied'); | |
| } | |
| // Double check credentials are valid | |
| $user = $this->checkCredentials($data); | |
| // Check 2fa code | |
| $this->check2faToken($user, $data); | |
| // Generate token and store | |
| $authToken = bin2hex(random_bytes(24)); | |
| // Store token | |
| $user->token = $authToken; | |
| $user->save(); | |
| UserAudit::track("auth_login", "Login", $user['id']); | |
| // Return response body | |
| return response()->api([ | |
| 'token' => $authToken | |
| ], 200)->withCookie(cookie('token', $authToken, 90000)); | |
| } | |
| /** | |
| * Sends a test 2fa token | |
| */ | |
| public function test2fa(Request $request) { | |
| $data = $request->only('2fa_method', '2fa_telephone'); | |
| Log::debug('Attempting to test 2fa', [$data]); | |
| // Check we have data | |
| if(sizeof($data) == 0) { | |
| Log::debug('Attempting to test 2fa but no data supplied'); | |
| abort(400, 'Bad data supplied'); | |
| } | |
| // Send 2fa | |
| $this->send2faToken(array( | |
| 'id' => "TEST", | |
| '2fa_method' => $data['2fa_method'], | |
| '2fa_telephone' => $data['2fa_telephone'] | |
| ), true); | |
| // Return response body | |
| return response()->api([ | |
| 'success' => true, | |
| ], 200); | |
| } | |
| /** | |
| * Validate a set of given user credentials | |
| * | |
| * @param Array | |
| * @return User | |
| */ | |
| private function checkCredentials($data) { | |
| if($data != null && sizeof($data) > 0) { | |
| // Check if user exists in DB | |
| $user = User::where(['initials' => $data['initials'], 'dob' => $data['dob']])->select('id', 'email', 'is_active', 'type', '2fa_method', '2fa_telephone')->get(); | |
| // Check we only have one user returned, otherwise we need to raise exception | |
| if(sizeof($user) == 0) { | |
| Log::debug('No user found matching the supplied data', [$data]); | |
| abort(404, 'User not found'); | |
| } elseif(sizeof($user) != 1) { | |
| Log::critical('Something that should never happen has happened! Multiple users found', $user->toArray()); | |
| abort(404, 'User not found'); | |
| } | |
| // Should only have one user, so lets grab it. | |
| $user = $user[0]; | |
| // Check user is active | |
| if($user['is_active'] !== 1) { | |
| Log::debug('User tried to login but is not active', $user->toArray()); | |
| abort('403', 'User is not active. Permission denied'); | |
| } | |
| Log::debug('User initially verfieid as OK', $user->toArray()); | |
| } else { | |
| Log::debug('User data was not supplied'); | |
| abort(400, "No data supplied"); | |
| } | |
| return $user; | |
| } | |
| /** | |
| * Check 2fa token | |
| */ | |
| private function check2faToken($user, $data) { | |
| Log::debug('Checking 2fa token', [$data]); | |
| // Ensure code is a number | |
| $data['2fa_code'] = (int) $data['2fa_code']; | |
| // Check if token exists for the user | |
| $tokenValid = User2fa::where(['user_id' => $user['id'], 'method' => $data['2fa_method'], 'code' => $data['2fa_code']])->orderBy('created_at', 'DESC')->first(); | |
| if(sizeof($tokenValid) != 1) { | |
| Log::debug('Invalid 2fa token', [$data]); | |
| abort(403, "Invalid two-factor token"); | |
| } | |
| // Clean up all previous tokens | |
| $date = new \DateTime; | |
| $oneHour = $date->modify('-1 hour')->format('Y-m-d H:i:s'); | |
| User2fa::where('created_at', '<=', $oneHour)->delete(); | |
| Log::debug('Token is valid', [$data]); | |
| return true; | |
| } | |
| /** | |
| * Send 2fa token | |
| */ | |
| private function send2faToken($user, $test=false) { | |
| // Generate token | |
| $token = mt_rand(1000, 9999); | |
| $tokenForVoice = implode(', ',str_split($token)); | |
| Log::debug('Sending user 2FA code', ['user_id' => $user['id'], 'token' => $token]); | |
| // Find out which method (sms/voice) | |
| switch($user['2fa_method']) { | |
| case "sms": | |
| if($test) { | |
| Notification::sendSms($user['id'], $user['2fa_telephone'], "This is a test message to verify you can receive notifications."); | |
| } else { | |
| Notification::sendSms($user['id'], $user['2fa_telephone'], "This is your code to login. Please enter this on the login screen: ".$token); | |
| } | |
| break; | |
| case "phone": | |
| if($test) { | |
| Notification::sendVoiceCall($user['id'], $user['2fa_telephone'], "Hello, this is a call from the xxxx: This is a test phone call to verify you can receive notifications: Thank you:"); | |
| } else { | |
| Notification::sendVoiceCall($user['id'], $user['2fa_telephone'], "Hello, this is a call from the xxxx. Your log in code is: ".$tokenForVoice.":. The code again is: ".$tokenForVoice.". Please enter this code into the log in screen on your computer:"); | |
| } | |
| break; | |
| default: | |
| Log::critical('Something that should never happen has happened! No valid 2fa method was found', $user->toArray()); | |
| abort(404, "No two-factor authentication method found"); | |
| } | |
| if(!$test) { | |
| // Store token in DB | |
| $tokenRecord = new User2fa; | |
| $tokenRecord->code = $token; | |
| $tokenRecord->method = $user['2fa_method']; | |
| $tokenRecord->user_id = $user['id']; | |
| $tokenRecord->save(); | |
| Log::debug('2FA code sent successfully', ['user_id' => $user['id'], 'token' => $token]); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment