Login up with Google
+Authenticating with Google failed. Please retry!
+diff --git a/mail/signup-noconfirm.html b/mail/signup-noconfirm.html
new file mode 100644
index 0000000..8c00ae0
--- /dev/null
+++ b/mail/signup-noconfirm.html
@@ -0,0 +1,9 @@
+Hi,
+
+You recently signed up on MapGuesser with this Google account ({{EMAIL}}).
+
+Have fun on MapGuesser!
+
+Regards,
+MapGuesser
+{{BASE_URL}}
diff --git a/mail/signup.html b/mail/signup.html
index 480e0c1..629d774 100644
--- a/mail/signup.html
+++ b/mail/signup.html
@@ -10,4 +10,5 @@ However if you want to immediately delete it, please click on the following link
Have fun on MapGuesser!
Regards,
-MapGuesser
+MapGuesser
+{{BASE_URL}}
diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css
index c492c19..0663d22 100644
--- a/public/static/css/mapguesser.css
+++ b/public/static/css/mapguesser.css
@@ -107,6 +107,10 @@ hr {
margin-right: 10px;
}
+.center {
+ text-align: center;
+}
+
.right {
text-align: right;
}
diff --git a/src/Controller/LoginController.php b/src/Controller/LoginController.php
index f83633d..be27d43 100644
--- a/src/Controller/LoginController.php
+++ b/src/Controller/LoginController.php
@@ -3,31 +3,84 @@
use MapGuesser\Interfaces\Request\IRequest;
use MapGuesser\Interfaces\Response\IContent;
use MapGuesser\Interfaces\Response\IRedirect;
+use MapGuesser\Mailing\Mail;
+use MapGuesser\OAuth\GoogleOAuth;
+use MapGuesser\PersistentData\Model\User;
+use MapGuesser\PersistentData\Model\UserConfirmation;
+use MapGuesser\PersistentData\PersistentDataManager;
+use MapGuesser\Repository\UserConfirmationRepository;
use MapGuesser\Repository\UserRepository;
use MapGuesser\Response\HtmlContent;
use MapGuesser\Response\JsonContent;
use MapGuesser\Response\Redirect;
+use MapGuesser\Util\JwtParser;
class LoginController
{
private IRequest $request;
+ private PersistentDataManager $pdm;
+
private UserRepository $userRepository;
+ private UserConfirmationRepository $userConfirmationRepository;
+
public function __construct(IRequest $request)
{
$this->request = $request;
+ $this->pdm = new PersistentDataManager();
$this->userRepository = new UserRepository();
+ $this->userConfirmationRepository = new UserConfirmationRepository();
}
public function getLoginForm()
{
if ($this->request->user() !== null) {
- return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
}
$data = [];
- return new HtmlContent('login', $data);
+ return new HtmlContent('login/login', $data);
+ }
+
+ public function getGoogleLoginRedirect()
+ {
+ $state = bin2hex(random_bytes(16));
+
+ $this->request->session()->set('oauth_state', $state);
+
+ $oAuth = new GoogleOAuth();
+ $url = $oAuth->getDialogUrl($state, $this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink());
+
+ return new Redirect($url, IRedirect::TEMPORARY);
+ }
+
+ public function getSignupForm()
+ {
+ if ($this->request->user() !== null) {
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ $data = [];
+ return new HtmlContent('login/signup', $data);
+ }
+
+ public function getSignupWithGoogleForm()
+ {
+ if ($this->request->user() !== null) {
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ if (!$this->request->session()->has('google_user_data')) {
+ return new Redirect(\Container::$routeCollection->getRoute('login-google')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ $userData = $this->request->session()->get('google_user_data');
+
+ $user = $this->userRepository->getByEmail($userData['email']);
+
+ $data = ['found' => $user !== null, 'email' => $userData['email']];
+ return new HtmlContent('login/google_signup', $data);
}
public function login(): IContent
@@ -60,10 +113,219 @@ class LoginController
return new JsonContent($data);
}
+ public function loginWithGoogle()
+ {
+ if ($this->request->user() !== null) {
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ if ($this->request->query('state') !== $this->request->session()->get('oauth_state')) {
+ $data = [];
+ return new HtmlContent('login/google_login', $data);
+ }
+
+ $oAuth = new GoogleOAuth();
+ $tokenData = $oAuth->getToken($this->request->query('code'), $this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink());
+
+ if (!isset($tokenData['id_token'])) {
+ $data = [];
+ return new HtmlContent('login/google_login', $data);
+ }
+
+ $jwtParser = new JwtParser($tokenData['id_token']);
+ $userData = $jwtParser->getPayload();
+
+ if (!$userData['email_verified']) {
+ $data = [];
+ return new HtmlContent('login/google_login', $data);
+ }
+
+ $user = $this->userRepository->getByGoogleSub($userData['sub']);
+
+ if ($user === null) {
+ $this->request->session()->set('google_user_data', $userData);
+
+ return new Redirect(\Container::$routeCollection->getRoute('signup-google')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ $this->request->setUser($user);
+
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
public function logout(): IRedirect
{
$this->request->setUser(null);
- return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ public function signup(): IContent
+ {
+ if ($this->request->user() !== null) {
+ $data = ['error' => 'logged_in'];
+ return new JsonContent($data);
+ }
+
+ if (filter_var($this->request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
+ $data = ['error' => 'email_not_valid'];
+ return new JsonContent($data);
+ }
+
+ $user = $this->userRepository->getByEmail($this->request->post('email'));
+
+ if ($user !== null) {
+ if ($user->getActive()) {
+ $data = ['error' => 'user_found'];
+ } else {
+ $data = ['error' => 'not_active_user_found'];
+ }
+ return new JsonContent($data);
+ }
+
+ if (strlen($this->request->post('password')) < 6) {
+ $data = ['error' => 'passwords_too_short'];
+ return new JsonContent($data);
+ }
+
+ if ($this->request->post('password') !== $this->request->post('password_confirm')) {
+ $data = ['error' => 'passwords_not_match'];
+ return new JsonContent($data);
+ }
+
+ $user = new User();
+ $user->setEmail($this->request->post('email'));
+ $user->setPlainPassword($this->request->post('password'));
+
+ \Container::$dbConnection->startTransaction();
+
+ $this->pdm->saveToDb($user);
+
+ $token = hash('sha256', serialize($user) . random_bytes(10) . microtime());
+
+ $confirmation = new UserConfirmation();
+ $confirmation->setUser($user);
+ $confirmation->setToken($token);
+
+ $this->pdm->saveToDb($confirmation);
+
+ \Container::$dbConnection->commit();
+
+ $this->sendConfirmationEmail($user->getEmail(), $token);
+
+ $data = ['success' => true];
+ return new JsonContent($data);
+ }
+
+ public function signupWithGoogle()
+ {
+ if ($this->request->user() !== null) {
+ $data = ['success' => true];
+ return new JsonContent($data);
+ }
+
+ $userData = $this->request->session()->get('google_user_data');
+
+ $user = $this->userRepository->getByEmail($userData['email']);
+
+ if ($user === null) {
+ $user = new User();
+ $user->setEmail($userData['email']);
+ }
+
+ $user->setActive(true);
+ $user->setGoogleSub($userData['sub']);
+
+ $this->pdm->saveToDb($user);
+
+ $this->sendWelcomeEmail($user->getEmail());
+
+ $this->request->session()->delete('google_user_data');
+ $this->request->setUser($user);
+
+ $data = ['success' => true];
+ return new JsonContent($data);
+ }
+
+ public function activate()
+ {
+ if ($this->request->user() !== null) {
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ $confirmation = $this->userConfirmationRepository->getByToken($this->request->query('token'));
+
+ if ($confirmation === null) {
+ $data = [];
+ return new HtmlContent('login/activate', $data);
+ }
+
+ \Container::$dbConnection->startTransaction();
+
+ $this->pdm->deleteFromDb($confirmation);
+
+ $user = $this->userRepository->getById($confirmation->getUserId());
+ $user->setActive(true);
+
+ $this->pdm->saveToDb($user);
+
+ \Container::$dbConnection->commit();
+
+ $this->request->setUser($user);
+
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ public function cancel()
+ {
+ if ($this->request->user() !== null) {
+ return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
+ }
+
+ $confirmation = $this->userConfirmationRepository->getByToken($this->request->query('token'));
+
+ if ($confirmation === null) {
+ $data = ['success' => false];
+ return new HtmlContent('login/cancel', $data);
+ }
+
+ \Container::$dbConnection->startTransaction();
+
+ $this->pdm->deleteFromDb($confirmation);
+
+ $user = $this->userRepository->getById($confirmation->getUserId());
+
+ $this->pdm->deleteFromDb($user);
+
+ \Container::$dbConnection->commit();
+
+ $data = ['success' => true];
+ return new HtmlContent('login/cancel', $data);
+ }
+
+ private function sendConfirmationEmail(string $email, string $token): void
+ {
+ $mail = new Mail();
+ $mail->addRecipient($email);
+ $mail->setSubject('Welcome to MapGuesser - Activate your account');
+ $mail->setBodyFromTemplate('signup', [
+ 'EMAIL' => $email,
+ 'ACTIVATE_LINK' => $this->request->getBase() . '/'. \Container::$routeCollection->getRoute('signup.activate')->generateLink(['token' => $token]),
+ 'CANCEL_LINK' => $this->request->getBase() . '/' . \Container::$routeCollection->getRoute('signup.cancel')->generateLink(['token' => $token]),
+ 'BASE_URL' => $this->request->getBase(),
+ ]);
+ $mail->send();
+ }
+
+ private function sendWelcomeEmail(string $email): void
+ {
+ $mail = new Mail();
+ $mail->addRecipient($email);
+ $mail->setSubject('Welcome to MapGuesser');
+ $mail->setBodyFromTemplate('signup-noconfirm', [
+ 'EMAIL' => $email,
+ 'BASE_URL' => $this->request->getBase(),
+ ]);
+ $mail->send();
}
}
diff --git a/src/Controller/SignupController.php b/src/Controller/SignupController.php
deleted file mode 100644
index 77b3197..0000000
--- a/src/Controller/SignupController.php
+++ /dev/null
@@ -1,170 +0,0 @@
-request = $request;
- $this->pdm = new PersistentDataManager();
- $this->userRepository = new UserRepository();
- $this->userConfirmationRepository = new UserConfirmationRepository();
- }
-
- public function getSignupForm()
- {
- if ($this->request->user() !== null) {
- return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
- }
-
- $data = [];
- return new HtmlContent('signup/signup', $data);
- }
-
- public function signup(): IContent
- {
- if ($this->request->user() !== null) {
- //TODO: return with some error
- $data = ['success' => true];
- return new JsonContent($data);
- }
-
- if (filter_var($this->request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
- $data = ['error' => 'email_not_valid'];
- return new JsonContent($data);
- }
-
- $user = $this->userRepository->getByEmail($this->request->post('email'));
-
- if ($user !== null) {
- if ($user->getActive()) {
- $data = ['error' => 'user_found'];
- } else {
- $data = ['error' => 'not_active_user_found'];
- }
- return new JsonContent($data);
- }
-
- if (strlen($this->request->post('password')) < 6) {
- $data = ['error' => 'passwords_too_short'];
- return new JsonContent($data);
- }
-
- if ($this->request->post('password') !== $this->request->post('password_confirm')) {
- $data = ['error' => 'passwords_not_match'];
- return new JsonContent($data);
- }
-
- $user = new User();
- $user->setEmail($this->request->post('email'));
- $user->setPlainPassword($this->request->post('password'));
-
- \Container::$dbConnection->startTransaction();
-
- $this->pdm->saveToDb($user);
-
- $token = hash('sha256', serialize($user) . random_bytes(10) . microtime());
-
- $confirmation = new UserConfirmation();
- $confirmation->setUser($user);
- $confirmation->setToken($token);
-
- $this->pdm->saveToDb($confirmation);
-
- \Container::$dbConnection->commit();
-
- $this->sendConfirmationEmail($user->getEmail(), $token);
-
- $data = ['success' => true];
- return new JsonContent($data);
- }
-
- public function activate()
- {
- if ($this->request->user() !== null) {
- return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
- }
-
- $confirmation = $this->userConfirmationRepository->getByToken($this->request->query('token'));
-
- if ($confirmation === null) {
- $data = [];
- return new HtmlContent('signup/activate', $data);
- }
-
- \Container::$dbConnection->startTransaction();
-
- $this->pdm->deleteFromDb($confirmation);
-
- $user = $this->userRepository->getById($confirmation->getUserId());
- $user->setActive(true);
-
- $this->pdm->saveToDb($user);
-
- \Container::$dbConnection->commit();
-
- $this->request->setUser($user);
-
- return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
- }
-
- public function cancel()
- {
- if ($this->request->user() !== null) {
- return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
- }
-
- $confirmation = $this->userConfirmationRepository->getByToken($this->request->query('token'));
-
- if ($confirmation === null) {
- $data = ['success' => false];
- return new HtmlContent('signup/cancel', $data);
- }
-
- \Container::$dbConnection->startTransaction();
-
- $this->pdm->deleteFromDb($confirmation);
-
- $user = $this->userRepository->getById($confirmation->getUserId());
-
- $this->pdm->deleteFromDb($user);
-
- \Container::$dbConnection->commit();
-
- $data = ['success' => true];
- return new HtmlContent('signup/cancel', $data);
- }
-
- private function sendConfirmationEmail($email, $token): void
- {
- $mail = new Mail();
- $mail->addRecipient($email);
- $mail->setSubject('Welcome to MapGuesser - Activate your account');
- $mail->setBodyFromTemplate('signup', [
- 'EMAIL' => $email,
- 'ACTIVATE_LINK' => $this->request->getBase() . '/signup/activate/' . $token,
- 'CANCEL_LINK' => $this->request->getBase() . '/signup/cancel/' . $token,
- ]);
- $mail->send();
- }
-}
diff --git a/views/signup/activate.php b/views/login/activate.php
similarity index 100%
rename from views/signup/activate.php
rename to views/login/activate.php
diff --git a/views/signup/cancel.php b/views/login/cancel.php
similarity index 100%
rename from views/signup/cancel.php
rename to views/login/cancel.php
diff --git a/views/login/google_login.php b/views/login/google_login.php
new file mode 100644
index 0000000..334857b
--- /dev/null
+++ b/views/login/google_login.php
@@ -0,0 +1,9 @@
+
+
+
Authenticating with Google failed. Please retry!
+