├── .gitignore ├── src ├── Facades │ └── Laravauth.php ├── Contracts │ ├── TwoFactorSmsGateway.php │ └── TokenAuthenticator.php ├── resources │ └── views │ │ ├── mail │ │ └── email_token.blade.php │ │ ├── sms_response.blade.php │ │ ├── email_response.blade.php │ │ └── false_token.blade.php ├── Traits │ ├── UsesPassword.php │ ├── User.php │ └── AuthTokens.php ├── Auth │ ├── Gateways │ │ ├── Twilio.php │ │ ├── MessageBird.php │ │ └── Nexmo.php │ ├── EmailToken.php │ └── TwoFactorSms.php ├── helpers.php ├── Providers │ ├── TwilioProvider.php │ ├── MessageBirdProvider.php │ └── LaravauthServiceProvider.php ├── Mail │ └── EmailTokenSent.php ├── routes │ └── web.php ├── Http │ ├── Requests │ │ └── LaravauthLoginRequest.php │ └── Controllers │ │ └── LaravauthLoginController.php ├── migrations │ └── 2017_08_07_164617_create_laravauth_token_columns.php ├── Lib │ └── SmsRouter │ │ └── SmsRouter.php ├── Laravauth.php └── config │ └── laravauth.php ├── CHANGELOG.md ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /.idea 3 | composer.lock 4 | .DS_Store 5 | Thumbs.db -------------------------------------------------------------------------------- /src/Facades/Laravauth.php: -------------------------------------------------------------------------------- 1 | $url]) 7 | Login 8 | @endcomponent 9 | 10 | If the button is not clickable, copy and paste this in your browser. {{ $url }} 11 | 12 | Thanks,
13 | {{ config('app.name') }} 14 | @endcomponent 15 | -------------------------------------------------------------------------------- /src/Traits/UsesPassword.php: -------------------------------------------------------------------------------- 1 | request->{Laravauth::getPasswordID()}, 21 | $this->user->{Laravauth::getPasswordIDRelationship()}); 22 | } 23 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to `paschaldev/laravauth` are documented in this file. 4 | 5 | ## [1.0.2] - 25-08-2017 6 | 7 | - Updated routes loading in service provider. 8 | - Fixed a problem with using uppercase as variable values instead of lower case. 9 | - Disabled `soft_disable` option by default. 10 | - Removed redundant files. 11 | - Renamed files to match class name. 12 | 13 | ## [1.0.1] - 24-08-2017 14 | 15 | - Removed redundant files. 16 | 17 | ## [1.0.0] - 16-08-2017 18 | 19 | - First release 20 | -------------------------------------------------------------------------------- /src/Auth/Gateways/Twilio.php: -------------------------------------------------------------------------------- 1 | messages->create($to, 20 | ['from' => env('TWILIO_FROM'), 'body' => $message]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Auth/Gateways/MessageBird.php: -------------------------------------------------------------------------------- 1 | messages->create([ 19 | 'originator' => env('MESSAGEBIRD_FROM'), 20 | 'recipients' => [$to], 21 | 'body' => $message 22 | ]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | app->bind('twilio', function () { 26 | return new Client(env('TWILIO_SID'), env('TWILIO_TOKEN')); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Providers/MessageBirdProvider.php: -------------------------------------------------------------------------------- 1 | app->bind('messagebird', function () { 28 | return new Client(env('MESSAGEBIRD_KEY')); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Auth/Gateways/Nexmo.php: -------------------------------------------------------------------------------- 1 | send([ 22 | 23 | 'to' => $to, 24 | 'from' => env('NEXMO_FROM', config('app.name')), 25 | 'text' => $message 26 | ]); 27 | } 28 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paschaldev/laravauth", 3 | "description": "Laravel authentication with a twist.", 4 | "type": "library", 5 | "keywords": ["laravauth", "laravel", "2fa", "auth", "nexmo", "twilio", "authentication"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Ezeugwu Paschal", 10 | "email": "ezeugwupaschal@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "nexmo/laravel": "^1.1.1", 15 | "twilio/sdk": "^5.28", 16 | "messagebird/php-rest-api": "1.10.*" 17 | }, 18 | "require-dev": { 19 | "laravel/laravel": "dev-develop" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "PaschalDev\\Laravauth\\": "src/" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mail/EmailTokenSent.php: -------------------------------------------------------------------------------- 1 | url = Laravauth::getValidatorURI().$user->{config('laravauth.token_column_name')}; 25 | } 26 | 27 | /** 28 | * Build the message. 29 | * 30 | * @return $this 31 | */ 32 | public function build() 33 | { 34 | return $this->markdown('laravauth::mail.email_token') 35 | ->subject( str_replace('%appname%', config('app.name'), config('laravauth.email_token.mail_subject')) ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/routes/web.php: -------------------------------------------------------------------------------- 1 | ['web']], function () { 5 | 6 | $login_route = config('laravauth.login_route'); 7 | $validator_route = config('laravauth.validator_route'); 8 | 9 | Route::post($login_route, 10 | 'PaschalDev\Laravauth\Http\Controllers\LaravauthLoginController@login')->middleware('guest'); 11 | Route::match(['GET', 'POST'], $validator_route, 12 | 'PaschalDev\Laravauth\Http\Controllers\LaravauthLoginController@authenticate')->middleware('guest'); 13 | 14 | Route::get($login_route . '/email', 15 | 'PaschalDev\Laravauth\Http\Controllers\LaravauthLoginController@emailResponse')->middleware('guest')->name('laravauth_email_response'); 16 | 17 | Route::get($login_route . '/sms', 18 | 'PaschalDev\Laravauth\Http\Controllers\LaravauthLoginController@smsResponse')->middleware('guest')->name('laravauth_sms_response'); 19 | }); 20 | } -------------------------------------------------------------------------------- /src/Http/Requests/LaravauthLoginRequest.php: -------------------------------------------------------------------------------- 1 | 'required|string|email' 33 | ]; 34 | } 35 | 36 | return [ 37 | Laravauth::getLoginID() => 'required', 38 | Laravauth::getPasswordID() => 'required|string' 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ezeugwu Paschal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/migrations/2017_08_07_164617_create_laravauth_token_columns.php: -------------------------------------------------------------------------------- 1 | longText( config('laravauth.token_column_name') )->nullable(); 20 | $table->string( config('laravauth.token_type_column_name') , 20)->nullable(); 21 | $table->timestamp( config('laravauth.token_column_name').'_created_at')->nullable(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::table( Laravauth::getUserModelTableName(), function(Blueprint $table){ 33 | 34 | $table->dropColumn([ 35 | config('laravauth.token_column_name'), 36 | config('laravauth.token_type_column_name'), 37 | config('laravauth.token_column_name').'_created_at' 38 | ]); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Traits/User.php: -------------------------------------------------------------------------------- 1 | user = Laravauth::getSingleUserModel( 27 | $request->{Laravauth::getLoginID()}); 28 | } 29 | 30 | /** 31 | * Gets the user property on this object 32 | * 33 | * @return \Illuminate\Database\Eloquent\Model 34 | */ 35 | function getUser() 36 | { 37 | return $this->user; 38 | } 39 | 40 | /** 41 | * Get the failed login response instance. 42 | * 43 | * @param \Illuminate\Http\Request $request 44 | * @return \Illuminate\Http\RedirectResponse 45 | */ 46 | function userLoginFailed() 47 | { 48 | $errors = [Laravauth::getLoginID() => trans('auth.failed')]; 49 | 50 | if ($this->request->expectsJson()) { 51 | return response()->json($errors, 422); 52 | } 53 | 54 | return redirect()->back() 55 | ->withInput($this->request->only(Laravauth::getLoginID(), 'remember')) 56 | ->withErrors($errors); 57 | } 58 | } -------------------------------------------------------------------------------- /src/Contracts/TokenAuthenticator.php: -------------------------------------------------------------------------------- 1 | login(); 22 | } 23 | 24 | /** 25 | * Validates an authentication request, if successful, logs 26 | * the user in. 27 | * 28 | * @return mixed 29 | */ 30 | public function authenticate(Request $request) 31 | { 32 | 33 | return Laravauth::getAuthMethodInstance($request)->auth(); 34 | } 35 | 36 | /** 37 | * Returns the view after email has been provided when using 38 | * the email_token auth method. 39 | * 40 | * This method rejects & aborts a request if the URL is 41 | * accessed directly. 42 | * 43 | * @return void 44 | */ 45 | public function emailResponse(Request $request) 46 | { 47 | 48 | abort_unless($request->session()->has('laravauth_var'), 404); 49 | 50 | return view('laravauth::email_response'); 51 | } 52 | 53 | /** 54 | * Returns the view after login when using the two_factor_sms 55 | * auth method. 56 | * 57 | * This method rejects & aborts a request if the URL is 58 | * accessed directly. 59 | * 60 | * @return void 61 | */ 62 | public function smsResponse(Request $request) 63 | { 64 | 65 | abort_unless($request->session()->has('laravauth_var'), 404); 66 | 67 | $request->session()->reflash(); 68 | 69 | return view('laravauth::sms_response'); 70 | } 71 | } -------------------------------------------------------------------------------- /src/resources/views/sms_response.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 |
7 |
8 |
{{ __('Two-Factor Authentication') }}
9 | 10 |
11 |
12 | @csrf 13 | 14 |
15 | 16 | 17 |
18 | 19 | 20 | @if ($errors->has(laravauth_token_var_name())) 21 | 22 | {{ $errors->first(laravauth_token_var_name()) }} 23 | 24 | @endif 25 |
26 |
27 |
28 |
29 | 32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 | @endsection 42 | -------------------------------------------------------------------------------- /src/Lib/SmsRouter/SmsRouter.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class SmsRouter 11 | { 12 | 13 | /** 14 | * The Phone number the sms is to be sent to. 15 | * 16 | * @var string 17 | */ 18 | private $to; 19 | 20 | /** 21 | * The text message to be sent. 22 | * 23 | * @var string 24 | */ 25 | private $message; 26 | 27 | 28 | public function __construct($to, $message) 29 | { 30 | $this->setTo($to); 31 | $this->setMessage($message); 32 | } 33 | 34 | /** 35 | * Sets the $to property on this class 36 | * 37 | * @param string 38 | * @return void 39 | */ 40 | public function setTo($to) 41 | { 42 | 43 | $this->to = $to; 44 | } 45 | 46 | /** 47 | * Returns the $to property 48 | * 49 | * @return string 50 | */ 51 | public function getTo() 52 | { 53 | 54 | return $this->to; 55 | } 56 | 57 | /** 58 | * Sets the message property. 59 | * 60 | * @param string 61 | * @return void 62 | */ 63 | public function setMessage($message) 64 | { 65 | 66 | $this->message = $message; 67 | } 68 | 69 | /** 70 | * Returns the message 71 | * 72 | * @return string 73 | */ 74 | public function getMessage() 75 | { 76 | 77 | return $this->message; 78 | } 79 | 80 | /** 81 | * Send SMS to user after choosing the appropriate gateway. 82 | * 83 | * @return mixed 84 | */ 85 | public function send() 86 | { 87 | 88 | return $this->gateway()->send($this->getTo(), $this->getMessage()); 89 | } 90 | 91 | /** 92 | * Fetches the appropriate gateway to use. 93 | * 94 | * @return PaschalDev\Laravauth\Contracts\TwoFactorSmsGateway 95 | */ 96 | public function gateway() 97 | { 98 | 99 | $gateway = laravauth_class_namespace('Auth\Gateways', config('laravauth.two_factor_sms.gateway')); 100 | 101 | return new $gateway; 102 | } 103 | } -------------------------------------------------------------------------------- /src/resources/views/email_response.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name') }} | Login Ok 9 | 10 | 11 | 12 | 13 | 14 | 65 | 66 | 67 |
68 |
69 |
70 | Login OK! 71 |
72 |

