$this->getRequest()->getQueryParams(), ], ); if (!$this->isVerifyAllowed()) { return $this->redirect($loginAction); } $temporarySession = $this->getRequest()->getSession()->read( AuthenticationService::TWO_FACTOR_VERIFY_SESSION_KEY, ); $secretVerified = $temporarySession['secret_verified'] ?? null; // showing QR-code until shared secret is verified if (!$secretVerified) { $secret = $this->onVerifyGetSecret($temporarySession); if (empty($secret)) { return $this->redirect($loginAction); } $secretDataUri = $this->OneTimePasswordAuthenticator->getQRCodeImageAsDataUri( $temporarySession['email'], $secret, ); $this->set(['secretDataUri' => $secretDataUri]); } if ($this->getRequest()->is('post')) { return $this->onPostVerifyCode($loginAction); } } /** * Check If Google Authenticator's enabled we need to verify * authenticated user and if temporySession is present * * @return bool */ protected function isVerifyAllowed() { if (!Configure::read('OneTimePasswordAuthenticator.login')) { $message = __d('cake_d_c/users', 'Please enable Google Authenticator first.'); $this->Flash->error($message, [ 'key' => 'auth', 'element' => 'default', 'params' => [], ]); return false; } $temporarySession = $this->getRequest()->getSession()->read( AuthenticationService::TWO_FACTOR_VERIFY_SESSION_KEY, ); if (empty($temporarySession) || !isset($temporarySession['id'])) { $message = __d('cake_d_c/users', 'Could not find user data'); $this->Flash->error($message, [ 'key' => 'auth', 'element' => 'default', 'params' => [], ]); return false; } return true; } /** * Get the Google Authenticator secret of user, if not exists try to create one and save * * @param \CakeDC\Users\Model\Entity\User $user user data present on session * @return string if empty the creation has failed */ protected function onVerifyGetSecret($user) { if (isset($user['secret']) && $user['secret']) { return $user['secret']; } $secret = $this->OneTimePasswordAuthenticator->createSecret(); // catching sql exception in case of any sql inconsistencies try { $query = $this->getUsersTable() ->updateQuery() ->set(['secret' => $secret]) ->where(['id' => $user['id']]); $query->execute(); $user['secret'] = $secret; $this->getRequest()->getSession()->write(AuthenticationService::TWO_FACTOR_VERIFY_SESSION_KEY, $user); } catch (\Exception $e) { $this->getRequest()->getSession()->destroy(); $this->log($e); $message = __d('cake_d_c/users', 'Could not verify, please try again'); $this->Flash->error($message, [ 'key' => 'auth', 'element' => 'default', 'params' => [], ]); return ''; } return $secret; } /** * Handle the action when user post the form with code * * @param array $loginAction url to login page used in redirect * @return \Cake\Http\Response */ protected function onPostVerifyCode($loginAction) { $codeVerified = false; $verificationCode = $this->getRequest()->getData('code'); $user = $this->getRequest()->getSession()->read(AuthenticationService::TWO_FACTOR_VERIFY_SESSION_KEY); $entity = $this->getUsersTable()->get($user['id']); if (!empty($entity['secret'])) { $codeVerified = $this->OneTimePasswordAuthenticator->verifyCode($entity['secret'], $verificationCode); } if (!$codeVerified) { $this->getRequest()->getSession()->destroy(); $message = __d('cake_d_c/users', 'Verification code is invalid. Try again'); $this->Flash->error($message, [ 'key' => 'auth', 'element' => 'default', 'params' => [], ]); return $this->redirect($loginAction); } return $this->onPostVerifyCodeOkay($loginAction, $user); } /** * Handle the part of action when user post the form with valid code * * @param array $loginAction url to login page used in redirect * @param \CakeDC\Users\Model\Entity\User $user user data present on session * @return \Cake\Http\Response */ protected function onPostVerifyCodeOkay($loginAction, $user) { unset($user['secret']); if (!$user['secret_verified']) { $this->getUsersTable() ->updateQuery() ->set(['secret_verified' => true]) ->where(['id' => $user['id']]) ->execute(); } $this->getRequest()->getSession()->delete(AuthenticationService::TWO_FACTOR_VERIFY_SESSION_KEY); $this->getRequest()->getSession()->write(TwoFactorAuthenticator::USER_SESSION_KEY, $user); return $this->redirect($loginAction); } }