1111use Illuminate \Contracts \Cache \Repository as CacheRepository ;
1212use Pterodactyl \Contracts \Repository \UserRepositoryInterface ;
1313use Pterodactyl \Exceptions \Repository \RecordNotFoundException ;
14+ use Pterodactyl \Repositories \Eloquent \RecoveryTokenRepository ;
1415
1516class LoginCheckpointController extends AbstractLoginController
1617{
@@ -34,6 +35,11 @@ class LoginCheckpointController extends AbstractLoginController
3435 */
3536 private $ encrypter ;
3637
38+ /**
39+ * @var \Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository
40+ */
41+ private $ recoveryTokenRepository ;
42+
3743 /**
3844 * LoginCheckpointController constructor.
3945 *
@@ -42,6 +48,7 @@ class LoginCheckpointController extends AbstractLoginController
4248 * @param \PragmaRX\Google2FA\Google2FA $google2FA
4349 * @param \Illuminate\Contracts\Config\Repository $config
4450 * @param \Illuminate\Contracts\Cache\Repository $cache
51+ * @param \Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository $recoveryTokenRepository
4552 * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
4653 */
4754 public function __construct (
@@ -50,6 +57,7 @@ public function __construct(
5057 Google2FA $ google2FA ,
5158 Repository $ config ,
5259 CacheRepository $ cache ,
60+ RecoveryTokenRepository $ recoveryTokenRepository ,
5361 UserRepositoryInterface $ repository
5462 ) {
5563 parent ::__construct ($ auth , $ config );
@@ -58,6 +66,7 @@ public function __construct(
5866 $ this ->cache = $ cache ;
5967 $ this ->repository = $ repository ;
6068 $ this ->encrypter = $ encrypter ;
69+ $ this ->recoveryTokenRepository = $ recoveryTokenRepository ;
6170 }
6271
6372 /**
@@ -76,21 +85,35 @@ public function __construct(
7685 public function __invoke (LoginCheckpointRequest $ request ): JsonResponse
7786 {
7887 $ token = $ request ->input ('confirmation_token ' );
88+ $ recoveryToken = $ request ->input ('recovery_token ' );
7989
8090 try {
91+ /** @var \Pterodactyl\Models\User $user */
8192 $ user = $ this ->repository ->find ($ this ->cache ->get ($ token , 0 ));
8293 } catch (RecordNotFoundException $ exception ) {
83- return $ this ->sendFailedLoginResponse ($ request );
94+ return $ this ->sendFailedLoginResponse ($ request, null , ' The authentication token provided has expired, please refresh the page and try again. ' );
8495 }
8596
86- $ decrypted = $ this ->encrypter ->decrypt ($ user ->totp_secret );
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 );
103+
104+ return $ this ->sendLoginResponse ($ user , $ request );
105+ }
106+ }
107+ } else {
108+ $ decrypted = $ this ->encrypter ->decrypt ($ user ->totp_secret );
87109
88- if ($ this ->google2FA ->verifyKey ($ decrypted , (string ) $ request ->input ('authentication_code ' ) ?? '' , config ('pterodactyl.auth.2fa.window ' ))) {
89- $ this ->cache ->delete ($ token );
110+ if ($ this ->google2FA ->verifyKey ($ decrypted , (string ) $ request ->input ('authentication_code ' ) ?? '' , config ('pterodactyl.auth.2fa.window ' ))) {
111+ $ this ->cache ->delete ($ token );
90112
91- return $ this ->sendLoginResponse ($ user , $ request );
113+ return $ this ->sendLoginResponse ($ user , $ request );
114+ }
92115 }
93116
94- return $ this ->sendFailedLoginResponse ($ request , $ user );
117+ return $ this ->sendFailedLoginResponse ($ request , $ user, ! empty ( $ recoveryToken ) ? ' The recovery token provided is not valid. ' : null );
95118 }
96119}
0 commit comments