auth = $auth; $this->cache = $cache; $this->config = $config; $this->encrypter = $encrypter; $this->google2FA = $google2FA; $this->repository = $repository; $this->lockoutTime = $this->config->get('auth.lockout.time'); $this->maxLoginAttempts = $this->config->get('auth.lockout.attempts'); } /** * Handle a login request to the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response * * @throws \Illuminate\Validation\ValidationException */ public function login(Request $request) { $username = $request->input(self::USER_INPUT_FIELD); $useColumn = $this->getField($username); if ($this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); $this->sendLockoutResponse($request); } try { $user = $this->repository->findFirstWhere([[$useColumn, '=', $username]]); } catch (RecordNotFoundException $exception) { return $this->sendFailedLoginResponse($request); } $validCredentials = password_verify($request->input('password'), $user->password); if ($user->use_totp) { $token = str_random(64); $this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => $validCredentials], 5); return redirect()->route('auth.totp')->with('authentication_token', $token); } if ($validCredentials) { $this->auth->guard()->login($user, true); return $this->sendLoginResponse($request); } return $this->sendFailedLoginResponse($request, $user); } /** * Handle a TOTP implementation page. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View */ public function totp(Request $request) { $token = $request->session()->get('authentication_token'); if (is_null($token) || $this->auth->guard()->user()) { return redirect()->route('auth.login'); } return view('auth.totp', ['verify_key' => $token]); } /** * Handle a login where the user is required to provide a TOTP authentication * token. In order to add additional layers of security, users are not * informed of an incorrect password until this stage, forcing them to * provide a token on each login attempt. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response */ public function loginUsingTotp(Request $request) { if (is_null($request->input('verify_token'))) { return $this->sendFailedLoginResponse($request); } try { $cache = $this->cache->pull($request->input('verify_token'), []); $user = $this->repository->find(array_get($cache, 'user_id', 0)); } catch (RecordNotFoundException $exception) { return $this->sendFailedLoginResponse($request); } if (is_null($request->input('2fa_token')) || ! array_get($cache, 'valid_credentials')) { return $this->sendFailedLoginResponse($request, $user); } if (! $this->google2FA->verifyKey( $this->encrypter->decrypt($user->totp_secret), $request->input('2fa_token'), $this->config->get('pterodactyl.auth.2fa.window') )) { return $this->sendFailedLoginResponse($request, $user); } $this->auth->guard()->login($user, true); return $this->sendLoginResponse($request); } /** * Get the failed login response instance. * * @param \Illuminate\Http\Request $request * @param \Illuminate\Contracts\Auth\Authenticatable|null $user * @return \Illuminate\Http\RedirectResponse */ protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null): RedirectResponse { $this->incrementLoginAttempts($request); $this->fireFailedLoginEvent($user, [ $this->getField($request->input(self::USER_INPUT_FIELD)) => $request->input(self::USER_INPUT_FIELD), ]); $errors = [self::USER_INPUT_FIELD => trans('auth.failed')]; if ($request->expectsJson()) { return response()->json($errors, 422); } return redirect()->route('auth.login') ->withInput($request->only(self::USER_INPUT_FIELD)) ->withErrors($errors); } /** * Determine if the user is logging in using an email or username,. * * @param string $input * @return string */ private function getField(string $input = null): string { return str_contains($input, '@') ? 'email' : 'username'; } /** * Fire a failed login event. * * @param \Illuminate\Contracts\Auth\Authenticatable|null $user * @param array $credentials */ private function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = []) { event(new Failed(config('auth.defaults.guard'), $user, $credentials)); } }