Skip to content

Commit 4f3c668

Browse files
committed
Refactor auth controllers to be cleaner and easier to maintain
1 parent 324b989 commit 4f3c668

File tree

7 files changed

+275
-203
lines changed

7 files changed

+275
-203
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\Auth;
4+
5+
use Illuminate\Http\Request;
6+
use Illuminate\Auth\AuthManager;
7+
use Illuminate\Http\JsonResponse;
8+
use PragmaRX\Google2FA\Google2FA;
9+
use Illuminate\Auth\Events\Failed;
10+
use Pterodactyl\Exceptions\DisplayException;
11+
use Pterodactyl\Http\Controllers\Controller;
12+
use Illuminate\Contracts\Auth\Authenticatable;
13+
use Illuminate\Contracts\Encryption\Encrypter;
14+
use Illuminate\Foundation\Auth\AuthenticatesUsers;
15+
use Illuminate\Contracts\Cache\Repository as CacheRepository;
16+
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
17+
18+
abstract class AbstractLoginController extends Controller
19+
{
20+
use AuthenticatesUsers;
21+
22+
/**
23+
* @var \Illuminate\Auth\AuthManager
24+
*/
25+
protected $auth;
26+
27+
/**
28+
* @var \Illuminate\Contracts\Cache\Repository
29+
*/
30+
protected $cache;
31+
32+
/**
33+
* @var \Illuminate\Contracts\Encryption\Encrypter
34+
*/
35+
protected $encrypter;
36+
37+
/**
38+
* @var \PragmaRX\Google2FA\Google2FA
39+
*/
40+
protected $google2FA;
41+
42+
/**
43+
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
44+
*/
45+
protected $repository;
46+
47+
/**
48+
* Lockout time for failed login requests.
49+
*
50+
* @var int
51+
*/
52+
protected $lockoutTime;
53+
54+
/**
55+
* After how many attempts should logins be throttled and locked.
56+
*
57+
* @var int
58+
*/
59+
protected $maxLoginAttempts;
60+
61+
/**
62+
* Where to redirect users after login / registration.
63+
*
64+
* @var string
65+
*/
66+
protected $redirectTo = '/';
67+
68+
/**
69+
* LoginController constructor.
70+
*
71+
* @param \Illuminate\Auth\AuthManager $auth
72+
* @param \Illuminate\Contracts\Cache\Repository $cache
73+
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
74+
* @param \PragmaRX\Google2FA\Google2FA $google2FA
75+
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
76+
*/
77+
public function __construct(
78+
AuthManager $auth,
79+
CacheRepository $cache,
80+
Encrypter $encrypter,
81+
Google2FA $google2FA,
82+
UserRepositoryInterface $repository
83+
) {
84+
$this->auth = $auth;
85+
$this->cache = $cache;
86+
$this->encrypter = $encrypter;
87+
$this->google2FA = $google2FA;
88+
$this->repository = $repository;
89+
90+
$this->lockoutTime = config('auth.lockout.time');
91+
$this->maxLoginAttempts = config('auth.lockout.attempts');
92+
}
93+
94+
/**
95+
* Get the failed login response instance.
96+
*
97+
* @param \Illuminate\Http\Request $request
98+
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
99+
*
100+
* @throws \Pterodactyl\Exceptions\DisplayException
101+
*/
102+
protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null)
103+
{
104+
$this->incrementLoginAttempts($request);
105+
$this->fireFailedLoginEvent($user, [
106+
$this->getField($request->input('user')) => $request->input('user'),
107+
]);
108+
109+
throw new DisplayException(trans('auth.failed'));
110+
}
111+
112+
/**
113+
* Send the response after the user was authenticated.
114+
*
115+
* @param \Illuminate\Http\Request $request
116+
* @return \Illuminate\Http\JsonResponse
117+
*/
118+
protected function sendLoginResponse(Request $request): JsonResponse
119+
{
120+
$request->session()->regenerate();
121+
122+
$this->clearLoginAttempts($request);
123+
124+
return $this->authenticated($request, $this->guard()->user())
125+
?: response()->json([
126+
'intended' => $this->redirectPath(),
127+
]);
128+
}
129+
130+
/**
131+
* Determine if the user is logging in using an email or username,.
132+
*
133+
* @param string $input
134+
* @return string
135+
*/
136+
protected function getField(string $input = null): string
137+
{
138+
return str_contains($input, '@') ? 'email' : 'username';
139+
}
140+
141+
/**
142+
* Fire a failed login event.
143+
*
144+
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
145+
* @param array $credentials
146+
*/
147+
protected function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = [])
148+
{
149+
event(new Failed($user, $credentials));
150+
}
151+
}

app/Http/Controllers/Auth/ForgotPasswordController.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,17 @@ protected function sendResetLinkFailedResponse(Request $request, $response): Red
2929

3030
return $this->sendResetLinkResponse(Password::RESET_LINK_SENT);
3131
}
32+
33+
/**
34+
* Get the response for a successful password reset link.
35+
*
36+
* @param string $response
37+
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
38+
*/
39+
protected function sendResetLinkResponse($response)
40+
{
41+
return response()->json([
42+
'status' => trans($response),
43+
]);
44+
}
3245
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\Auth;
4+
5+
use Illuminate\Http\JsonResponse;
6+
use Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest;
7+
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
8+
9+
class LoginCheckpointController extends AbstractLoginController
10+
{
11+
/**
12+
* Handle a login where the user is required to provide a TOTP authentication
13+
* token. In order to add additional layers of security, users are not
14+
* informed of an incorrect password until this stage, forcing them to
15+
* provide a token on each login attempt.
16+
*
17+
* @param \Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest $request
18+
* @return \Illuminate\Http\JsonResponse
19+
*
20+
* @throws \Pterodactyl\Exceptions\DisplayException
21+
*/
22+
public function index(LoginCheckpointRequest $request): JsonResponse
23+
{
24+
try {
25+
$cache = $this->cache->pull($request->input('confirmation_token'), []);
26+
$user = $this->repository->find(array_get($cache, 'user_id', 0));
27+
} catch (RecordNotFoundException $exception) {
28+
return $this->sendFailedLoginResponse($request);
29+
}
30+
31+
if (! array_get($cache, 'valid_credentials') || array_get($cache, 'request_ip') !== $request->ip()) {
32+
return $this->sendFailedLoginResponse($request, $user);
33+
}
34+
35+
if (! $this->google2FA->verifyKey(
36+
$this->encrypter->decrypt($user->totp_secret),
37+
$request->input('authentication_code'),
38+
config('pterodactyl.auth.2fa.window')
39+
)) {
40+
return $this->sendFailedLoginResponse($request, $user);
41+
}
42+
43+
$this->authManager->guard()->login($user, true);
44+
45+
return $this->sendLoginResponse($request);
46+
}
47+
}

0 commit comments

Comments
 (0)