Skip to content

Feature/login post plugin #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5bdd28f
Update GoogleAuthenticatorService.php
kaplansin Dec 5, 2023
461316a
Update TfaFrontendCheck.php
kaplansin Dec 5, 2023
cee01b4
Update composer.json
kaplansin Dec 5, 2023
632da4f
Update GoogleAuthenticatorService.php
kaplansin Dec 5, 2023
fe62035
Update nl_NL.csv
kaplansin Dec 11, 2023
3161b59
Merge pull request #1 from YouweGit/update-translation
kaplansin Dec 11, 2023
30c9905
return last url and clear 2fa session after logout
kaplansin Dec 12, 2023
ba35d10
Merge pull request #2 from YouweGit/feature/return-last-url
kaplansin Dec 12, 2023
1ef14ee
Update composer.json
kaplansin Dec 12, 2023
cf98320
redirect last page
kaplansin Dec 13, 2023
0f5e529
add cancel button to 2fa setting page
kaplansin Dec 13, 2023
f276bb0
update token title
kaplansin Dec 13, 2023
0fd2b3d
Merge pull request #3 from YouweGit/feature/redirect-last-page
kaplansin Dec 13, 2023
528a16c
allow nullable redirect url
kaplansin Dec 13, 2023
3b169b2
Merge pull request #4 from YouweGit/bug/allow-nullable-redirect-url
kaplansin Dec 13, 2023
1bda31d
add cancel button
kaplansin Dec 14, 2023
badbffc
format cancel class
kaplansin Dec 14, 2023
f45ec42
Merge pull request #5 from YouweGit/bug/fix-account-redirect
kaplansin Dec 14, 2023
f8e4c51
cancel button margin
kaplansin Dec 14, 2023
534b9b2
Merge pull request #6 from YouweGit/feature/cancel-button-style
kaplansin Dec 14, 2023
9937119
make cancel button optional
kaplansin Dec 18, 2023
9cdc171
refactor
kaplansin Dec 18, 2023
cf83ee2
Merge pull request #7 from YouweGit/feature/hide-cancel-if-forced
kaplansin Dec 18, 2023
e46b2ea
cancel 2fa auth and logout
kaplansin Dec 19, 2023
e9150fb
Merge pull request #8 from YouweGit/feature/cancel-2fa-auth
kaplansin Dec 19, 2023
6053dd3
login redirect
kaplansin Dec 26, 2023
ff32f36
Merge pull request #9 from YouweGit/feature/login-redirect
kaplansin Dec 26, 2023
b4fb22b
add logger to 2fa observer
kaplansin Dec 26, 2023
7f56484
Merge pull request #10 from YouweGit/feature/log-2fa-observer
kaplansin Dec 26, 2023
c4c16c1
set redirect url before 2fa
kaplansin Dec 26, 2023
21d7605
Merge pull request #11 from YouweGit/feature/redirect-url-after-login
kaplansin Dec 26, 2023
20a9e90
revert changes use only controller dispatch event obsever
kaplansin Dec 26, 2023
30846c9
Merge pull request #12 from YouweGit/feature/revert-changes
kaplansin Dec 26, 2023
2706d71
cutomer login event added
kaplansin Dec 26, 2023
979cfe7
Merge pull request #13 from YouweGit/feature/customer-login-event
kaplansin Dec 26, 2023
67945fb
change route_id to customer
kaplansin Dec 28, 2023
968392e
Merge pull request #14 from YouweGit/feature/change-route-id
kaplansin Dec 28, 2023
d111309
login post plugin added for redirect to 2fa auth
kaplansin Dec 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Api/ConfigInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Elgentos\Frontend2FA\Api;

use Magento\Framework\App\Config\ScopeConfigInterface;

interface ConfigInterface
{
public function isEnabled(): bool;
public function getForced2faCustomerGroups(): array;
}
24 changes: 24 additions & 0 deletions Api/TfaCheckInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Elgentos\Frontend2FA\Api;

use Magento\Framework\App\Config\ScopeConfigInterface;

interface TfaCheckInterface
{

const FRONTEND_2_FA_ACCOUNT_SETUP_ROUTE = 'customer_account_setup';
const FRONTEND_2_FA_ACCOUNT_AUTHENTICATE_ROUTE = 'customer_account_authenticate';
const CUSTOMER_ACCOUNT_LOGOUT_ROUTE = 'customer_account_logout';
const FRONTEND_2_FA_ACCOUNT_SETUP_PATH = 'customer/account/setup';
const FRONTEND_2_FA_ACCOUNT_AUTHENTICATE_PATH = 'customer/account/authenticate';

public function isCustomerInForced2faGroup(\Magento\Customer\Model\Customer $customer): bool;

public function is2faConfiguredForCustomer(\Magento\Customer\Model\Customer $customer): bool;

public function getAllowedRoutes(\Magento\Customer\Model\Customer $customer): array;

}
43 changes: 31 additions & 12 deletions Block/Authenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

