Skip to content

Commit 4585753

Browse files
committed
Implement Two-factor authentication
1 parent 59ff1eb commit 4585753

File tree

6 files changed

+102
-26
lines changed

6 files changed

+102
-26
lines changed

app/Http/Controllers/Auth/AuthController.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Auth;
99

1010
use Pterodactyl\Http\Controllers\Controller;
11+
use PragmaRX\Google2FA\Google2FA;
1112
use Illuminate\Http\Request;
1213
use Illuminate\Foundation\Auth\ThrottlesLogins;
1314
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
@@ -27,6 +28,73 @@ class AuthController extends Controller
2728

2829
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
2930

31+
/**
32+
* Handle a login request to the application.
33+
*
34+
* @param \Illuminate\Http\Request $request
35+
* @return \Illuminate\Http\Response
36+
*/
37+
public function postLogin(Request $request)
38+
{
39+
$this->validate($request, [
40+
$this->loginUsername() => 'required', 'password' => 'required',
41+
]);
42+
43+
$throttles = $this->isUsingThrottlesLoginsTrait();
44+
45+
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
46+
return $this->sendLockoutResponse($request);
47+
}
48+
49+
$credentials = $this->getCredentials($request);
50+
51+
if (Auth::attempt($credentials, $request->has('remember'))) {
52+
if(User::select('id')->where('email', $request->input('email'))->where('use_totp', 1)->exists()) {
53+
$validator = Validator::make($request->all(), [
54+
'totp_token' => 'required|numeric'
55+
]);
56+
57+
if($validator->fails()) {
58+
Auth::logout();
59+
return redirect('auth/login')->withErrors($validator)->withInput();
60+
}
61+
62+
$google2fa = new Google2FA();
63+
64+
if($google2fa->verifyKey(User::where('email', $request->input('email'))->first()->totp_secret, $request->input('totp_token'))) {
65+
return $this->handleUserWasAuthenticated($request, $throttles);
66+
} else {
67+
Auth::logout();
68+
$validator->errors()->add('field', trans('validation.welcome'));
69+
return redirect('auth/login')->withErrors($validator)->withInput();
70+
}
71+
} else {
72+
return $this->handleUserWasAuthenticated($request, $throttles);
73+
}
74+
}
75+
76+
if ($throttles) {
77+
$this->incrementLoginAttempts($request);
78+
}
79+
80+
return redirect($this->loginPath())
81+
->withInput($request->only($this->loginUsername(), 'remember'))
82+
->withErrors([
83+
$this->loginUsername() => $this->getFailedLoginMessage(),
84+
]);
85+
}
86+
87+
/**
88+
* Check if the provided user has TOTP enabled.
89+
*
90+
* @param \Illuminate\Http\Request $request
91+
* @return \Illuminate\Http\Response
92+
*/
93+
public function checkTotp(Request $request)
94+
{
95+
return response()->json(User::select('id')->where('email', $request->input('email'))->where('use_totp', 1)->first());
96+
}
97+
3098
/**
3199
* Post-Authentication redirect location.
32100
*

app/Http/Middleware/RedirectIfAuthenticated.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function __construct(Guard $auth)
3535
public function handle($request, Closure $next)
3636
{
3737
if ($this->auth->check()) {
38-
return redirect('/home');
38+
return redirect('/');
3939
}
4040

4141
return $next($request);

app/Http/Routes/AuthRoutes.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
namespace Pterodactyl\Http\Routes;
44

55
use Illuminate\Routing\Router;
6+
use Request;
7+
use Pterodactyl\Models\User as User;
68

79
class AuthRoutes {
810

911
public function map(Router $router) {
1012
$router->group(['prefix' => 'auth'], function () use ($router) {
1113
$router->get('login', [ 'as' => 'auth.login', 'uses' => 'Auth\AuthController@getLogin' ]);
14+
$router->post('login/totp', [ 'as' => 'auth.login.totp', 'uses' => 'Auth\AuthController@checkTotp' ]);
1215
$router->post('login', [ 'as' => 'auth.login.submit', 'uses' => 'Auth\AuthController@postLogin' ]);
1316

1417
$router->get('password', [ 'as' => 'auth.password', 'uses' => 'Auth\PasswordController@getEmail' ]);

resources/lang/en/strings.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
'password' => 'Password',
1313
'email' => 'Email',
1414
'whoops' => 'Whoops',
15+
'failed' => 'Your request could not be processed. Please try again later.',
1516
'success' => 'Success',
1617
'location' => 'Location',
1718
'node' => 'Node',

resources/lang/en/validation.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
'array' => 'The :attribute must contain :size items.',
7373
],
7474
'string' => 'The :attribute must be a string.',
75+
'totp' => 'The totp token is invalid. Did it expire?',
7576
'timezone' => 'The :attribute must be a valid zone.',
7677
'unique' => 'The :attribute has already been taken.',
7778
'url' => 'The :attribute format is invalid.',

resources/views/auth/login.blade.php

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,31 +75,34 @@
7575
</div>
7676
<div class="col-md-3"></div>
7777
<script type="text/javascript">
78-
$(document).ready(function(){
79-
// $("#login-form").submit(function(event){
80-
// var check_email = $("#email").val();
81-
// $.ajax({
82-
// type: "POST",
83-
// url: "/auth/login/totp",
84-
// async: false,
85-
// data: { check: check_email },
86-
// success: function(data){
87-
// if(data == 'true'){
88-
// $("#openTOTP").modal('show');
89-
// $('#openTOTP').on('shown.bs.modal', function(){
90-
// $("#totp_token").focus();
91-
// })
92-
// event.preventDefault();
93-
// }else{
94-
// $(this).submit();
95-
// }
96-
// }
97-
// });
98-
// });
99-
// $("#totp-form").submit(function(){
100-
// $('#login-form :input').not(':submit').clone().hide().appendTo('#totp-form');
101-
// return true;
102-
// });
78+
$(document).ready(function() {
79+
$("#login-form").one("submit", function(event) {
80+
event.preventDefault();
81+
var check_email = $("#email").val();
82+
$.ajax({
83+
type: 'POST',
84+
url: '/auth/login/totp',
85+
data: {
86+
email: check_email,
87+
_token: '{!! csrf_token() !!}'
88+
}
89+
}).done(function(data) {
90+
if (typeof data.id !== 'undefined') {
91+
$("#openTOTP").modal('show');
92+
$('#openTOTP').on('shown.bs.modal', function() {
93+
$("#totp_token").focus();
94+
});
95+
} else {
96+
$("#login-form").submit();
97+
}
98+
}).fail(function(jqXHR) {
99+
alert("{{ trans('strings.failed') }}");
100+
});
101+
});
102+
$("#totp-form").submit(function() {
103+
$('#login-form :input').not(':submit').clone().hide().appendTo('#totp-form');
104+
return true;
105+
});
103106
});
104107
</script>
105108
@endsection

0 commit comments

Comments
 (0)