22
33namespace Pterodactyl \Http \Controllers \Auth ;
44
5+ use Pterodactyl \Models \User ;
56use Illuminate \Auth \AuthManager ;
67use Illuminate \Http \JsonResponse ;
78use PragmaRX \Google2FA \Google2FA ;
89use Illuminate \Contracts \Config \Repository ;
910use Illuminate \Contracts \Encryption \Encrypter ;
11+ use Illuminate \Database \Eloquent \ModelNotFoundException ;
1012use Pterodactyl \Http \Requests \Auth \LoginCheckpointRequest ;
1113use Illuminate \Contracts \Cache \Repository as CacheRepository ;
1214use Pterodactyl \Contracts \Repository \UserRepositoryInterface ;
@@ -80,29 +82,31 @@ public function __construct(
8082 * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
8183 * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
8284 * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
83- * @throws \Pterodactyl\Exceptions\DisplayException
85+ * @throws \Exception
86+ * @throws \Illuminate\Validation\ValidationException
8487 */
8588 public function __invoke (LoginCheckpointRequest $ request ): JsonResponse
8689 {
87- $ token = $ request ->input ('confirmation_token ' );
88- $ recoveryToken = $ request ->input ('recovery_token ' );
90+ if ($ this ->hasTooManyLoginAttempts ($ request )) {
91+ $ this ->sendLockoutResponse ($ request );
92+ }
8993
94+ $ token = $ request ->input ('confirmation_token ' );
9095 try {
9196 /** @var \Pterodactyl\Models\User $user */
92- $ user = $ this ->repository ->find ($ this ->cache ->get ($ token , 0 ));
93- } catch (RecordNotFoundException $ exception ) {
94- return $ this ->sendFailedLoginResponse ($ request , null , 'The authentication token provided has expired, please refresh the page and try again. ' );
95- }
97+ $ user = User::query ()->findOrFail ($ this ->cache ->get ($ token , 0 ));
98+ } catch (ModelNotFoundException $ exception ) {
99+ $ this ->incrementLoginAttempts ($ request );
96100
97- // If we got a recovery token try to find one that matches for the user and then continue
98- // through the process (and delete the token).
99- if (! is_null ($ recoveryToken )) {
100- foreach ($ user ->recoveryTokens as $ token ) {
101- if (password_verify ($ recoveryToken , $ token ->token )) {
102- $ this ->recoveryTokenRepository ->delete ($ token ->id );
101+ return $ this ->sendFailedLoginResponse (
102+ $ request , null , 'The authentication token provided has expired, please refresh the page and try again. '
103+ );
104+ }
103105
104- return $ this ->sendLoginResponse ($ user , $ request );
105- }
106+ // Recovery tokens go through a slightly different pathway for usage.
107+ if (! is_null ($ recoveryToken = $ request ->input ('recovery_token ' ))) {
108+ if ($ this ->isValidRecoveryToken ($ user , $ recoveryToken )) {
109+ return $ this ->sendLoginResponse ($ user , $ request );
106110 }
107111 } else {
108112 $ decrypted = $ this ->encrypter ->decrypt ($ user ->totp_secret );
@@ -114,6 +118,31 @@ public function __invoke(LoginCheckpointRequest $request): JsonResponse
114118 }
115119 }
116120
121+ $ this ->incrementLoginAttempts ($ request );
122+
117123 return $ this ->sendFailedLoginResponse ($ request , $ user , ! empty ($ recoveryToken ) ? 'The recovery token provided is not valid. ' : null );
118124 }
125+
126+ /**
127+ * Determines if a given recovery token is valid for the user account. If we find a matching token
128+ * it will be deleted from the database.
129+ *
130+ * @param \Pterodactyl\Models\User $user
131+ * @param string $value
132+ * @return bool
133+ *
134+ * @throws \Exception
135+ */
136+ protected function isValidRecoveryToken (User $ user , string $ value )
137+ {
138+ foreach ($ user ->recoveryTokens as $ token ) {
139+ if (password_verify ($ value , $ token ->token )) {
140+ $ token ->delete ();
141+
142+ return true ;
143+ }
144+ }
145+
146+ return false ;
147+ }
119148}
0 commit comments