MAPG-142 redefine tokens and increase OAuth security with nonce
This commit is contained in:
10 files changed
+63
-32
No files matched your search
@@ -53,13 +53,16 @@ class LoginController
|
||||
public function getGoogleLoginRedirect(): IRedirect
|
||||
{
|
||||
$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('login-google-action')->generateLink()
|
||||
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink(),
|
||||
$nonce
|
||||
);
|
||||
|
||||
return new Redirect($url, IRedirect::TEMPORARY);
|
||||
@@ -214,16 +217,20 @@ class LoginController
|
||||
}
|
||||
|
||||
$jwtParser = new JwtParser($tokenData['id_token']);
|
||||
$userData = $jwtParser->getPayload();
|
||||
$idToken = $jwtParser->getPayload();
|
||||
|
||||
if (!$userData['email_verified']) {
|
||||
if ($idToken['nonce'] !== $this->request->session()->get('oauth_nonce')) {
|
||||
return new HtmlContent('login/google_login');
|
||||
}
|
||||
|
||||
$user = $this->userRepository->getByGoogleSub($userData['sub']);
|
||||
if (!$idToken['email_verified']) {
|
||||
return new HtmlContent('login/google_login');
|
||||
}
|
||||
|
||||
$user = $this->userRepository->getByGoogleSub($idToken['sub']);
|
||||
|
||||
if ($user === null) {
|
||||
$this->request->session()->set('google_user_data', $userData);
|
||||
$this->request->session()->set('google_user_data', ['sub' => $idToken['sub'], 'email' => $idToken['email']]);
|
||||
|
||||
return new Redirect(\Container::$routeCollection->getRoute('signup-google')->generateLink(), IRedirect::TEMPORARY);
|
||||
}
|
||||
|
||||
@@ -59,14 +59,17 @@ class UserController implements ISecured
|
||||
$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()
|
||||
);
|
||||
|
||||
@@ -95,9 +98,13 @@ class UserController implements ISecured
|
||||
}
|
||||
|
||||
$jwtParser = new JwtParser($tokenData['id_token']);
|
||||
$userData = $jwtParser->getPayload();
|
||||
$idToken = $jwtParser->getPayload();
|
||||
|
||||
if ($userData['sub'] !== $user->getGoogleSub()) {
|
||||
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.'
|
||||
|
||||
@@ -135,6 +135,6 @@ class Modify
|
||||
|
||||
private function generateKey(): string
|
||||
{
|
||||
return substr(hash('sha256', serialize($this->attributes) . random_bytes(10) . microtime()), 0, 7);
|
||||
return substr(hash('sha256', serialize($this->attributes) . random_bytes(5) . microtime()), 0, 7);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php namespace MapGuesser\Interfaces\Session;
|
||||
|
||||
use SessionHandlerInterface;
|
||||
use SessionIdInterface;
|
||||
use SessionUpdateTimestampHandlerInterface;
|
||||
|
||||
interface ISessionHandler extends SessionHandlerInterface, SessionIdInterface, SessionUpdateTimestampHandlerInterface
|
||||
{
|
||||
}
|
||||
@@ -15,7 +15,7 @@ class GoogleOAuth
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function getDialogUrl(string $state, string $redirectUrl, ?string $loginHint = null): string
|
||||
public function getDialogUrl(string $state, string $redirectUrl, ?string $nonce = null, ?string $loginHint = null): string
|
||||
{
|
||||
$oauthParams = [
|
||||
'response_type' => 'code',
|
||||
@@ -23,9 +23,12 @@ class GoogleOAuth
|
||||
'scope' => 'openid email',
|
||||
'redirect_uri' => $redirectUrl,
|
||||
'state' => $state,
|
||||
'nonce' => hash('sha256', random_bytes(10) . microtime()),
|
||||
];
|
||||
|
||||
if ($nonce !== null) {
|
||||
$oauthParams['nonce'] = $nonce;
|
||||
}
|
||||
|
||||
if ($loginHint !== null) {
|
||||
$oauthParams['login_hint'] = $loginHint;
|
||||
}
|
||||
|
||||
@@ -4,11 +4,9 @@ use DateTime;
|
||||
use MapGuesser\Database\Query\Modify;
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\Interfaces\Database\IResultSet;
|
||||
use SessionHandlerInterface;
|
||||
use SessionIdInterface;
|
||||
use SessionUpdateTimestampHandlerInterface;
|
||||
use MapGuesser\Interfaces\Session\ISessionHandler;
|
||||
|
||||
class DatabaseSessionHandler implements SessionHandlerInterface, SessionIdInterface, SessionUpdateTimestampHandlerInterface
|
||||
class DatabaseSessionHandler implements ISessionHandler
|
||||
{
|
||||
private bool $exists = false;
|
||||
|
||||
@@ -28,7 +26,7 @@ class DatabaseSessionHandler implements SessionHandlerInterface, SessionIdInterf
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection, 'sessions');
|
||||
$select->columns(['data']);
|
||||
$select->whereId($id);
|
||||
$select->whereId(substr($id, 0, 32));
|
||||
|
||||
$result = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||
|
||||
@@ -46,16 +44,16 @@ class DatabaseSessionHandler implements SessionHandlerInterface, SessionIdInterf
|
||||
$modify = new Modify(\Container::$dbConnection, 'sessions');
|
||||
|
||||
if ($this->exists) {
|
||||
$modify->setId($id);
|
||||
$modify->setId(substr($id, 0, 32));
|
||||
} else {
|
||||
$modify->setExternalId($id);
|
||||
$modify->setExternalId(substr($id, 0, 32));
|
||||
}
|
||||
|
||||
$modify->set('data', $data);
|
||||
$modify->set('updated', (new DateTime())->format('Y-m-d H:i:s'));
|
||||
$modify->save();
|
||||
|
||||
$written = true;
|
||||
$this->written = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -63,9 +61,11 @@ class DatabaseSessionHandler implements SessionHandlerInterface, SessionIdInterf
|
||||
public function destroy($id): bool
|
||||
{
|
||||
$modify = new Modify(\Container::$dbConnection, 'sessions');
|
||||
$modify->setId($id);
|
||||
$modify->setId(substr($id, 0, 32));
|
||||
$modify->delete();
|
||||
|
||||
$this->exists = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,12 +88,12 @@ class DatabaseSessionHandler implements SessionHandlerInterface, SessionIdInterf
|
||||
|
||||
public function create_sid(): string
|
||||
{
|
||||
return hash('sha256', random_bytes(10) . microtime());
|
||||
return bin2hex(random_bytes(16));
|
||||
}
|
||||
|
||||
public function validateId($id): bool
|
||||
{
|
||||
return preg_match('/^[a-f0-9]{64}$/', $id);
|
||||
return preg_match('/^[a-f0-9]{32}$/', $id);
|
||||
}
|
||||
|
||||
public function updateTimestamp($id, $data): bool
|
||||
@@ -104,7 +104,7 @@ class DatabaseSessionHandler implements SessionHandlerInterface, SessionIdInterf
|
||||
|
||||
$modify = new Modify(\Container::$dbConnection, 'sessions');
|
||||
|
||||
$modify->setId($id);
|
||||
$modify->setId(substr($id, 0, 32));
|
||||
$modify->set('updated', (new DateTime())->format('Y-m-d H:i:s'));
|
||||
$modify->save();
|
||||
|
||||
|
||||
Reference in new issue
Block a user