Check your email for login link.

73 | 74 | 77 |
78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /src/resources/views/false_token.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name') }} | False token 9 | 10 | 11 | 12 | 13 | 14 | 65 | 66 | 67 |
68 |
69 |
70 | False Token 71 |
72 |

The token is either invalid or expired.

73 | 74 | 77 |
78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /src/Traits/AuthTokens.php: -------------------------------------------------------------------------------- 1 | request = $request; 20 | $this->token = $this->request->{laravauth_token_var_name()}; 21 | $this->setUser( $this->request ); 22 | } 23 | 24 | /** 25 | * Adds a token to the user model DB 26 | * 27 | * @return mixed 28 | */ 29 | public function addTokenToUser(){ 30 | 31 | $this->user->{config('laravauth.token_column_name')} = $this->generateToken(); 32 | $this->user->{config('laravauth.token_type_column_name')} = config('laravauth.auth_method'); 33 | $this->user->{config('laravauth.token_column_name').'_created_at'} = Carbon::now(); 34 | 35 | return $this->user->save(); 36 | } 37 | 38 | /** 39 | * Remove token from user 40 | * 41 | * @return mixed 42 | */ 43 | public function removeTokenFromUser($user){ 44 | 45 | $user->{config('laravauth.token_column_name')} = null; 46 | $user->{config('laravauth.token_type_column_name')} = null; 47 | $user->{config('laravauth.token_column_name').'_created_at'} = null; 48 | 49 | return $user->save(); 50 | } 51 | 52 | /** 53 | * Determines if the user token is still active for use. 54 | * 55 | * @return bool 56 | */ 57 | public function userTokenIsOK($user, $token) 58 | { 59 | $tokenCreated = Carbon::parse( $user->{config('laravauth.token_column_name').'_created_at'} ); 60 | 61 | return $user->{config('laravauth.token_column_name')} == $token 62 | && Carbon::now()->diffInSeconds( $tokenCreated ) < config('laravauth.'.config('laravauth.auth_method').'.lifetime'); 63 | } 64 | 65 | /** 66 | * The main Login logic. Handles incoming token request, 67 | * validates the token and logs the user in. 68 | * 69 | * @return Response 70 | */ 71 | public function auth() 72 | { 73 | //Fetch user model from token 74 | $user = $this->getUserToAuth(); 75 | 76 | // If the $user variable is null or the token is 77 | // inavlid \ expired. Show the false_token page. 78 | if( ! $user || ! $this->userTokenIsOK( $user, $this->token ) ) 79 | { 80 | return method_exists($this, 'failedAttempt') ? 81 | $this->failedAttempt() : response()->view('laravauth::false_token'); 82 | } 83 | 84 | // All good. $user variable is not null and the token is valid 85 | // Proceed to log in the user. 86 | Auth::login($user); 87 | 88 | // Clear the tokens 89 | $this->removeTokenFromUser( $user ); 90 | 91 | return redirect( config('laravauth.auth_redirect') ); 92 | } 93 | } -------------------------------------------------------------------------------- /src/Auth/EmailToken.php: -------------------------------------------------------------------------------- 1 | user)) { 44 | return $this->userLoginFailed(); 45 | } 46 | 47 | // Login passed at this point. i.e User is present in 48 | // database. 49 | // Add the user token to the database. 50 | $this->addTokenToUser(); 51 | 52 | // Send the user email containing login link. 53 | $this->tokenMessenger(); 54 | 55 | // Data for responses 56 | $data = [ 57 | 'success' => true, 58 | 'laravauth_var' => route('laravauth_email_response') 59 | ]; 60 | 61 | // If the request was expecting a JSON, probably 62 | // XMLHttpRequest (Ajax). 63 | if ($this->request->expectsJson()) { 64 | return response()->json($data, 200); 65 | } 66 | 67 | return redirect()->route('laravauth_email_response')->with($data); 68 | } 69 | 70 | /** 71 | * Fetches the user to be authenticated using the token. 72 | * 73 | * @return Illuminate\Database\Eloquent\Model 74 | */ 75 | public function getUserToAuth() 76 | { 77 | 78 | return Laravauth::getUserModel() 79 | ->where(config('laravauth.token_column_name'), $this->token) 80 | ->where(config('laravauth.token_type_column_name'), config('laravauth.auth_method')) 81 | ->first(); 82 | } 83 | 84 | /** 85 | * Generates a secure token 86 | * 87 | * Check http://php.net/manual/en/function.hash-hmac.php 88 | * for more info about the hash_mac PHP function. 89 | * 90 | * @return string 91 | */ 92 | public function generateToken() 93 | { 94 | return hash_hmac(config('laravauth.email_token.algorithm'), 95 | str_random(config('laravauth.email_token.length')), config('app.key')); 96 | } 97 | 98 | /** 99 | * The token messenger sends the generated token and login link 100 | * to the user's email. Uses Laravel Queue system. 101 | * 102 | * @return Response 103 | */ 104 | public function tokenMessenger() 105 | { 106 | return Mail::to($this->user)->queue(new EmailTokenSent($this->user)); 107 | } 108 | } -------------------------------------------------------------------------------- /src/Laravauth.php: -------------------------------------------------------------------------------- 1 | getAuthMethodClass()); 79 | 80 | return new $authMethodInstance($request); 81 | } 82 | 83 | /** 84 | * Gets the User model used by the application. 85 | * 86 | * @return string 87 | */ 88 | public function getUserModel() 89 | { 90 | $userModel = config('laravauth.user_model'); 91 | 92 | if (!class_exists($userModel)) { 93 | throw new \Exception("The 'user_model' value you specified in the config file does not exist.", 1); 94 | } 95 | 96 | return new $userModel; 97 | } 98 | 99 | /** 100 | * Gets single User model from the provided where clause 101 | * and value. 102 | * 103 | * @param string $value 104 | * @return string 105 | */ 106 | public function getSingleUserModel($value) 107 | { 108 | return $this->getUserModel()->where($this->getLoginIDRelationship(), $value)->first(); 109 | } 110 | 111 | /** 112 | * Gets the table name used by the model 113 | * 114 | * @return string 115 | */ 116 | public function getUserModelTableName() 117 | { 118 | return $this->getUserModel()->getTable(); 119 | } 120 | 121 | /** 122 | * Gets the validator URI for Token based auth 123 | * 124 | * @return string 125 | */ 126 | public function getValidatorURI() 127 | { 128 | 129 | return config('app.url') . '/' . config('laravauth.validator_route') . '?' . laravauth_token_var_name() . '='; 130 | } 131 | } -------------------------------------------------------------------------------- /src/Providers/LaravauthServiceProvider.php: -------------------------------------------------------------------------------- 1 | laravauthRoutes(); 19 | $this->laravauthPublishes(); 20 | $this->laravauthConfig(); 21 | $this->laravauthViews(); 22 | $this->laravauthMigrations(); 23 | $this->laravauthHelpers(); 24 | } 25 | 26 | /** 27 | * Register the application services. 28 | * 29 | * @return void 30 | */ 31 | public function register() 32 | { 33 | AliasLoader::getInstance()->alias('Laravauth', 34 | 'PaschalDev\Laravauth\Facades\Laravauth'); 35 | 36 | $this->app->bind('laravauth', function () { 37 | return new Laravauth; 38 | }); 39 | 40 | $this->registerNexmo(); 41 | $this->registerTwilio(); 42 | $this->registerMessageBird(); 43 | } 44 | 45 | /** 46 | * Items to be published 47 | * 48 | * @return void 49 | */ 50 | protected function laravauthPublishes() 51 | { 52 | 53 | $this->publishes([ 54 | __DIR__ . '/../config/laravauth.php' => config_path('laravauth.php'), 55 | __DIR__ . '/../resources/views' => resource_path('views/vendor/laravauth'), 56 | ]); 57 | } 58 | 59 | /** 60 | * Marge the package's configuration. 61 | * 62 | * @return void 63 | */ 64 | protected function laravauthConfig() 65 | { 66 | 67 | $this->mergeConfigFrom(__DIR__ . '/../config/laravauth.php', 68 | 'laravauth'); 69 | } 70 | 71 | /** 72 | * Include routes. 73 | * 74 | * @return void 75 | */ 76 | protected function laravauthRoutes() 77 | { 78 | 79 | // This loads the routes when the app is booted thereby 80 | // overriding the initial login routes 81 | $this->app->booted(function () { 82 | 83 | $this->loadRoutesFrom(__DIR__ . '/../routes/web.php'); 84 | }); 85 | } 86 | 87 | /** 88 | * Load views. 89 | * 90 | * @return void 91 | */ 92 | protected function laravauthViews() 93 | { 94 | 95 | $this->loadViewsFrom(__DIR__ . '/../resources/views', 96 | 'laravauth'); 97 | } 98 | 99 | /** 100 | * Load migrations. 101 | * 102 | * @return void 103 | */ 104 | protected function laravauthMigrations() 105 | { 106 | 107 | $this->loadMigrationsFrom(__DIR__ . '/../migrations'); 108 | } 109 | 110 | /** 111 | * Register helpers file 112 | * 113 | * @return void 114 | */ 115 | protected function laravauthHelpers() 116 | { 117 | require_once __DIR__ . '/../helpers.php'; 118 | } 119 | 120 | /** 121 | * Register Nexmo (SMS Gateway) 122 | * 123 | * @return void 124 | */ 125 | public function registerNexmo() 126 | { 127 | 128 | $this->app->register( 129 | 'Nexmo\Laravel\NexmoServiceProvider' 130 | ); 131 | } 132 | 133 | /** 134 | * Register Twilio (SMS Gateway) 135 | * 136 | * @return void 137 | */ 138 | public function registerTwilio() 139 | { 140 | 141 | $this->app->register( 142 | 'PaschalDev\Laravauth\Providers\TwilioProvider' 143 | ); 144 | } 145 | 146 | /** 147 | * Register MessageBird (SMS Gateway) 148 | * 149 | * @return void 150 | */ 151 | public function registerMessageBird() 152 | { 153 | $this->app->register( 154 | 'PaschalDev\Laravauth\Providers\MessageBirdProvider' 155 | ); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Auth/TwoFactorSms.php: -------------------------------------------------------------------------------- 1 | user) || !$this->passwordMatch()) { 46 | return $this->userLoginFailed(); 47 | } 48 | 49 | // Login passed at this point. i.e User is present in 50 | // database ans credentials match. 51 | // Add the user token to the database. 52 | $this->addTokenToUser(); 53 | 54 | // Send the user SMS containing SLT (Short Lived Token). 55 | $this->tokenMessenger(); 56 | 57 | // Data for responses 58 | $data = [ 59 | 'success' => true, 60 | 'laravauth_var' => route('laravauth_sms_response'), 61 | 'user_id' => $this->user->getKey() 62 | ]; 63 | 64 | // Add the user id to the session. 65 | $this->request->session()->flash('user_id', $this->user->getKey()); 66 | 67 | // If the request was expecting a JSON, probably 68 | // XMLHttpRequest (Ajax). 69 | if ($this->request->expectsJson()) { 70 | return response()->json($data, 200); 71 | } 72 | 73 | return redirect()->route('laravauth_sms_response')->with($data); 74 | } 75 | 76 | /** 77 | * Fetches the user to be authenticated using the token. 78 | * Uses a slightly different approach to email_token method. 79 | * 80 | * @return Illuminate\Database\Eloquent\Model 81 | */ 82 | public function getUserToAuth() 83 | { 84 | 85 | $this->request->session()->regenerate(); 86 | 87 | return Laravauth::getUserModel()->find($this->request->session()->get('user_id')); 88 | } 89 | 90 | /** 91 | * Generates a random token 92 | * 93 | * @return string 94 | */ 95 | public function generateToken() 96 | { 97 | return str_random(config('laravauth.two_factor_sms.length')); 98 | } 99 | 100 | /** 101 | * The token messenger sends the generated token and login link 102 | * to the user's phone. 103 | * 104 | * @return Response 105 | */ 106 | public function tokenMessenger() 107 | { 108 | $to = $this->user->laravauthPhone(); 109 | $message = str_replace('%validity%', intval(config('laravauth.two_factor_sms.lifetime') / 60), 110 | config('laravauth.two_factor_sms.text_prefix')) . $this->user->{config('laravauth.token_column_name')}; 111 | 112 | $messenger = new SmsRouter($to, $message); 113 | return $messenger->send(); 114 | } 115 | 116 | /** 117 | * Custom handler for a failed auth attempt 118 | * 119 | * @return Response 120 | */ 121 | public function failedAttempt() 122 | { 123 | 124 | $this->request->session()->reflash(); 125 | 126 | return redirect()->route('laravauth_sms_response'); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/config/laravauth.php: -------------------------------------------------------------------------------- 1 | 'login', 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Laravauth Validator Route 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This is the login route used by your application. It is usually a post 24 | | request with the forwarded data to this route that handles the login. 25 | | 26 | | @required String 27 | */ 28 | 29 | 'validator_route' => 'validate', 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Login Unique Identifier 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Depending on how your application is structured, every user should have a 37 | | unique ID for authentication. This value can be username, email, phone or 38 | | whatsoever and should match the input name passed in the login form view. 39 | | 40 | | This is basically the name of the input from the the login view form. 41 | | 42 | | @required string 43 | */ 44 | 45 | 'login_id' => 'email', 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Login Identifier Relationship 50 | |-------------------------------------------------------------------------- 51 | | 52 | | Database column name relating the Login Identifier from the view and 53 | | the Relationship in the User database. Default is email. 54 | | 55 | | @required string 56 | */ 57 | 58 | 'login_id_rel' => 'email', 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | Login Unique Identifier 63 | |-------------------------------------------------------------------------- 64 | | 65 | | This is the name of the input tag that will contain password from the the 66 | | login view form. 67 | | 68 | | PS: If you are using auth_methods that require passwords e.g 69 | | two_factor_sms make sure this value corresponds. 70 | | 71 | | @required string 72 | */ 73 | 74 | 'password_id' => 'password', 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | Password Relationship 79 | |-------------------------------------------------------------------------- 80 | | 81 | | Database column name relating the user password from the view and 82 | | the Relationship in the User database. Default is password. 83 | | 84 | | PS: If you are using auth_methods that require passwords e.g 85 | | two_factor_sms make sure this value corresponds. 86 | | 87 | | @required string 88 | */ 89 | 90 | 'password_rel' => 'password', 91 | 92 | /* 93 | |-------------------------------------------------------------------------- 94 | | User Model 95 | |-------------------------------------------------------------------------- 96 | | 97 | | The User model of your application. This model is used to retrieve the 98 | | unique user information from the login unique identifier provided. 99 | | 100 | | @required String 101 | */ 102 | 103 | 'user_model' => 'App\User', 104 | 105 | /* 106 | |-------------------------------------------------------------------------- 107 | | Authentication Methods 108 | |-------------------------------------------------------------------------- 109 | | 110 | | The authentication method to use. 111 | | 112 | | Possible Values: 'email_token', 'two_factor_sms' 113 | | 114 | | @required string 115 | */ 116 | 117 | 'auth_method' => 'email_token', 118 | 119 | /* 120 | |-------------------------------------------------------------------------- 121 | | Token Variable 122 | |-------------------------------------------------------------------------- 123 | | 124 | | The name of the token variable used in view forms and URL generator. 125 | | 126 | | @required string 127 | */ 128 | 129 | 'token_var' => 'token', 130 | 131 | /* 132 | |-------------------------------------------------------------------------- 133 | | Soft Disable 134 | |-------------------------------------------------------------------------- 135 | | 136 | | It is easy to swicth the availability of this package by simply toggling 137 | | this value. The default value is false. If set to true, none 138 | | of the routes are executed thereby soft disabling the package. 139 | | 140 | | @optional bool 141 | */ 142 | 143 | 'soft_disable' => false, 144 | 145 | /* 146 | |-------------------------------------------------------------------------- 147 | | DATABASE: Auth Plus Token Column 148 | |-------------------------------------------------------------------------- 149 | | 150 | | The name of the column to be created and used for storing tokens for auth 151 | | methods that require tokens on the users table. 152 | | 153 | | @required string 154 | */ 155 | 156 | 'token_column_name' => 'laravauth_token', 157 | 158 | /* 159 | |-------------------------------------------------------------------------- 160 | | DATABASE: Auth Plus Token Type column name 161 | |-------------------------------------------------------------------------- 162 | | 163 | | The name of the column to be created and used for storing token types for 164 | | auth methods that require tokens on the users table. 165 | | 166 | | @required string 167 | */ 168 | 169 | 'token_type_column_name' => 'laravauth_token_type', 170 | 171 | /* 172 | |-------------------------------------------------------------------------- 173 | | Email Token Auth Method specific configuration 174 | |-------------------------------------------------------------------------- 175 | | 176 | | Specific confguarion for email_token auth method. 177 | | 178 | | @required array 179 | */ 180 | 181 | 'email_token' => [ 182 | 183 | //Token lifetime in seconds 184 | 'lifetime' => 600, 185 | 186 | //Length of the random string to be hashed 187 | 'length' => 50, 188 | 189 | //See full list of options here http://php.net/manual/en/function.hash-algos.php 190 | 'algorithm' => 'sha256', 191 | 192 | //Subject of the mail 193 | 'mail_subject' => 'Login to %appname%' 194 | ], 195 | 196 | /* 197 | |-------------------------------------------------------------------------- 198 | | Two Factor (SMS) Auth Method specific configuration 199 | |-------------------------------------------------------------------------- 200 | | 201 | | Specific confguarion for two_factor_sms auth method. 202 | | 203 | | @required array 204 | */ 205 | 206 | 'two_factor_sms' => [ 207 | 208 | //Token lifetime in seconds 209 | 'lifetime' => 600, 210 | 211 | //Length of the token 212 | 'length' => 10, 213 | 214 | //Text to append before the token code 215 | 'text_prefix' => 'Your one time password (OTP) valid for %validity%min is: ', 216 | 217 | //Support for 3 gateways / sms providers. 218 | //Possible values: nexmo, twilio, messagebird 219 | 'gateway' => 'nexmo' 220 | ], 221 | 222 | /* 223 | |-------------------------------------------------------------------------- 224 | | Auth Redirect 225 | |-------------------------------------------------------------------------- 226 | | 227 | | After a successful authentication, the user will be redirected to the 228 | | value provided below. This is the equivalent to $redirectTo property in 229 | | the default LoginController. 230 | | 231 | | @required string 232 | */ 233 | 234 | 'auth_redirect' => '/home', 235 | ]; 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravauth 2 | 3 | > Laravel authentication with a twist. 4 | 5 | **Laravauth** is an authentication package for laravel that uses a different technique other than the traditional authentication methods. 6 | 7 | **REQUIREMENTS: Laravel >= 5.4** 8 | 9 | ## Synopsis 10 | 11 | This package works by hooking. How? Simply hooks to the `login` route, intercepts it and continues work from there. 12 | 13 | The first step to hooking is from your route, the default route is a post request to `login`, once there is a request to that route, the plugin comes alive. The following modes of authorization are available: 14 | 15 | 1. Email Token 16 | 2. Two Factor Authorization (SMS) 17 | 18 | ##### Email Token 19 | 20 | This type of authorization does not require passwords. The user simply provides his email address (must have been a registered user), if the email is present in the database, a temporary login link containing a secure token is sent to the user's email address. This login link is only valid for a specific amount of time, the default is 10minutes after which the link becomes invalid and the user will have to make a new login request. 21 | 22 | ##### Two Factor Authorization (SMS) 23 | 24 | Two Factor Authorization is an extra layer of security that ensures the user has another "thing they have" to couple with the "thing they know". The "thing they know" is usually their password, the "thing they have" for this case is a personal mobile number a token is sent to. [Read more](https://www.google.com.ng/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&cad=rja&uact=8&ved=0ahUKEwiZpdqFy8nVAhXCL1AKHR9HAboQFghMMAM&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FMulti-factor_authentication&usg=AFQjCNG5ZrSt445-9PIhTFVh7goI4j6Jbw "Wikipedia - Multi-factor authorization") 25 | 26 | This mode of authorization requires password and a phone number for the user. After the user provides his login credentials, default is `email` and `password`, **Laravauth** serves a page requesting a token, a short lived token valid for a specific amount of time (default is 10minutes) is sent to the user's phone. The user provides the token, if it's valid, the user is authenticated. 27 | 28 | 29 | ## Installation 30 | 31 | The installation process of this package is a breeze. The first step is to require with composer. 32 | 33 | ``` 34 | $ composer require paschaldev/laravauth 35 | ``` 36 | 37 | Open `app.php` in your config directory of your laravel installation and add this line in the `providers` array. 38 | 39 | ``` 40 | PaschalDev\Laravauth\Providers\LaravauthServiceProvider::class, 41 | ``` 42 | 43 | You will need to publish the configuration and view files. 44 | 45 | ``` 46 | $ php artisan vendor:publish --provider="PaschalDev\Laravauth\Providers\LaravauthServiceProvider" 47 | ``` 48 | 49 | **Laravauth** alters the user's table and adds extra columns it uses for authentication. Next step is to migrate the database. 50 | 51 | > Make sure your database has been setup and working fine before proceeding. 52 | 53 | The default configuration assumes the user's model is `App\Users`, if this is not so in your application, please skip this step, update your laravauth configuration to reflect this change before running the command below. 54 | 55 | Now run: 56 | 57 | ``` 58 | $ php artisan migrate 59 | ``` 60 | 61 | If all is good, migration is successful. That's all there is to do. Installation complete. 62 | 63 | ## Usage 64 | 65 | **Laravauth** does not ship with a `login` view, you can use your present `login` view and just make slight changes. 66 | 67 | Depending on the mode you choose, there are different ways to make things work. The configuration file has everything you'll need to tweak the package so that it plugs in perfectly to your application. 68 | 69 | **Laravauth** requires little or no alteration of your previous application code, everything works seamlessly from start to finish. You don't need to bend your codes at all, all you need to is make sure to key in the right configuration values in your **laravauth** config file `laravauth.php` in the config directory after you must have published. 70 | 71 | #### Email Token 72 | 73 | This is the default mode. All that is required in this mode is an input in the login view that contains the user's email. Like below: 74 | 75 | ``` 76 | 77 | ``` 78 | 79 | **Laravauth** uses `email` as the default. If your login view has a name that is not `email`, say for example: 80 | 81 | ``` 82 | 83 | ``` 84 | 85 | You'll need to update your **Laravauth** configuration file to match this, open the config file `laravauth.php` and change the config `login_id` to `user_email` and you're good to go. 86 | 87 | **Laravauth** also assumes the email column on the user's table is named `email`, if it is anything other than that, change the config `login_id_rel` to match the name of the email column on the user's table. 88 | 89 | P.S: Very important, make sure your app `url` in laravel's `app.php` config file is set to the correct value else the package might generate invalid links. 90 | 91 | #### Two Factor Authorization (SMS) 92 | 93 | This mode of authorization requires an SMS provider. Several providers are shipped with this package: 94 | 95 | 1. [Nexmo](http://nexmo.com) 96 | 2. [Twilio](http://twilio.com) 97 | 3. [MessageBird](http://messagebird.com) 98 | 99 | You can choose your preferred provider by changing the value in the `laravauth` config file. Look for the option `two_factor_sms`, its an array that contains specific configuration for the two factor sms mode. Inside the array is a `gateway` option you can toggle. Possible values are `nexmo` and `twilio`. 100 | 101 | Each of these providers have their own specific requirements. You are required to register with any provider of your choice. 102 | 103 | PS: Using this option, you know you should have a large amount of balance to be able to accommodate the frequent logins. 104 | 105 | ##### Nexmo 106 | 107 | Nexmo requires the following to be added and set in your `.env` file: 108 | 109 | ``` 110 | NEXMO_KEY=xxxxxx 111 | NEXMO_SECRET=xxxxxx 112 | NEXMO_FROM=xxxxxx 113 | ``` 114 | 115 | You can get your Nexmo key and secret from your dashboard after creating account. 116 | 117 | ##### Twilio 118 | 119 | Twilio requires the following to be added in your `.env` file: 120 | 121 | ``` 122 | TWILIO_SID=xxxxxx 123 | TWILIO_TOKEN=xxxxx 124 | TWILIO_FROM=xxxxx 125 | ``` 126 | 127 | You can get all the values from your Twilio dashboard. The `TWILIO_FROM` is a phone number you get when you are done creating account. This is where your SMS will originate from. 128 | 129 | ##### MessageBird 130 | 131 | MessageBird requires the following to be added and set in your `.env` file: 132 | 133 | ``` 134 | MESSAGEBIRD_KEY=xxxxxx 135 | MESSAGEBIRD_FROM=xxxxxx 136 | ``` 137 | 138 | You can get your MessageBird key from your dashboard after creating account. 139 | ___ 140 | 141 | After setting the SMS providers, you are almost there. Now you need to tell **Laravauth** how to retrieve a user's phone number by adding the following method to your user model: `laravauthPhone()`. 142 | 143 | ```php 144 | class User extends Authenticatable 145 | { 146 | use Notifiable; 147 | 148 | . 149 | . 150 | . 151 | 152 | public function laravauthPhone() 153 | { 154 | //The logic to retrieve user's phone number. 155 | } 156 | } 157 | 158 | ``` 159 | 160 | 161 | Next thing is the view that validates the token. In this mode, it requires the user logs in with `email` and `password` by default. If your login is not like this, maybe you use `username` and `password`, no problem, just update your configuration file and set the `login_id` and `login_id_rel` to the correct value. 162 | 163 | These two options are required to be passed from your `login` view, a corresponding `email` or whatever the case may be and `password`. Also, if your password uses another name other than `password`, make sure to update the `password_id` in the configuration and also the `password_id_rel` if the password column on the user's table in the database uses another name other than 'password'. 164 | 165 | Once a user logs in and the credentials are valid, a page asking for the token is served. **Laravauth** ships with a sample working page. The major thing is the markup seen below: 166 | 167 | ```html 168 |
169 | 170 | 171 | {{ csrf_field() }} 172 |
173 | ``` 174 | 175 | **Laravauth** uses a different route for validating authentication, which can be customized in your config file, look for `validator_route` and adjust to suit your needs. The default is `'validate'`. The form's action attribute should point to the validator route, the method should also be `POST`. 176 | 177 | The other thing required is a `token` input. This should not be confused with Laravel's own `_token` for protecting against CSRF (Cross-Site Request Forgery). You can change the name of the `token` variable used by **Laravauth** by updating the `token_var` option in the config file, the default is `'token'`. **Laravauth** has an helper method to output this variable name `laravauth_token_var_name()` so you don't need bother much, just output the function in the `name` attribute of the input that will be sent to the `validator` route. 178 | 179 | Once the form is submitted, the `validator` confirms if it's a valid token, if not the page is re-served. If it's valid, the user is authenticated and redirected to the auth page you define. 180 | 181 | # Configuration 182 | 183 | **Laravauth** comes with a handful of configuration, you can check the `laravauth` config file for all available options, they are documented so it should be easy to see what they do. 184 | 185 | One notable option is `soft_disable`. If you will like to disable **Laravauth** temporarily without removing the package, just set this value to `true` and **Laravauth** goes to sleep mode, it doesnt intercept your login. 186 | 187 | Another very important option is the `user_model` option. This should point to the model that access your user table. The default is `App\User` which is Laravel's default. 188 | 189 | For more options, check the configuration file. 190 | --------------------------------------------------------------------------------