Skip to content

Instantly share code, notes, and snippets.

@BitBy73Bit
Created April 26, 2018 08:38
Show Gist options
  • Select an option

  • Save BitBy73Bit/347ea04946aaabcaaca721e3f83b3bdc to your computer and use it in GitHub Desktop.

Select an option

Save BitBy73Bit/347ea04946aaabcaaca721e3f83b3bdc to your computer and use it in GitHub Desktop.
<?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