namespace Elgentos\Frontend2FA\Block;

use Elgentos\Frontend2FA\Api\ConfigInterface;
use Elgentos\Frontend2FA\Api\TfaCheckInterface;
use Elgentos\Frontend2FA\Model\GoogleAuthenticatorService;
use Elgentos\Frontend2FA\Observer\TfaFrontendCheck;
use Magento\Catalog\Model\Session as CatalogSession;
use Magento\Customer\Model\Session;
use Magento\Framework\View\Element\Template\Context;
Expand All @@ -18,10 +19,6 @@

class Authenticator extends \Neyamtux\Authenticator\Block\Authenticator
{
/**
* @var TfaFrontendCheck
*/
public $observer;
/**
* @var Session
*/
Expand All @@ -42,7 +39,7 @@ class Authenticator extends \Neyamtux\Authenticator\Block\Authenticator
* @param Context $context
* @param GoogleAuthenticator $googleAuthenticator
* @param CatalogSession $session
* @param TfaFrontendCheck $observer
* @param TfaCheckInterface $tfaCheck
* @param Session $customerSession
* @param StoreManagerInterface $storeManager
* @param array $data
Expand All @@ -52,14 +49,14 @@ public function __construct(
GoogleAuthenticator $googleAuthenticator,
GoogleAuthenticatorService $googleAuthenticatorService,
CatalogSession $session,
TfaFrontendCheck $observer,
private readonly TfaCheckInterface $tfaCheck,
Session $customerSession,
StoreManagerInterface $storeManager,
private readonly ConfigInterface $config,
array $data = []
) {
parent::__construct($context, $googleAuthenticator, $session, $data);
$this->googleAuthenticatorService = $googleAuthenticatorService;
$this->observer = $observer;
$this->customerSession = $customerSession;
$this->storeManager = $storeManager;
}
Expand All @@ -72,7 +69,11 @@ public function __construct(
public function getQrCodeBase64Image()
{
// Replace non-alphanumeric characters with dashes; Google Authenticator does not like spaces in the title
$title = preg_replace('/[^a-z0-9]+/i', '-', $this->storeManager->getWebsite()->getName().' 2FA Login');
$title = sprintf("%s %s: %s",
$this->storeManager->getWebsite()->getName(),
'2FA',
$this->customerSession->getCustomer()->getEmail()
);
$imageData = base64_encode($this->googleAuthenticatorService->getQrCodeEndroid($title, $this->_googleSecret));

return 'data:image/png;base64,'.$imageData;
Expand All @@ -85,7 +86,7 @@ public function getQrCodeBase64Image()
*/
public function getSetupFormAction()
{
return $this->getUrl('frontend2fa/account/setup', ['_secure' => true]);
return $this->getUrl(TfaCheckInterface::FRONTEND_2_FA_ACCOUNT_SETUP_PATH, ['_secure' => true]);
}

/**
Expand All @@ -95,7 +96,7 @@ public function getSetupFormAction()
*/
public function getAuthenticateFormAction()
{
return $this->getUrl('frontend2fa/account/authenticate', ['_secure' => true]);
return $this->getUrl(TfaCheckInterface::FRONTEND_2_FA_ACCOUNT_AUTHENTICATE_PATH, ['_secure' => true]);
}

/**
Expand All @@ -109,6 +110,24 @@ public function is2faConfiguredForCustomer($customer = null)
$customer = $this->customerSession->getCustomer();
}

return $this->observer->is2faConfiguredForCustomer($customer);
return $this->tfaCheck->is2faConfiguredForCustomer($customer);
}

public function getCancelSetupUrl()
{
return '/customer/account/';
}

public function getCancel2faUrl()
{
return '/customer/account/logout/';
}

public function isInForcedGroup(): bool {
$customer = $this->customerSession->getCustomer();
return in_array(
$customer->getGroupId(),
$this->config->getForced2faCustomerGroups()
);
}
}
8 changes: 6 additions & 2 deletions Controller/Account/Authenticate.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,13 @@ public function execute()
} else {
$secret = $this->secretFactory->create()->load($this->_customerSession->getCustomerId(), 'customer_id')->getSecret();
if ($this->_authenticateQRCode($secret, $post['code'])) {
$this->messageManager->addSuccessMessage(__('Two Factor Authentication successful'));
$redirectUrl = $this->_customerSession->getBefore2faUrl() ?? 'customer/account';
if (!str_contains($redirectUrl, 'checkout')) {
$this->messageManager->addSuccessMessage(__('Two Factor Authentication successful'));
}

$this->_customerSession->set2faSuccessful(true);
$this->_redirect('customer/account');
$this->_redirect($redirectUrl);
} else {
$this->messageManager->addErrorMessage(__('Two Factor Authentication code incorrect'));
$this->_customerSession->set2faSuccessful(false);
Expand Down
39 changes: 39 additions & 0 deletions Model/Config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/**
* Copyright Youwe. All rights reserved.
* https://www.youweagency.com
*/

declare(strict_types=1);

namespace Elgentos\Frontend2FA\Model;

use Elgentos\Frontend2FA\Api\ConfigInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;

class Config implements ConfigInterface
{
const ELGENTOS_AUTHENTICATOR_GENERAL_ENABLE = 'elgentos_authenticator/general/enable';
const ELGENTOS_AUTHENTICATOR_GENERAL_FORCED_GROUPS = 'elgentos_authenticator/general/forced_groups';

public function __construct(
private readonly ScopeConfigInterface $config
){

}

public function isEnabled(): bool
{
return $this->config->isSetFlag(
self::ELGENTOS_AUTHENTICATOR_GENERAL_ENABLE,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
}

public function getForced2faCustomerGroups(): array
{
$forced2faCustomerGroups = $this->config->getValue(self::ELGENTOS_AUTHENTICATOR_GENERAL_FORCED_GROUPS, \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ?? '';

return array_filter(array_map('trim', explode(',', $forced2faCustomerGroups)));
}
}
9 changes: 5 additions & 4 deletions Model/GoogleAuthenticatorService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Elgentos\Frontend2FA\Model;

use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
use Endroid\QrCode\Encoding\Encoding;

class GoogleAuthenticatorService extends \Neyamtux\Authenticator\Lib\PHPGangsta\GoogleAuthenticator
{
Expand All @@ -27,12 +29,11 @@ public function getQrCodeEndroid($name, $secret, $title = null, $params = [])
}
$qrCode = new QrCode($text);
$qrCode->setSize($size);
$qrCode->setWriterByName('png');
$qrCode->setMargin(0);
$qrCode->setEncoding('UTF-8');
$qrCode->setEncoding(new Encoding('UTF-8'));
$qrCode->setSize($size);
$qrCode->setText($text);

return $qrCode->writeString();
$writer = new PngWriter();
return $writer->write($qrCode)->getString();
}
}
57 changes: 57 additions & 0 deletions Model/TfaCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/**
* Copyright Youwe. All rights reserved.
* https://www.youweagency.com
*/

declare(strict_types=1);

namespace Elgentos\Frontend2FA\Model;

use Elgentos\Frontend2FA\Api\ConfigInterface;
use Elgentos\Frontend2FA\Api\TfaCheckInterface;
use Elgentos\Frontend2FA\Model\SecretFactory;

class TfaCheck implements TfaCheckInterface
{
public function __construct(
private readonly SecretFactory $secretFactory,
private readonly ConfigInterface $config
)
{

}

public function isCustomerInForced2faGroup(\Magento\Customer\Model\Customer $customer):bool
{
return in_array($customer->getGroupId(), $this->config->getForced2faCustomerGroups());
}


public function is2faConfiguredForCustomer(\Magento\Customer\Model\Customer $customer): bool
{
$secret = $this->secretFactory->create()->load($customer->getId(), 'customer_id');
if ($secret->getId() && $secret->getSecret()) {
return true;
}

return false;
}

public function getAllowedRoutes(\Magento\Customer\Model\Customer $customer): array
{
// When 2FA is configured, the customer needs to authenticate
if ($this->is2faConfiguredForCustomer($customer)) {
$routes = [self::FRONTEND_2_FA_ACCOUNT_AUTHENTICATE_ROUTE];
} else {
$routes = [self::FRONTEND_2_FA_ACCOUNT_SETUP_ROUTE];
}

// Customer should always be able to log out
$routes[] = self::CUSTOMER_ACCOUNT_LOGOUT_ROUTE;

return $routes;
}

}
20 changes: 20 additions & 0 deletions Observer/CustomerLogoutObserver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Elgentos\Frontend2FA\Observer;

use Magento\Customer\Model\Session;
use Magento\Framework\Event\ObserverInterface;

class CustomerLogoutObserver implements ObserverInterface
{
public function __construct(
private readonly Session $customerSession
) {
}
public function execute(\Magento\Framework\Event\Observer $observer)
{
$this->customerSession->set2faSuccessful(false);
}
}
Loading