request = $request; $this->pdm = new PersistentDataManager(); $this->userRepository = new UserRepository(); } public function isAuthenticationRequired(): bool { return true; } public function getAccount(): IContent { /** * @var User $user */ $user = $this->request->user(); return new HtmlContent('account/account', ['user' => $user->toArray()]); } public function getGoogleAuthenticateRedirect(): IRedirect { /** * @var User $user */ $user = $this->request->user(); $state = bin2hex(random_bytes(16)); $nonce = bin2hex(random_bytes(16)); $this->request->session()->set('oauth_state', $state); $this->request->session()->set('oauth_nonce', $nonce); $oAuth = new GoogleOAuth(new Request()); $url = $oAuth->getDialogUrl( $state, $this->request->getBase() . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink(), $nonce, $user->getEmail() ); return new Redirect($url, IRedirect::TEMPORARY); } public function authenticateWithGoogle(): IContent { /** * @var User $user */ $user = $this->request->user(); if ($this->request->query('state') !== $this->request->session()->get('oauth_state')) { return new HtmlContent('account/google_authenticate', ['success' => false]); } $oAuth = new GoogleOAuth(new Request()); $tokenData = $oAuth->getToken( $this->request->query('code'), $this->request->getBase() . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink() ); if (!isset($tokenData['id_token'])) { return new HtmlContent('account/google_authenticate', ['success' => false]); } $jwtParser = new JwtParser($tokenData['id_token']); $idToken = $jwtParser->getPayload(); if ($idToken['nonce'] !== $this->request->session()->get('oauth_nonce')) { return new HtmlContent('account/google_authenticate', ['success' => false]); } if ($idToken['sub'] !== $user->getGoogleSub()) { return new HtmlContent('account/google_authenticate', [ 'success' => false, 'errorText' => 'This Google account is not linked to your account.' ]); } $authenticatedWithGoogleUntil = new DateTime('+45 seconds'); $this->request->session()->set('authenticated_with_google_until', $authenticatedWithGoogleUntil); return new HtmlContent('account/google_authenticate', [ 'success' => true, 'authenticatedWithGoogleUntil' => $authenticatedWithGoogleUntil ]); } public function saveAccount(): IContent { /** * @var User $user */ $user = $this->request->user(); if (!$this->confirmUserIdentity( $user, $this->request->session()->get('authenticated_with_google_until'), $this->request->post('password'), $error )) { return new JsonContent(['error' => ['errorText' => $error]]); } $newEmail = $this->request->post('email'); if ($newEmail !== $user->getEmail()) { if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) { return new JsonContent(['error' => ['errorText' => 'Please provide a valid email address.']]); } if ($this->userRepository->getByEmail($newEmail) !== null) { return new JsonContent(['error' => ['errorText' => 'The given email address belongs to another account.']]); } $user->setEmail($newEmail); } $newUsername = $this->request->post('username'); if ($newUsername !== $user->getUsername()) { if (strlen($newUsername) > 0) { if (filter_var($newUsername, FILTER_VALIDATE_EMAIL)) { return new JsonContent(['error' => ['errorText' => 'Please select a username that is not a valid email address.']]); } if ($this->userRepository->getByUsername($newUsername) !== null) { return new JsonContent(['error' => ['errorText' => 'The given username is already taken.']]); } $user->setUsername($newUsername); } else { $user->setUsername(null); } } $newPassword = $this->request->post('password_new'); if (strlen($newPassword) > 0) { if (strlen($newPassword) < 6) { return new JsonContent([ 'error' => [ 'errorText' => 'The given new password is too short. Please choose a password that is at least 6 characters long!' ] ]); } if ($newPassword !== $this->request->post('password_new_confirm')) { return new JsonContent([ 'error' => [ 'errorText' => 'The given new passwords do not match.' ] ]); } $user->setPlainPassword($newPassword); } $user->setNickname($this->request->post('nickname')); $user->setPhone($this->request->post('phone')); $user->setIdNumber($this->request->post('id_number')); $this->pdm->saveToDb($user); $this->request->session()->delete('authenticated_with_google_until'); return new JsonContent(['success' => true]); } private function confirmUserIdentity(User $user, ?DateTime $authenticatedWithGoogleUntil, ?string $password, ?string &$error): bool { if ($authenticatedWithGoogleUntil !== null && $authenticatedWithGoogleUntil > new DateTime()) { return true; } if ($password !== null) { if ($user->checkPassword($password)) { return true; } $error = 'The given current password is wrong.'; return false; } $error = 'Could not confirm your identity. Please try again!'; return false; } }