├── .gitignore ├── tests ├── temp │ └── .gitignore ├── TwoFactorAuthenticationTest.php └── BaseTestCase.php ├── .travis.yml ├── src ├── Contracts │ └── TwoFactorAuthenticationInterface.php ├── Http │ └── Controllers │ │ ├── Controller.php │ │ └── TwoFactorAuthenticationController.php ├── RedirectUsers2FA.php ├── Exceptions │ └── TwoFactorAuthenticationExceptions.php ├── routes │ └── routes.php ├── TwoFactorAuthenticationServiceProvider.php └── AuthenticatesUsersWith2FA.php ├── .codeclimate.yml ├── phpunit.xml ├── LICENSE ├── database └── migrations │ └── 2017_01_20_160000_add_two_factor_authentication_required_fields.php ├── composer.json ├── resources └── views │ ├── verify.blade.php │ └── setup.blade.php ├── config └── 2fa-config.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor -------------------------------------------------------------------------------- /tests/temp/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.1 5 | 6 | before_script: 7 | - travis_retry composer self-update 8 | - travis_retry composer install --prefer-source --no-interaction 9 | 10 | script: phpunit 11 | -------------------------------------------------------------------------------- /src/Contracts/TwoFactorAuthenticationInterface.php: -------------------------------------------------------------------------------- 1 | intended($this->redirectPath()); 13 | } 14 | return redirect()->intended(config('2fa-config.redirect_to')); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Exceptions/TwoFactorAuthenticationExceptions.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | tests 14 | 15 | 16 | 17 | 18 | src/ 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/TwoFactorAuthenticationTest.php: -------------------------------------------------------------------------------- 1 | user = \DB::table('users')->first(); 14 | $this->assertEquals(1, $this->user->id); 15 | } 16 | 17 | public function testIfColumnExists() 18 | { 19 | $this->assertTrue(Schema::hasColumn(config('2fa-config.table'), 'two_factor_provisioned_uri')); 20 | $this->assertTrue(Schema::hasColumn(config('2fa-config.table'), 'is_two_factor_enabled')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/routes/routes.php: -------------------------------------------------------------------------------- 1 | ['web'], 'namespace' => '\Thecodework\TwoFactorAuthentication\Http\Controllers'], function () { 4 | Route::get(config('2fa-config.verify_2fa'), 'TwoFactorAuthenticationController@verifyTwoFactorAuthentication'); 5 | Route::post(config('2fa-config.verify_2fa_post'), 'TwoFactorAuthenticationController@verifyToken'); 6 | Route::get(config('2fa-config.setup_2fa'), 'TwoFactorAuthenticationController@setupTwoFactorAuthentication'); 7 | Route::post(config('2fa-config.enable_2fa'), 'TwoFactorAuthenticationController@enableTwoFactorAuthentication'); 8 | Route::post(config('2fa-config.disable_2fa'), 'TwoFactorAuthenticationController@disableTwoFactorAuthentication'); 9 | }); 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 The Code Work 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 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_160000_add_two_factor_authentication_required_fields.php: -------------------------------------------------------------------------------- 1 | smallInteger('is_two_factor_enabled')->nullable()->default(0)->before('created_at'); 18 | $table->string('two_factor_provisioned_uri', 500)->nullable()->after('is_two_factor_enabled'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::table(config('2fa-config.table'), function (Blueprint $table) { 30 | $table->dropColumn('is_two_factor_enabled'); 31 | $table->dropColumn('two_factor_provisioned_uri'); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thecodework/two-factor-authentication", 3 | "type": "package", 4 | "description": "Two Factor Authentication (2FA) for Laravel", 5 | "license": "MIT", 6 | "keywords": [ 7 | "laravel", 8 | "Laravel 5", 9 | "two-factor", 10 | "authentication", 11 | "2fa", 12 | "rfc-6238" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Ashish Singh", 17 | "email": "imrealashu@gmail.com" 18 | } 19 | ], 20 | "require": { 21 | "php": ">=8.0", 22 | "spomky-labs/otphp": "^10.0.3", 23 | "paragonie/constant_time_encoding": "^2.5", 24 | "endroid/qr-code": "~4.4.8" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Thecodework\\TwoFactorAuthentication\\": "src/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "Thecodework\\TwoFactorAuthentication\\Tests\\": "tests/" 34 | } 35 | }, 36 | "require-dev": { 37 | "phpunit/phpunit": "~9.5.20", 38 | "orchestra/testbench": "~7.4.0" 39 | }, 40 | "scripts": { 41 | "test": "vendor/bin/phpunit" 42 | }, 43 | "extra": { 44 | "laravel": { 45 | "providers": [ 46 | "Thecodework\\TwoFactorAuthentication\\TwoFactorAuthenticationServiceProvider" 47 | ] 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/TwoFactorAuthenticationServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadRoutesFrom(__DIR__ . '/routes/routes.php'); 19 | $this->loadViewsFrom(__DIR__ . '/../resources/views', '2fa'); 20 | $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); 21 | 22 | // Publishing configuration file 23 | $this->publishes([ 24 | __DIR__ . '/../config/2fa-config.php' => config_path('2fa-config.php'), 25 | ], 'config'); 26 | 27 | // Publishing migration 28 | $this->publishes([ 29 | __DIR__ . '/../database/migrations/' => database_path('migrations'), 30 | ], 'migrations'); 31 | 32 | // Publishing views 33 | $this->publishes([ 34 | __DIR__ . '/../resources/views/' => resource_path('views/vendor/2fa'), 35 | ], 'views'); 36 | } 37 | 38 | /** 39 | * Get User model defined in config file. 40 | * 41 | * @return string 42 | */ 43 | public static function determineTwoFAModel(): string 44 | { 45 | return config('2fa-config.model'); 46 | } 47 | 48 | /** 49 | * Get User Model Instance. 50 | * 51 | * @return \Illuminate\Database\Eloquent\Model 52 | */ 53 | public static function getTwoFAModelInstance(): Model 54 | { 55 | $TwoFAModelClassName = self::determineTwoFAModel(); 56 | 57 | return new $TwoFAModelClassName(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /resources/views/verify.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('content') 3 |
4 |
5 |
6 |
7 |
8 | Verify Two Factor Authentication 9 |
10 |
11 |
12 | {{ csrf_field() }} 13 | {{--
Download the Google Authenticator App on your phone from the Play Store or the App Store.

--}} 14 |
15 | 16 |
17 | 19 | 20 | @if ($errors->has('totp_token')) 21 | 22 | {{ $errors->first('totp_token') }} 23 | 24 | @endif 25 |
26 |
27 |
28 |
29 | 32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | @endsection -------------------------------------------------------------------------------- /resources/views/setup.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('content') 3 |
4 |
5 |
6 |
7 |
Verify Two Factor Authentication
8 |
9 | @if($user->is_two_factor_enabled) 10 |
11 | @else 12 | 13 | @endif 14 | {{ csrf_field() }} 15 |
16 | Google Authenticator generates 2-Step Verification codes on your phone. 17 | 2-Step Verification provides stronger security for account by requiring a second step of verification when you sign in. In addition to your password, you’ll also need a code generated by the Google Authenticator app on your phone. 18 |
19 |
20 | @if(! $user->is_two_factor_enabled) 21 |

Please scan this barcode using Google Authenticator or Authy client Application and Click Enable Button

22 |
23 | 24 |
25 | @endif 26 |
27 |
28 |
29 | @if($user->is_two_factor_enabled) 30 | 33 | @else 34 | 37 | @endif 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | @endsection -------------------------------------------------------------------------------- /tests/BaseTestCase.php: -------------------------------------------------------------------------------- 1 | setUpDatabase(); 15 | } 16 | 17 | protected function getPackageProviders($app) 18 | { 19 | return [ 20 | \Thecodework\TwoFactorAuthentication\TwoFactorAuthenticationServiceProvider::class, 21 | ]; 22 | } 23 | 24 | protected function seedUserDetails() 25 | { 26 | \DB::table('users')->insert([ 27 | 'name' => 'Test User', 28 | 'email' => 'test@user.in', 29 | 'password' => bcrypt('test'), 30 | ]); 31 | } 32 | 33 | protected function AddTwoFactorAuthenticationRequiredFields() 34 | { 35 | include_once '__DIR__' . '/../database/migrations/2017_01_20_160000_add_two_factor_authentication_required_fields.php'; 36 | 37 | $this->createUsersTable(); 38 | (new \AddTwoFactorAuthenticationRequiredFields())->up(); 39 | } 40 | 41 | public function getTempDirectory(): string 42 | { 43 | return __DIR__ . '/temp'; 44 | } 45 | 46 | protected function resetDatabase() 47 | { 48 | file_put_contents($this->getTempDirectory() . '/database.sqlite', null); 49 | } 50 | 51 | protected function setUpDatabase() 52 | { 53 | $this->resetDatabase(); 54 | $this->AddTwoFactorAuthenticationRequiredFields(); 55 | $this->seedUserDetails(); 56 | } 57 | 58 | protected function createUsersTable() 59 | { 60 | Schema::create('users', function (Blueprint $table) { 61 | $table->increments('id'); 62 | $table->string('name'); 63 | $table->string('email')->unique(); 64 | $table->string('password'); 65 | $table->rememberToken(); 66 | $table->timestamps(); 67 | }); 68 | } 69 | 70 | public function getEnvironmentSetUp($app) 71 | { 72 | $app['config']->set('database.default', 'sqlite'); 73 | $app['config']->set('database.connections.sqlite', [ 74 | 'driver' => 'sqlite', 75 | 'database' => $this->getTempDirectory() . '/database.sqlite', 76 | 'prefix' => '', 77 | ]); 78 | $app['config']->set('auth.providers.users.model', User::class); 79 | $app['config']->set('app.key', '6rE9Nz59bGRbeMATftriyQjrpF7DcOQm'); 80 | $app['config']->set('2fa-config.table', 'users'); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /config/2fa-config.php: -------------------------------------------------------------------------------- 1 | '/home', 12 | 13 | /* 14 | |-------------------------------------------------------------------------- 15 | | Routes 16 | |-------------------------------------------------------------------------- 17 | | 18 | | Change the routes if your existing routes conflicts with default Two-Factor Authentication Routes. 19 | | Customize route name. 20 | | 21 | | Route Name => Default Route Name Used 22 | | Customize Name Example 23 | | setup_2fa => customize_route_name 24 | | 25 | */ 26 | 'setup_2fa' => 'setup-2fa', 27 | 'enable_2fa' => 'enable-2fa', 28 | 'disable_2fa' => 'disable-2fa', 29 | 'verify_2fa' => 'verify-2fa', //get Route 30 | 'verify_2fa_post' => 'verify-2fa', //post 31 | 32 | /* 33 | * Account name which will be used as label to show on 34 | * authenticator mobile application. 35 | */ 36 | 'account_name' => env('APP_NAME', 'Thecodework 2FA'), 37 | 38 | /* 39 | * Set Guard for 2FA 40 | * By default the `web` guard will be used but you 41 | * can define any custom guard to utilize 2FA. 42 | */ 43 | 'guard' => 'web', 44 | 45 | /* 46 | * Currently Support 'Sha1' 47 | * The library works with the Google Authenticator application 48 | * for iPhone and Android. Google only supports SHA-1 digest algorithm, 49 | * 30 second period and 6 digits OTP. Other values for these parameters 50 | * are ignored by the Google Authenticator application. 51 | */ 52 | 'digest_algorithm' => 'sha1', 53 | 54 | /* 55 | * Size of Base32 encoded secret key. 56 | * Default 6 Works with GA and Authy. 57 | */ 58 | 'number_of_digits' => 6, 59 | 60 | /* 61 | * The Number of Seconds the code will be valid. 62 | * Default 30. 63 | * Google Authenticator only uses 30 sec period 64 | */ 65 | 'period' => 30, 66 | 67 | /* 68 | * Explicitly Define Table name for the model. 69 | */ 70 | 'table' => 'users', 71 | 72 | /* 73 | * User Model 74 | * By Default `\App\Models\User` Model is defined. 75 | */ 76 | 'model' => '\App\Models\User', 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Logo 81 | |-------------------------------------------------------------------------- 82 | | 83 | | Some App like Authy Use Logo .A default company logo will be used 84 | | Note-* all apps support logo being sent. 85 | | you can use you own logo file requirements : 86 | | 1. Image File must be png 87 | | 2. Image must be public 88 | | 3. Full Uri with qualify path and protocol 89 | | 90 | */ 91 | 'logo' => 'https://thecodework.com/wp-content/themes/thecodework/assets/img/thecodework_logo.png' 92 | ]; 93 | -------------------------------------------------------------------------------- /src/AuthenticatesUsersWith2FA.php: -------------------------------------------------------------------------------- 1 | is_two_factor_enabled) { 33 | $request->session()->put('2fa:user:id', encrypt($user->id)); 34 | $secret = getenv('HMAC_SECRET'); 35 | $signature = hash_hmac('sha256', $user->id, $secret); 36 | Auth::logout(); 37 | 38 | return redirect()->intended(config('2fa-config.verify_2fa') . '?signature=' . $signature); 39 | } 40 | 41 | return $this->redirectUsers2FA(); 42 | } 43 | 44 | /** 45 | * Verify token and sign in the user. 46 | * 47 | * @param \Illuminate\Http\Request $request 48 | * 49 | * @return \Illuminate\Http\RedirectResponse 50 | */ 51 | public function verifyToken(Request $request) 52 | { 53 | $TwoFAModel = TwoFactorAuthenticationServiceProvider::getTwoFAModelInstance(); 54 | // Pulling encrypted user id from session and getting user details 55 | $userId = $request->session()->get('2fa:user:id'); 56 | $this->user = $TwoFAModel->find(decrypt($userId)); 57 | 58 | // If token is not valid then custom validation error message will be shown. 59 | $messages = [ 60 | 'totp_token.valid_token' => 'Security code is not valid', 61 | 'totp_token.required' => 'Security code is required', 62 | ]; 63 | 64 | // Impllicitly adding an validation rule to check if token is valid or not. 65 | Validator::extendImplicit('valid_token', function ($attribute, $value) { 66 | $totp = Factory::loadFromProvisioningUri($this->user->two_factor_provisioned_uri); 67 | 68 | return $totp->verify($value); 69 | }); 70 | 71 | // If Validation fails, it will return the error else sign in the user. 72 | $number_of_digits = config('2fa-config.number_of_digits'); 73 | $validator = Validator::make($request->all(), [ 74 | 'totp_token' => "required|digits:$number_of_digits|valid_token", 75 | ], $messages); 76 | 77 | $secret = getenv('HMAC_SECRET'); 78 | $signature = hash_hmac('sha256', $this->user->id, $secret); 79 | if ($validator->fails()) { 80 | return redirect(config('2fa-config.verify_2fa') . '?signature=' . $signature) 81 | ->withErrors($validator) 82 | ->withInput(); 83 | } 84 | 85 | // Flush the session. 86 | $request->session()->forget('2fa:user:id'); 87 | 88 | Auth::loginUsingId($this->user->id); 89 | 90 | return $this->redirectUsers2FA(); 91 | } 92 | 93 | public function setUser($user) 94 | { 95 | $this->user = $user; 96 | } 97 | 98 | public function getUser() 99 | { 100 | return $this->user; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/thecodework/two-factor-authentication.svg?branch=master)](https://travis-ci.org/thecodework/two-factor-authentication) 2 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/thecodework/two-factor-authentication/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/thecodework/two-factor-authentication/?branch=master) 3 | [![StyleCI](https://styleci.io/repos/85341644/shield?branch=master)](https://styleci.io/repos/85341644) 4 | [![License](https://poser.pugx.org/thecodework/two-factor-authentication/license)](https://packagist.org/packages/thecodework/two-factor-authentication) 5 | 6 | # Laravel Two Factor Authentication (2FA) 7 | 8 | ![Two](http://imrealashu.in/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-00.19.05.png) 9 | 10 | Two Factor Authentication or 2-Step Verification provides stronger security for your Account by requiring a second step of verification when you sign in. In addition to your password, you’ll also need a code generated by the Google Authenticator app on your phone. This package implements TOTP defined in [RFC 6238](https://tools.ietf.org/html/rfc6238) 11 | 12 | ## Requirements 13 | 14 | - PHP >= 7.1 15 | - Laravel >= 5.3 16 | - Google Authenticator [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en) - [iOS](https://itunes.apple.com/in/app/google-authenticator/id388497605?mt=8) (Recommended) or [Authy](https://www.authy.com/) mobile app 17 | 18 | ## Installation 19 | 20 | **1. Composer Install** 21 | 22 | ```bash 23 | $ composer require thecodework/two-factor-authentication 24 | ``` 25 | 26 | _Note_ - If your're using Laravel 5.5 or newer version then auto-discovery-pacakge would automatically update the providers and you could skip to **Step 3** 27 | 28 | **2. Add Service Provider** 29 | 30 | After requiring the package add `TwoFactorAuthenticationServiceProvider::class` into providors array in `app.php` confi file 31 | 32 | ```php 33 | [ 34 | 'providers' => [ 35 | //... 36 | Thecodework\TwoFactorAuthentication\TwoFactorAuthenticationServiceProvider::class 37 | ] 38 | ] 39 | ``` 40 | 41 | **3. Publish the ConfigFile** 42 | 43 | Publish config file 44 | 45 | ``` 46 | $ php artisan vendor:publish --provider="Thecodework\TwoFactorAuthentication\TwoFactorAuthenticationServiceProvider" --tag=config 47 | ``` 48 | 49 | Once the config file is published you can navigate to config directory of your application and look for `2fa-config.php` file and change configuration as you want. 50 | 51 | **4. Run Migrations** 52 | 53 | Now run the migration 54 | 55 | ```bash 56 | $ php artisan migrate 57 | ``` 58 | 59 | It will use the default User model and adds two columns `is_2fa_enabled` and `secret_key`. 60 | 61 | **5. Add `AuthenticatesUserWith2FA` trait in the LoginController** 62 | 63 | Now the config file is placed. The last thing to do is addding `AuthenticatesUsersWith2FA` trait in the `Http/Controllers/Auth/LoginController.php` file which helps to stop user at verify-2fa page to enter TOTP token after each login. 64 | 65 | The final snippet will look like this. 66 | 67 | ```php 68 | use AuthenticatesUsers, AuthenticatesUsersWith2FA { 69 | AuthenticatesUsersWith2FA::authenticated insteadof AuthenticatesUsers; 70 | } 71 | ``` 72 | 73 | Note: Don't forget to include use statement `use Thecodework\TwoFactorAuthentication\AuthenticatesUsersWith2FA` in the header. 74 | 75 | **6. Setup 2FA for user** 76 | 77 | **• Enable 2FA** 78 | 79 | Now login to the application and visit `/setup-2fa/` route, which will show a barcode which can be scanned either using Google Authenticator or Authy mobile application as described above. 80 | Scan that code and click **Enable Two Factor Authentication**. 81 | 82 | **• Disable 2FA** 83 | 84 | To disable Two Factor, visit `/setup-2fa` route, which will now show a **Disable Two Factor Authentication** button. Click to disable 2FA for your account. 85 | 86 | **7. Testing 2FA** 87 | 88 | Now to test 2FA, perform logout and log back in again, it will ask you to enter Token which can be obtain from the authenticator mobile application. Enter the token and you're logged in. 89 | 90 | ### Additionally 91 | 92 | If you want to publish views, and migration as well along with config file then run 93 | 94 | ``` 95 | $ php artisan vendor:publish --provider="Thecodework\TwoFactorAuthentication\TwoFactorAuthenticationServiceProvider" 96 | ``` 97 | 98 | ## Contribution 99 | 100 | Feel free to create issues, submit PRs and talk about features and enhancement through proposing issue. If you find any security consideration, instead of creating an issue send an email to [imrealashu@gmail.com](mailto:imrealashu@gmail.com). 101 | -------------------------------------------------------------------------------- /src/Http/Controllers/TwoFactorAuthenticationController.php: -------------------------------------------------------------------------------- 1 | TwoFAModel = TwoFactorAuthenticationServiceProvider::getTwoFAModelInstance(); 33 | 34 | $this->middleware(function ($request, $next) { 35 | $this->setUser(\Auth::guard(config('2fa-config.guard'))->user()); 36 | 37 | return $next($request); 38 | }); 39 | } 40 | 41 | /** 42 | * Setup two factor authentication. 43 | * 44 | * @param \Illuminate\Http\Request 45 | * @param \Illuminate\Http\Response 46 | * 47 | * @throws \Thecodework\TwoFactorAuthentications\Exceptions\TwoFactorAuthenticationExceptions 48 | * 49 | * @return mixed 50 | */ 51 | public function setupTwoFactorAuthentication(Request $request) 52 | { 53 | $user = $this->getUser(); 54 | $totp = TOTP::create( 55 | $this->base32EncodedString(), 56 | config('2fa-config.period'), 57 | config('2fa-config.digest_algorithm'), 58 | config('2fa-config.number_of_digits') 59 | ); 60 | $totp->setLabel(config('2fa-config.account_name')); 61 | $totp->setParameter('image', config('2fa-config.logo')); 62 | 63 | $this->updateUserWithProvisionedUri($totp->getProvisioningUri()); 64 | 65 | //Generate Qr Code 66 | $writer = new PngWriter(); 67 | $qrCode = QrCode::create($totp->getProvisioningUri())->setEncoding(new Encoding('ISO-8859-1')); 68 | $result = $writer->write($qrCode); 69 | $barcode = $result->getDataUri(); 70 | 71 | if ($request->ajax()) { 72 | return $barcode; 73 | } 74 | 75 | return view('2fa::setup', compact('barcode', 'user')); 76 | } 77 | 78 | /** 79 | * Enable 2FA. 80 | * 81 | * @param \Illuminate\Http\Request 82 | * 83 | * @return mixed 84 | */ 85 | public function enableTwoFactorAuthentication(Request $request) 86 | { 87 | $user = $this->getUser(); 88 | $user->is_two_factor_enabled = 1; 89 | $user->update(); 90 | 91 | if ($request->ajax()) { 92 | return [ 93 | 'data' => [ 94 | 'message' => 'success', 95 | 'description' => '2FA Enabled', 96 | ], 97 | ]; 98 | } 99 | 100 | return $this->redirectUsers2FA(); 101 | } 102 | 103 | /** 104 | * Disable 2FA. 105 | * 106 | * @param \Illuminate\Http\Request 107 | * 108 | * @return mixed 109 | */ 110 | public function disableTwoFactorAuthentication(Request $request) 111 | { 112 | $user = $this->getUser(); 113 | $user->is_two_factor_enabled = 0; 114 | $user->two_factor_provisioned_uri = null; 115 | $user->update(); 116 | 117 | if ($request->ajax()) { 118 | return [ 119 | 'data' => [ 120 | 'message' => 'success', 121 | 'description' => '2FA Disabled', 122 | ], 123 | ]; 124 | } 125 | 126 | return $this->redirectUsers2FA(); 127 | } 128 | /** 129 | * Verify Two Factor Authentication. 130 | * 131 | * @param \Illuminate\Http\Request $request 132 | */ 133 | public function verifyTwoFactorAuthentication(Request $request) 134 | { 135 | if ($request->session()->has('2fa:user:id')) { 136 | $secret = getenv('HMAC_SECRET'); 137 | $signature = hash_hmac('sha256', decrypt($request->session()->get('2fa:user:id')), $secret); 138 | 139 | if (md5($signature) !== md5($request->signature)) { 140 | return redirect()->intended('login'); 141 | } 142 | 143 | return view('2fa::verify'); 144 | } 145 | 146 | return redirect()->back(); //shoud be configurable 147 | } 148 | 149 | /** 150 | * Encode Random String to 32 Base Transfer Encoding. 151 | * 152 | * @return string 153 | */ 154 | private function base32EncodedString(): string 155 | { 156 | return trim(Base32::encodeUpper(random_bytes(128)), '='); 157 | } 158 | 159 | /** 160 | * Update User data with 2FA generated Key. 161 | * 162 | * @return void 163 | */ 164 | private function updateUserWithProvisionedUri($twoFactorProvisionedUri) 165 | { 166 | $user = $this->TwoFAModel->find($this->getUser()->id); 167 | if ( 168 | !Schema::hasColumn(config('2fa-config.table'), 'two_factor_provisioned_uri') || 169 | !Schema::hasColumn(config('2fa-config.table'), 'is_two_factor_enabled') 170 | ) { 171 | throw TwoFactorAuthenticationExceptions::columnNotFound(); 172 | } 173 | $user->two_factor_provisioned_uri = $twoFactorProvisionedUri; 174 | $user->update(); 175 | } 176 | } 177 | --------------------------------------------------------------------------------