-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
2FA Two Authentication Factor #164 #724
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
Changes from 31 commits
7db3df7
b8f3803
280498a
f5ef3f9
9c956a5
70cb16b
a6cbd94
b0ef8cc
df3b0a4
a1c1468
e49f927
63bc2f3
6f44483
0d37211
c8c8266
db98f2c
b81a417
6006cf6
663db7c
42260cf
2c5b1db
a740b68
b37c399
28380f2
fbe4586
efe87c5
8d0e1bf
151535b
a7c0cfa
0b0f393
d2159bb
06612eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,3 +98,6 @@ AWS_SECRET= | |
| AWS_REGION=us-east-1 | ||
| AWS_BUCKET= | ||
| AWS_SERVER= | ||
|
|
||
| # Enable Two Factor Authentication | ||
| 2FA_ENABLED=false | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,3 +10,5 @@ DB_TEST_PASSWORD= | |
| CACHE_DRIVER=array | ||
| SESSION_DRIVER=array | ||
| QUEUE_DRIVER=sync | ||
|
|
||
| 2FA_ENABLED=false | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,3 +11,4 @@ monica.sublime-workspace | |
| /storage/oauth-private.key | ||
| /storage/oauth-public.key | ||
| .DS_Store | ||
| npm-debug.log.* | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,4 +24,4 @@ Scott Williams @scott-joe | |
| Craig Davison @davisonio <[email protected]> | ||
| Michael Heap @mheap <[email protected]> | ||
| Matthew Du Pont @mattdp <[email protected]> | ||
| Alexis Saettler @asbiin | ||
| Alexis Saettler @asbiin <[email protected]> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| <?php | ||
|
|
||
| namespace App\Console\Commands; | ||
|
|
||
| use App\User; | ||
| use Illuminate\Console\Command; | ||
|
|
||
| class Deactivate2FA extends Command | ||
| { | ||
| /** | ||
| * The name and signature of the console command. | ||
| * | ||
| * @var string | ||
| */ | ||
| protected $signature = '2fa:deactivate {--email= : The email of the user to deactivate 2FA} {--force : run without asking for confirmation}'; | ||
|
|
||
| /** | ||
| * The console command description. | ||
| * | ||
| * @var string | ||
| */ | ||
| protected $description = 'Deactivate 2FA for this user'; | ||
|
|
||
| /** | ||
| * Create a new command instance. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function __construct() | ||
| { | ||
| parent::__construct(); | ||
| } | ||
|
|
||
| /** | ||
| * Execute the console command. | ||
| * | ||
| * @return mixed | ||
| */ | ||
| public function handle() | ||
| { | ||
| // retrieve the email from the option | ||
| $email = $this->option('email'); | ||
|
|
||
| // if no email was passed to the option, prompt the user to enter the email | ||
| if (! $email) { | ||
| $email = $this->ask('what is the user\'s email?'); | ||
| } | ||
|
|
||
| // retrieve the user with the specified email | ||
| $user = User::where('email', $email)->first(); | ||
|
|
||
| if (! $user) { | ||
| // show an error and exist if the user does not exist | ||
| $this->error('No user with that email.'); | ||
|
|
||
| return; | ||
| } | ||
| if (is_null($user->google2fa_secret)) { | ||
| // show an error and exist if the user does not exist | ||
| $this->error('2FA is currently not activated for this user.'); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // Print a warning | ||
| $this->info('2FA will be deactivated for '.$user->email); | ||
| $this->info('This action can\'t be cancelled.'); | ||
|
|
||
| // ask for confirmation if not forced | ||
| if (! $this->option('force') && ! $this->confirm('Do you wish to continue?')) { | ||
| return; | ||
| } | ||
|
|
||
| // remove google2fa_secret key | ||
| $user->google2fa_secret = null; | ||
|
|
||
| // save the user | ||
| $user->save(); | ||
|
|
||
| // show the new secret key | ||
| $this->info('2FA has been deactivated for '.$user->email); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| <?php | ||
|
|
||
| namespace App\Http\Controllers\Settings; | ||
|
|
||
| use Google2FA; | ||
| use Illuminate\Http\Request; | ||
| use App\Http\Controllers\Controller; | ||
| use Illuminate\Foundation\Auth\RedirectsUsers; | ||
| use PragmaRX\Google2FALaravel\Support\Authenticator; | ||
|
|
||
| class MultiFAController extends Controller | ||
| { | ||
| use RedirectsUsers; | ||
|
|
||
| protected $redirectTo = '/settings/security'; | ||
|
|
||
| /** | ||
| * Session var name to store secret code. | ||
| */ | ||
| private const SESSION_TFA_SECRET = '2FA_secret'; | ||
|
|
||
| /** | ||
| * Create a new authentication controller instance. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function __construct() | ||
| { | ||
| $this->middleware('web'); | ||
| } | ||
|
|
||
| /** | ||
| * @param \Illuminate\Http\Request $request | ||
| * @return \Illuminate\Http\Response | ||
| */ | ||
| public function enableTwoFactor(Request $request) | ||
| { | ||
| //generate new secret | ||
| $secret = $this->generateSecret(); | ||
|
|
||
| $user = $request->user(); | ||
|
|
||
| //generate image for QR barcode | ||
| $imageDataUri = Google2FA::getQRCodeInline( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if it's an issue, but static analysis says that this is an instance method, called statically
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, we should use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or just add it to the (great) PR #878 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have added |
||
| $request->getHttpHost(), | ||
| $user->email, | ||
| $secret, | ||
| 200 | ||
| ); | ||
|
|
||
| $request->session()->put(self::SESSION_TFA_SECRET, $secret); | ||
|
|
||
| return view('settings.security.2fa-enable', ['image' => $imageDataUri, 'secret' => $secret]); | ||
| } | ||
|
|
||
| /** | ||
| * @param \Illuminate\Http\Request $request | ||
| * @return \Illuminate\Http\Response | ||
| */ | ||
| public function validateTwoFactor(Request $request) | ||
| { | ||
| $this->validate($request, [ | ||
| 'one_time_password' => 'required', | ||
| ]); | ||
|
|
||
| //retrieve secret | ||
| $secret = $request->session()->pull(self::SESSION_TFA_SECRET); | ||
|
|
||
| $authenticator = app(Authenticator::class)->boot($request); | ||
|
|
||
| if ($authenticator->verifyGoogle2FA($secret, $request['one_time_password'])) { | ||
| //get user | ||
| $user = $request->user(); | ||
|
|
||
| //encrypt and then save secret | ||
| $user->google2fa_secret = $secret; | ||
| $user->save(); | ||
|
|
||
| $authenticator->login(); | ||
|
|
||
| return redirect($this->redirectPath()) | ||
| ->with('status', trans('settings.2fa_enable_success')); | ||
| } | ||
|
|
||
| $authenticator->logout(); | ||
|
|
||
| return redirect($this->redirectPath()) | ||
| ->withErrors(trans('settings.2fa_enable_error')); | ||
| } | ||
|
|
||
| /** | ||
| * @param \Illuminate\Http\Request $request | ||
| * @return \Illuminate\Http\Response | ||
| */ | ||
| public function disableTwoFactor(Request $request) | ||
| { | ||
| return view('settings.security.2fa-disable'); | ||
| } | ||
|
|
||
| /** | ||
| * @param \Illuminate\Http\Request $request | ||
| * @return \Illuminate\Http\Response | ||
| */ | ||
| public function deactivateTwoFactor(Request $request) | ||
| { | ||
| $this->validate($request, [ | ||
| 'one_time_password' => 'required', | ||
| ]); | ||
|
|
||
| $user = $request->user(); | ||
|
|
||
| //retrieve secret | ||
| $secret = $user->google2fa_secret; | ||
|
|
||
| $authenticator = app(Authenticator::class)->boot($request); | ||
|
|
||
| if ($authenticator->verifyGoogle2FA($secret, $request['one_time_password'])) { | ||
|
|
||
| //make secret column blank | ||
| $user->google2fa_secret = null; | ||
| $user->save(); | ||
|
|
||
| $authenticator->logout(); | ||
|
|
||
| return redirect($this->redirectPath()) | ||
| ->with('status', trans('settings.2fa_disable_success')); | ||
| } | ||
|
|
||
| return redirect($this->redirectPath()) | ||
| ->withErrors(trans('settings.2fa_disable_error')); | ||
| } | ||
|
|
||
| /** | ||
| * Generate a secret key in Base32 format. | ||
| * | ||
| * @return string | ||
| */ | ||
| private function generateSecret() | ||
| { | ||
| $google2fa = app('pragmarx.google2fa'); | ||
|
|
||
| return $google2fa->generateSecretKey(32); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment here on what 2FA is and does?