From 41fd79fb3134638929c355e3c6c1d1c3dd9fc3ae Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Fri, 9 Feb 2024 01:02:14 +0300 Subject: [PATCH 1/7] extract two factor logic into separate classes processing each type of 2fa. --- config/auth.php | 5 + src/Authentication/AuthenticationService.php | 102 ++------------- .../OneTimePasswordProcessor.php | 114 +++++++++++++++++ .../TwoFactorProcessor/U2FProcessor.php | 119 ++++++++++++++++++ .../Webauthn2faProcessor.php | 114 +++++++++++++++++ .../TwoFactorProcessorCollection.php | 110 ++++++++++++++++ .../TwoFactorProcessorInterface.php | 53 ++++++++ .../TwoFactorProcessorLoader.php | 35 ++++++ ...Webauthn2fAuthenticationCheckerFactory.php | 4 +- ...bauthn2fAuthenticationCheckerInterface.php | 2 +- src/Exception/InvalidProviderException.php | 8 +- src/Exception/InvalidSettingsException.php | 6 +- src/Middleware/TwoFactorMiddleware.php | 24 ++-- .../AuthenticationServiceTest.php | 35 ++++++ ...uthn2fAuthenticationCheckerFactoryTest.php | 2 +- 15 files changed, 616 insertions(+), 117 deletions(-) create mode 100644 src/Authentication/TwoFactorProcessor/OneTimePasswordProcessor.php create mode 100644 src/Authentication/TwoFactorProcessor/U2FProcessor.php create mode 100644 src/Authentication/TwoFactorProcessor/Webauthn2faProcessor.php create mode 100644 src/Authentication/TwoFactorProcessorCollection.php create mode 100644 src/Authentication/TwoFactorProcessorInterface.php create mode 100644 src/Authentication/TwoFactorProcessorLoader.php diff --git a/config/auth.php b/config/auth.php index 82fbef5..0264716 100644 --- a/config/auth.php +++ b/config/auth.php @@ -87,6 +87,11 @@ ] ], ], + 'TwoFactorProcessors' => [ + \CakeDC\Auth\Authentication\TwoFactorProcessor\OneTimePasswordProcessor::class, + \CakeDC\Auth\Authentication\TwoFactorProcessor\U2FProcessor::class, + \CakeDC\Auth\Authentication\TwoFactorProcessor\Webauthn2faProcessor::class, + ], 'OneTimePasswordAuthenticator' => [ 'checker' => \CakeDC\Auth\Authentication\DefaultOneTimePasswordAuthenticationChecker::class, 'verifyAction' => [ diff --git a/src/Authentication/AuthenticationService.php b/src/Authentication/AuthenticationService.php index 9cdf7ea..742f2ef 100644 --- a/src/Authentication/AuthenticationService.php +++ b/src/Authentication/AuthenticationService.php @@ -14,7 +14,6 @@ namespace CakeDC\Auth\Authentication; use Authentication\AuthenticationService as BaseService; -use Authentication\Authenticator\Result; use Authentication\Authenticator\ResultInterface; use Authentication\Authenticator\StatelessInterface; use Psr\Http\Message\ServerRequestInterface; @@ -42,95 +41,21 @@ class AuthenticationService extends BaseService protected $failures = []; /** - * Proceed to google verify action after a valid result result + * Proceed to 2fa processor after a valid result result * + * @param \CakeDC\Auth\Authentication\TwoFactorProcessorInterface $processor The processor. * @param \Psr\Http\Message\ServerRequestInterface $request The request. * @param \Authentication\Authenticator\ResultInterface $result The original result * @return \Authentication\Authenticator\ResultInterface The result object. */ - protected function proceedToGoogleVerify(ServerRequestInterface $request, ResultInterface $result) + protected function proceed2FA(TwoFactorProcessorInterface $processor, ServerRequestInterface $request, ResultInterface $result) { - /** - * @var \Cake\Http\Session $session - */ - $session = $request->getAttribute('session'); - $session->write(self::TWO_FACTOR_VERIFY_SESSION_KEY, $result->getData()); - $result = new Result(null, self::NEED_TWO_FACTOR_VERIFY); + $result = $processor->proceed($request, $result); $this->_successfulAuthenticator = null; return $this->_result = $result; } - /** - * Proceed to webauthn2fa flow after a valid result result - * - * @param \Psr\Http\Message\ServerRequestInterface $request response to manipulate - * @param \Authentication\Authenticator\ResultInterface $result valid result - * @return \Authentication\Authenticator\ResultInterface with result, request and response keys - */ - protected function proceedToWebauthn2fa(ServerRequestInterface $request, ResultInterface $result) - { - /** - * @var \Cake\Http\Session $session - */ - $session = $request->getAttribute('session'); - $session->write(self::WEBAUTHN_2FA_SESSION_KEY, $result->getData()); - $result = new Result(null, self::NEED_WEBAUTHN_2FA_VERIFY); - $this->_successfulAuthenticator = null; - - return $this->_result = $result; - } - - /** - * Proceed to U2f flow after a valid result result - * - * @param \Psr\Http\Message\ServerRequestInterface $request response to manipulate - * @param \Authentication\Authenticator\ResultInterface $result valid result - * @return \Authentication\Authenticator\ResultInterface with result, request and response keys - */ - protected function proceedToU2f(ServerRequestInterface $request, ResultInterface $result) - { - /** - * @var \Cake\Http\Session $session - */ - $session = $request->getAttribute('session'); - $session->write(self::U2F_SESSION_KEY, $result->getData()); - $result = new Result(null, self::NEED_U2F_VERIFY); - $this->_successfulAuthenticator = null; - - return $this->_result = $result; - } - - /** - * Get the configured one-time password authentication checker - * - * @return \CakeDC\Auth\Authentication\OneTimePasswordAuthenticationCheckerInterface - */ - protected function getOneTimePasswordAuthenticationChecker() - { - return (new OneTimePasswordAuthenticationCheckerFactory())->build(); - } - - /** - * Get the configured u2f authentication checker - * - * @return \CakeDC\Auth\Authentication\Webauthn2FAuthenticationCheckerInterface - */ - protected function getWebauthn2fAuthenticationChecker() - { - return (new Webauthn2fAuthenticationCheckerFactory())->build(); - } - - /** - * Get the configured u2f authentication checker - * - * @return \CakeDC\Auth\Authentication\U2fAuthenticationCheckerInterface - */ - protected function getU2fAuthenticationChecker() - { - return (new U2fAuthenticationCheckerFactory())->build(); - } - /** * {@inheritDoc} * @@ -145,26 +70,17 @@ public function authenticate(ServerRequestInterface $request): ResultInterface } $result = null; + $processors = $this->getConfig('processors'); foreach ($this->authenticators() as $authenticator) { $result = $authenticator->authenticate($request); if ($result->isValid()) { $skipTwoFactorVerify = $authenticator->getConfig('skipTwoFactorVerify'); $userData = $result->getData()->toArray(); - $webauthn2faChecker = $this->getWebauthn2fAuthenticationChecker(); - if ($skipTwoFactorVerify !== true && $webauthn2faChecker->isRequired($userData)) { - return $this->proceedToWebauthn2fa($request, $result); + foreach ($processors as $processor) { + if ($skipTwoFactorVerify !== true && $processor->isRequired($userData)) { + return $this->proceed2FA($processor, $request, $result); + } } - - $u2fCheck = $this->getU2fAuthenticationChecker(); - if ($skipTwoFactorVerify !== true && $u2fCheck->isRequired($userData)) { - return $this->proceedToU2f($request, $result); - } - - $otpCheck = $this->getOneTimePasswordAuthenticationChecker(); - if ($skipTwoFactorVerify !== true && $otpCheck->isRequired($userData)) { - return $this->proceedToGoogleVerify($request, $result); - } - $this->_successfulAuthenticator = $authenticator; $this->_result = $result; diff --git a/src/Authentication/TwoFactorProcessor/OneTimePasswordProcessor.php b/src/Authentication/TwoFactorProcessor/OneTimePasswordProcessor.php new file mode 100644 index 0000000..9ecd070 --- /dev/null +++ b/src/Authentication/TwoFactorProcessor/OneTimePasswordProcessor.php @@ -0,0 +1,114 @@ +getOneTimePasswordAuthenticationChecker()->isRequired($userData); + } + + /** + * Proceed to 2fa processor after a valid result result. + * + * @param \Psr\Http\Message\ServerRequestInterface $request Request instance. + * @param \Authentication\Authenticator\ResultInterface $result Input result object. + * @return \Authentication\Authenticator\ResultInterface + */ + public function proceed(ServerRequestInterface $request, ResultInterface $result): ResultInterface + { + /** + * @var \Cake\Http\Session $session + */ + $session = $request->getAttribute('session'); + $session->write($this->getSessionKey(), $result->getData()); + $result = new Result(null, $this->getType()); + + return $result; + } + + /** + * Generates 2fa url, if enable. + * + * @param string $type Processor type. + * @return array|null + */ + public function getUrlByType(string $type): ?array + { + if ($type == $this->getType()) { + return Configure::read('OneTimePasswordAuthenticator.verifyAction'); + } + + return null; + } + + /** + * Get the configured one-time password authentication checker + * + * @return \CakeDC\Auth\Authentication\OneTimePasswordAuthenticationCheckerInterface + */ + protected function getOneTimePasswordAuthenticationChecker() + { + return (new OneTimePasswordAuthenticationCheckerFactory())->build(); + } +} diff --git a/src/Authentication/TwoFactorProcessor/U2FProcessor.php b/src/Authentication/TwoFactorProcessor/U2FProcessor.php new file mode 100644 index 0000000..4e9e63f --- /dev/null +++ b/src/Authentication/TwoFactorProcessor/U2FProcessor.php @@ -0,0 +1,119 @@ +getU2fAuthenticationChecker()->isRequired($userData); + } + + /** + * Proceed to 2fa processor after a valid result result. + * + * @param \Psr\Http\Message\ServerRequestInterface $request Request instance. + * @param \Authentication\Authenticator\ResultInterface $result Input result object. + * @return \Authentication\Authenticator\ResultInterface + */ + public function proceed(ServerRequestInterface $request, ResultInterface $result): ResultInterface + { + /** + * @var \Cake\Http\Session $session + */ + $session = $request->getAttribute('session'); + $session->write($this->getSessionKey(), $result->getData()); + $result = new Result(null, $this->getType()); + + return $result; + } + + /** + * Generates 2fa url, if enable. + * + * @param string $type Processor type. + * @return array|null + */ + public function getUrlByType(string $type): ?array + { + if ($type == $this->getType()) { + return Configure::read('U2f.startAction'); + } + + return null; + } + + /** + * Get the configured u2f authentication checker + * + * @return \CakeDC\Auth\Authentication\U2fAuthenticationCheckerInterface + */ + protected function getU2fAuthenticationChecker() + { + return (new U2fAuthenticationCheckerFactory())->build(); + } +} diff --git a/src/Authentication/TwoFactorProcessor/Webauthn2faProcessor.php b/src/Authentication/TwoFactorProcessor/Webauthn2faProcessor.php new file mode 100644 index 0000000..d8d1064 --- /dev/null +++ b/src/Authentication/TwoFactorProcessor/Webauthn2faProcessor.php @@ -0,0 +1,114 @@ +getWebauthn2fAuthenticationChecker()->isRequired($userData); + } + + /** + * Proceed to 2fa processor after a valid result result. + * + * @param \Psr\Http\Message\ServerRequestInterface $request Request instance. + * @param \Authentication\Authenticator\ResultInterface $result Input result object. + * @return \Authentication\Authenticator\ResultInterface + */ + public function proceed(ServerRequestInterface $request, ResultInterface $result): ResultInterface + { + /** + * @var \Cake\Http\Session $session + */ + $session = $request->getAttribute('session'); + $session->write($this->getSessionKey(), $result->getData()); + $result = new Result(null, $this->getType()); + + return $result; + } + + /** + * Generates 2fa url, if enable. + * + * @param string $type Processor type. + * @return array|null + */ + public function getUrlByType(string $type): ?array + { + if ($type == $this->getType()) { + return Configure::read('Webauthn2fa.startAction'); + } + + return null; + } + + /** + * Get the configured u2f authentication checker + * + * @return \CakeDC\Auth\Authentication\Webauthn2fAuthenticationCheckerInterface + */ + protected function getWebauthn2fAuthenticationChecker() + { + return (new Webauthn2fAuthenticationCheckerFactory())->build(); + } +} diff --git a/src/Authentication/TwoFactorProcessorCollection.php b/src/Authentication/TwoFactorProcessorCollection.php new file mode 100644 index 0000000..a75653e --- /dev/null +++ b/src/Authentication/TwoFactorProcessorCollection.php @@ -0,0 +1,110 @@ + + */ + protected $processors = []; + + /** + * Constructor + * + * @param array<\CakeDC\Auth\Authentication\TwoFactorProcessorInterface> $processors The list of processors to add to the collection. + */ + public function __construct(array $processors = []) + { + $this->addMany($processors); + } + + /** + * Remove all processors from the collection + * + * @return $this + */ + public function clear() + { + $this->processors = []; + + return $this; + } + + /** + * Add multiple processors at once. + * + * @param array<\CakeDC\Auth\Authentication\TwoFactorProcessorInterface|string> $processors The list of processors to add to the collection. + * @return $this + */ + public function addMany(array $processors) + { + foreach ($processors as $processor) { + if (is_string($processor) && is_subclass_of($processor, TwoFactorProcessorInterface::class)) { + $processor = new $processor(); + } + $this->add($processor); + } + + return $this; + } + + /** + * Add a processor to the collection + * + * Processor will be keyed by their names. + * + * @param \CakeDC\Auth\Authentication\TwoFactorProcessorInterface $processor The processor to load. + * @return $this + * @throws \InvalidArgumentException + */ + public function add(TwoFactorProcessorInterface $processor) + { + $this->processors[] = $processor; + + return $this; + } + + /** + * Implementation of IteratorAggregate. + * + * @return \Traversable + * @psalm-return \Traversable + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->processors); + } + + /** + * Implementation of Countable. + * + * Get the number of processors in the collection. + * + * @return int + */ + public function count(): int + { + return count($this->processors); + } +} diff --git a/src/Authentication/TwoFactorProcessorInterface.php b/src/Authentication/TwoFactorProcessorInterface.php new file mode 100644 index 0000000..0a1809a --- /dev/null +++ b/src/Authentication/TwoFactorProcessorInterface.php @@ -0,0 +1,53 @@ +addMany($processors); + + return $collection; + } +} diff --git a/src/Authentication/Webauthn2fAuthenticationCheckerFactory.php b/src/Authentication/Webauthn2fAuthenticationCheckerFactory.php index 3d92573..3af4e2b 100644 --- a/src/Authentication/Webauthn2fAuthenticationCheckerFactory.php +++ b/src/Authentication/Webauthn2fAuthenticationCheckerFactory.php @@ -24,13 +24,13 @@ class Webauthn2fAuthenticationCheckerFactory /** * Get the two factor authentication checker * - * @return \CakeDC\Auth\Authentication\Webauthn2FAuthenticationCheckerInterface + * @return \CakeDC\Auth\Authentication\Webauthn2fAuthenticationCheckerInterface */ public function build() { $className = Configure::read('Webauthn2fa.checker'); $interfaces = class_implements($className); - $required = Webauthn2FAuthenticationCheckerInterface::class; + $required = Webauthn2fAuthenticationCheckerInterface::class; if (in_array($required, $interfaces)) { return new $className(); diff --git a/src/Authentication/Webauthn2fAuthenticationCheckerInterface.php b/src/Authentication/Webauthn2fAuthenticationCheckerInterface.php index f118a1b..e78d8f6 100644 --- a/src/Authentication/Webauthn2fAuthenticationCheckerInterface.php +++ b/src/Authentication/Webauthn2fAuthenticationCheckerInterface.php @@ -12,7 +12,7 @@ */ namespace CakeDC\Auth\Authentication; -interface Webauthn2FAuthenticationCheckerInterface +interface Webauthn2fAuthenticationCheckerInterface { /** * Check if two factor authentication is enabled diff --git a/src/Exception/InvalidProviderException.php b/src/Exception/InvalidProviderException.php index 9e7d851..e603f21 100644 --- a/src/Exception/InvalidProviderException.php +++ b/src/Exception/InvalidProviderException.php @@ -18,10 +18,10 @@ class InvalidProviderException extends CakeException { protected $_messageTemplate = 'Invalid provider or missing class (%s)'; - - /** - * @var int - */ + + /** + * @var int + */ protected $code = 500; /** diff --git a/src/Exception/InvalidSettingsException.php b/src/Exception/InvalidSettingsException.php index 38e9d04..c5571c5 100644 --- a/src/Exception/InvalidSettingsException.php +++ b/src/Exception/InvalidSettingsException.php @@ -19,9 +19,9 @@ class InvalidSettingsException extends CakeException { protected $_messageTemplate = 'Invalid settings for key (%s)'; - /** - * @var int - */ + /** + * @var int + */ protected $code = 500; /** diff --git a/src/Middleware/TwoFactorMiddleware.php b/src/Middleware/TwoFactorMiddleware.php index 677754f..172376d 100644 --- a/src/Middleware/TwoFactorMiddleware.php +++ b/src/Middleware/TwoFactorMiddleware.php @@ -13,10 +13,9 @@ namespace CakeDC\Auth\Middleware; -use Cake\Core\Configure; use Cake\Http\Response; use Cake\Routing\Router; -use CakeDC\Auth\Authentication\AuthenticationService; +use CakeDC\Auth\Authentication\TwoFactorProcessorLoader; use CakeDC\Auth\Authenticator\CookieAuthenticator; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -32,19 +31,18 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface { $service = $request->getAttribute('authentication'); $status = $service->getResult() ? $service->getResult()->getStatus() : null; - switch ($status) { - case AuthenticationService::NEED_TWO_FACTOR_VERIFY: - $url = Configure::read('OneTimePasswordAuthenticator.verifyAction'); + $processors = TwoFactorProcessorLoader::processors(); + $url = null; + foreach ($processors as $processor) { + $url = $processor->getUrlByType($status); + if ($url !== null) { break; - case AuthenticationService::NEED_U2F_VERIFY: - $url = Configure::read('U2f.startAction'); - break; - case AuthenticationService::NEED_WEBAUTHN_2FA_VERIFY: - $url = Configure::read('Webauthn2fa.startAction'); - break; - default: - return $handler->handle($request); + } } + if ($url === null) { + return $handler->handle($request); + } + /** * @var \Cake\Http\Session $session */ diff --git a/tests/TestCase/Authentication/AuthenticationServiceTest.php b/tests/TestCase/Authentication/AuthenticationServiceTest.php index e8750ac..7594fbd 100644 --- a/tests/TestCase/Authentication/AuthenticationServiceTest.php +++ b/tests/TestCase/Authentication/AuthenticationServiceTest.php @@ -20,6 +20,9 @@ use Cake\TestSuite\TestCase; use CakeDC\Auth\Authentication\AuthenticationService; use CakeDC\Auth\Authentication\Failure; +use CakeDC\Auth\Authentication\TwoFactorProcessor\OneTimePasswordProcessor; +use CakeDC\Auth\Authentication\TwoFactorProcessor\U2FProcessor; +use CakeDC\Auth\Authentication\TwoFactorProcessor\Webauthn2faProcessor; use CakeDC\Auth\Authenticator\FormAuthenticator; class AuthenticationServiceTest extends TestCase @@ -49,6 +52,7 @@ public function testAuthenticateEmptyAuthenticators() $response = new Response(); $service = new AuthenticationService([ + 'processors' => [], 'identifiers' => [ 'Authentication.Password', ], @@ -79,6 +83,7 @@ public function testAuthenticateFail() $response = new Response(); $service = new AuthenticationService([ + 'processors' => [], 'identifiers' => [ 'Authentication.Password', ], @@ -131,6 +136,11 @@ public function testAuthenticate() ); $service = new AuthenticationService([ + 'processors' => [ + new OneTimePasswordProcessor(), + new U2FProcessor(), + new Webauthn2faProcessor(), + ], 'identifiers' => [ 'Authentication.Password', ], @@ -180,6 +190,11 @@ public function testAuthenticateShouldDoGoogleVerifyEnabled() ); $service = new AuthenticationService([ + 'processors' => [ + new OneTimePasswordProcessor(), + new U2FProcessor(), + new Webauthn2faProcessor(), + ], 'identifiers' => [ 'Authentication.Password' => [], ], @@ -232,6 +247,11 @@ public function testAuthenticateShouldDoGoogleVerifyDisabled() ); $service = new AuthenticationService([ + 'processors' => [ + new OneTimePasswordProcessor(), + new U2FProcessor(), + new Webauthn2faProcessor(), + ], 'identifiers' => [ 'Authentication.Password' => [], ], @@ -285,6 +305,11 @@ public function testAuthenticateShouldDoWebauthn2faEnabled() $response = new Response(); $service = new AuthenticationService([ + 'processors' => [ + new OneTimePasswordProcessor(), + new U2FProcessor(), + new Webauthn2faProcessor(), + ], 'identifiers' => [ 'Authentication.Password' => [], ], @@ -337,6 +362,11 @@ public function testAuthenticateShouldDoU2fEnabled() $response = new Response(); $service = new AuthenticationService([ + 'processors' => [ + new OneTimePasswordProcessor(), + new U2FProcessor(), + new Webauthn2faProcessor(), + ], 'identifiers' => [ 'Authentication.Password' => [], ], @@ -389,6 +419,11 @@ public function testAuthenticateShouldDoU2fDisabled() $response = new Response(); $service = new AuthenticationService([ + 'processors' => [ + new OneTimePasswordProcessor(), + new U2FProcessor(), + new Webauthn2faProcessor(), + ], 'identifiers' => [ 'Authentication.Password' => [], ], diff --git a/tests/TestCase/Authentication/Webauthn2fAuthenticationCheckerFactoryTest.php b/tests/TestCase/Authentication/Webauthn2fAuthenticationCheckerFactoryTest.php index 7c63fa8..5f117bb 100644 --- a/tests/TestCase/Authentication/Webauthn2fAuthenticationCheckerFactoryTest.php +++ b/tests/TestCase/Authentication/Webauthn2fAuthenticationCheckerFactoryTest.php @@ -39,7 +39,7 @@ public function testGetCheckerInvalidInterface() { Configure::write('Webauthn2fa.checker', \stdClass::class); $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid config for 'Webauthn2fa.checker', 'stdClass' does not implement 'CakeDC\Auth\Authentication\Webauthn2FAuthenticationCheckerInterface'"); + $this->expectExceptionMessage("Invalid config for 'Webauthn2fa.checker', 'stdClass' does not implement 'CakeDC\Auth\Authentication\Webauthn2fAuthenticationCheckerInterface'"); (new Webauthn2fAuthenticationCheckerFactory())->build(); } } From d966e8f630b9f186d5aeb52b430ca84ce2e02152 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Fri, 9 Feb 2024 01:53:21 +0300 Subject: [PATCH 2/7] update workflow --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19bb238..007ed28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,11 +7,11 @@ on: jobs: testsuite: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: - php-version: ['7.2', '7.3', '7.4', '8.0', '8.1'] + php-version: ['7.2', '7.4', '8.0', '8.1'] db-type: [sqlite, mysql, pgsql] prefer-lowest: [''] From e25f32dc1236c4ce220045a4672b2ed709abe072 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Fri, 9 Feb 2024 01:54:27 +0300 Subject: [PATCH 3/7] remove obsolete php versions --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 007ed28..91d0b5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['7.2', '7.4', '8.0', '8.1'] + php-version: ['7.4', '8.0', '8.1'] db-type: [sqlite, mysql, pgsql] prefer-lowest: [''] From cb40f03b27c63e5e5b38beec7daba4c64cc9cffe Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Fri, 9 Feb 2024 01:55:07 +0300 Subject: [PATCH 4/7] update ubuntu version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91d0b5a..ada125a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: cs-stan: name: Coding Standard & Static Analysis - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 From 288976ad62ac393bb9044026a91702e96db7864c Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Fri, 9 Feb 2024 01:59:16 +0300 Subject: [PATCH 5/7] update code checker php version --- .github/workflows/ci.yml | 2 +- src/Authentication/TwoFactorProcessor/U2FProcessor.php | 2 +- src/Plugin.php | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ada125a..e1824a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.1' extensions: mbstring, intl, apcu, memcached, redis tools: cs2pr coverage: none diff --git a/src/Authentication/TwoFactorProcessor/U2FProcessor.php b/src/Authentication/TwoFactorProcessor/U2FProcessor.php index 4e9e63f..3e89bcf 100644 --- a/src/Authentication/TwoFactorProcessor/U2FProcessor.php +++ b/src/Authentication/TwoFactorProcessor/U2FProcessor.php @@ -17,8 +17,8 @@ use Cake\Core\Configure; use CakeDC\Auth\Authentication\TwoFactorProcessorInterface; use CakeDC\Auth\Authentication\U2fAuthenticationCheckerFactory; +use CakeDC\Auth\Plugin; use Psr\Http\Message\ServerRequestInterface; - /** * U2FProcessor class */ diff --git a/src/Plugin.php b/src/Plugin.php index 87557af..7166fa2 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -21,4 +21,5 @@ */ class Plugin extends BasePlugin { + public const DEPRECATED_MESSAGE_U2F = 'U2F is no longer supported by chrome, we suggest using Webauthn as a replacement'; } From e2e91f93a4059930dd4e2c17586346f344a7f426 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Fri, 9 Feb 2024 02:02:10 +0300 Subject: [PATCH 6/7] codestyle fix --- src/Authentication/TwoFactorProcessor/U2FProcessor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Authentication/TwoFactorProcessor/U2FProcessor.php b/src/Authentication/TwoFactorProcessor/U2FProcessor.php index 3e89bcf..2486d23 100644 --- a/src/Authentication/TwoFactorProcessor/U2FProcessor.php +++ b/src/Authentication/TwoFactorProcessor/U2FProcessor.php @@ -19,6 +19,7 @@ use CakeDC\Auth\Authentication\U2fAuthenticationCheckerFactory; use CakeDC\Auth\Plugin; use Psr\Http\Message\ServerRequestInterface; + /** * U2FProcessor class */ From 91fede5e1868567dfdfb3e9a96b3b157377c4209 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Fri, 9 Feb 2024 19:45:43 +0300 Subject: [PATCH 7/7] add documentation. update changelog. --- .editorconfig | 18 ++++++++++++++++++ CHANGELOG.md | 5 ++++- Docs/Documentation/TwoFactor.md | 17 +++++++++++++++++ Docs/Home.md | 1 + 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .editorconfig create mode 100644 Docs/Documentation/TwoFactor.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7061901 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.bat] +end_of_line = crlf + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/CHANGELOG.md b/CHANGELOG.md index c6e8a79..8de07b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [9.0.0] - 2024-02-09 +- Two factor processors introduced. Prvided way to add new two factor processors on client level. + ## [7.0.0] - 2021-10-30 -- upgrade to cakephp 4.3.0 +- upgrade to cakephp 4.3.0 - upgrade to phpunit 9.5 ## [6.1.0] - 2021-05-14 diff --git a/Docs/Documentation/TwoFactor.md b/Docs/Documentation/TwoFactor.md new file mode 100644 index 0000000..97fa518 --- /dev/null +++ b/Docs/Documentation/TwoFactor.md @@ -0,0 +1,17 @@ +Two Factor (2FA) +================ + +Two-factor authentication (2FA) is an identity and access management security method that requires two forms of identification to access resources and data. 2FA gives businesses the ability to monitor and help safeguard their most vulnerable information and networks. + +Configuration +------------- + +Processors defined as Configure storage with key `TwoFactorProcessors` + + +Processors +------------- + +* `U2FProcessor` - *deprecated*. Universal 2nd Factor (U2F) is an open standard that strengthens and simplifies two-factor authentication (2FA) using specialized Universal Serial Bus (USB) or near-field communication (NFC) devices based on similar security technology found in smart cards. +* `OneTimePassword` - Authenticator is an authenticator app used as part of a two-factor/multi-factor authentication (2FA/MFA) scheme. It acts as an example of a “something you have” factor by generating one-time passwords (OTPs) on a smartphone or other mobile device. +* `Webauthn2fa` - WebAuthn is a browser-based API that allows for web applications to simplify and secure user authentication by using registered devices (phones, laptops, etc) as factors. It uses public key cryptography to protect users from advanced phishing attacks. diff --git a/Docs/Home.md b/Docs/Home.md index 9782d32..2aeb858 100644 --- a/Docs/Home.md +++ b/Docs/Home.md @@ -1,6 +1,7 @@ Home ==== * [Authentication](Documentation/Authentication.md) + * [Two Factor](Documentation/TwoFactor.md) * [Authorization](Documentation/Authorization.md) * [Social](Documentation/Social.md) * [SimpleRbacAuthorize](Documentation/SimpleRbacAuthorize.md) RBAC Authorize based on configuration