diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 4b825d0..ea0f86c 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -57,6 +57,130 @@ class UserController implements IAuthenticationRequired return new HtmlContent('account/account', ['user' => $user->toArray()]); } + public function getGoogleConnectRedirect(): IRedirect + { + /** + * @var User $user + */ + $user = \Container::$request->user(); + + $state = bin2hex(random_bytes(16)); + $nonce = bin2hex(random_bytes(16)); + + \Container::$request->session()->set('oauth_state', $state); + \Container::$request->session()->set('oauth_nonce', $nonce); + + $oAuth = new GoogleOAuth(new Request()); + + $url = $oAuth->getDialogUrl( + $state, + \Container::$request->getBase() . \Container::$routeCollection->getRoute('account.googleConnect-confirm')->generateLink(), + $nonce, + $user->getEmail() + ); + + return new Redirect($url, IRedirect::TEMPORARY); + } + + public function getGoogleConnectConfirm(): IContent + { + $defaultError = 'Authentication with Google failed. Please try again!'; + + if (\Container::$request->query('state') !== \Container::$request->session()->get('oauth_state')) { + return new HtmlContent('account/google_connect', ['success' => false, 'error' => $defaultError]); + } + + $oAuth = new GoogleOAuth(new Request()); + $tokenData = $oAuth->getToken( + \Container::$request->query('code'), + \Container::$request->getBase() . \Container::$routeCollection->getRoute('account.googleConnect-confirm')->generateLink() + ); + if (!isset($tokenData['id_token'])) { + return new HtmlContent('account/google_connect', ['success' => false, 'error' => $defaultError]); + } + + $jwtParser = new JwtParser($tokenData['id_token']); + $idToken = $jwtParser->getPayload(); + if ($idToken['nonce'] !== \Container::$request->session()->get('oauth_nonce')) { + return new HtmlContent('account/google_connect', ['success' => false, 'error' => $defaultError]); + } + + $anotherUser = $this->userRepository->getByGoogleSub($idToken['sub']); + if ($anotherUser !== null) { + return new HtmlContent('account/google_connect', [ + 'success' => false, + 'error' => 'This Google account is linked to another account.' + ]); + } + + \Container::$request->session()->set('google_user_data', $idToken); + + /** + * @var User $user + */ + $user = \Container::$request->user(); + + return new HtmlContent('account/google_connect', [ + 'success' => true, + 'googleAccount' => $idToken['email'], + 'userEmail' => $user->getEmail() + ]); + } + + public function connectGoogle(): IContent + { + /** + * @var User $user + */ + $user = \Container::$request->user(); + if (!$user->checkPassword(\Container::$request->post('password'))) { + return new JsonContent([ + 'error' => [ + 'errorText' => 'The given password is wrong.' + ] + ]); + } + + $googleUserData = \Container::$request->session()->get('google_user_data'); + $user->setGoogleSub($googleUserData['sub']); + \Container::$persistentDataManager->saveToDb($user); + + return new JsonContent(['success' => true]); + } + + public function getGoogleDisconnectConfirm(): IContent + { + /** + * @var User $user + */ + $user = \Container::$request->user(); + + return new HtmlContent('account/google_disconnect', [ + 'success' => true, + 'userEmail' => $user->getEmail() + ]); + } + + public function disconnectGoogle(): IContent + { + /** + * @var User $user + */ + $user = \Container::$request->user(); + if (!$user->checkPassword(\Container::$request->post('password'))) { + return new JsonContent([ + 'error' => [ + 'errorText' => 'The given password is wrong.' + ] + ]); + } + + $user->setGoogleSub(null); + \Container::$persistentDataManager->saveToDb($user); + + return new JsonContent(['success' => true]); + } + public function getGoogleAuthenticateRedirect(): IRedirect { /** diff --git a/views/account/account.php b/views/account/account.php index e21ae64..b8bc188 100644 --- a/views/account/account.php +++ b/views/account/account.php @@ -33,6 +33,13 @@
+ + Connect with Google + + + Disconnect from Google + + Delete account
diff --git a/views/account/google_connect.php b/views/account/google_connect.php new file mode 100644 index 0000000..1779e20 --- /dev/null +++ b/views/account/google_connect.php @@ -0,0 +1,22 @@ +@extends(templates/layout_normal) + +@section(main) +

Connect with Google

+
+ +

+ +
+

Your account will be connected with the following Google account:

+ +

Password

+ +

+
+ Cancel +
+
+ +
+@endsection diff --git a/views/account/google_disconnect.php b/views/account/google_disconnect.php new file mode 100644 index 0000000..8e383e9 --- /dev/null +++ b/views/account/google_disconnect.php @@ -0,0 +1,18 @@ +@extends(templates/layout_normal) + +@section(main) +

Disconnect from Google

+
+
+

Your account will be disconnected from the currently set Google account.

+ +

Password

+ +

+
+ Cancel +
+
+
+@endsection diff --git a/web.php b/web.php index c557cfc..33a1b10 100644 --- a/web.php +++ b/web.php @@ -57,6 +57,11 @@ Container::$routeCollection->group('account', function (RouteCollection $routeCo $routeCollection->post('account-action', '', [UserController::class, 'saveAccount']); $routeCollection->get('account.delete', 'delete', [UserController::class, 'getDeleteAccount']); $routeCollection->post('account.delete-action', 'delete', [UserController::class, 'deleteAccount']); + $routeCollection->get('account.googleConnect', 'googleConnect', [UserController::class, 'getGoogleConnectRedirect']); + $routeCollection->get('account.googleConnect-confirm', 'googleConnect/code', [UserController::class, 'getGoogleConnectConfirm']); + $routeCollection->post('account.googleConnect-action', 'googleConnect', [UserController::class, 'connectGoogle']); + $routeCollection->get('account.googleDisconnect', 'googleDisconnect', [UserController::class, 'getGoogleDisconnectConfirm']); + $routeCollection->post('account.googleDisconnect-action', 'googleDisconnect', [UserController::class, 'disconnectGoogle']); $routeCollection->get('account.googleAuthenticate', 'googleAuthenticate', [UserController::class, 'getGoogleAuthenticateRedirect']); $routeCollection->get('account.googleAuthenticate-action', 'googleAuthenticate/code', [UserController::class, 'authenticateWithGoogle']); });