yeebase / twofactorauthentication
为 Neos Flow 实现的双因素认证 (2FA)
Requires
- bacon/bacon-qr-code: ^2.0
- neos/flow: ^6.0
- pragmarx/google2fa: ^4.0
Requires (Dev)
- t3n/coding-standard: ^1.0
This package is auto-updated.
Last update: 2024-08-24 22:01:22 UTC
README
Yeebase.TwoFactorAuthentication Flow 包包含对 Flow 认证机制的扩展,使得您能够轻松实现双因素认证 (2FA)。
它提供了一个新的 认证提供者,可以与现有提供者一起使用,以通过一次性密码 (OTP) 启用 2FA。
安装
此包可以通过 composer 安装
composer require yeebase/twofactorauthentication
此包需要一个名为 yeebase_twofactorauthentication_secret
的新数据库表,可以通过以下方式添加:
./flow doctrine:migrate
配置
以下部分描述了将双因素认证包集成到现有 Flow 应用程序中的过程。安装后,双因素认证默认对所有系统账户都是禁用的。
认证提供者
此包提供了一个 TwoFactorAuthenticationProvider
,需要除现有提供者之外进行配置。此外,必须将 authenticationStrategy 设置为 allTokens
,以确保两个提供者都被考虑在内。
示例
Settings.yaml
:
Neos: Flow: security: authentication: authenticationStrategy: 'allTokens' providers: 'Some.Package:Default': # That assumes that the "PersistedUsernamePasswordProvider" is used as base authentication: provider: 'PersistedUsernamePasswordProvider' 'Some.Package:2FA': provider: 'Yeebase\TwoFactorAuthentication\Security\Authentication\Provider\TwoFactorAuthenticationProvider'
应用程序名称和路由
如果有 TwoFactorAuthenticationProvider
Settings.yaml
:
Yeebase: TwoFactorAuthentication: # This is the "issuer" that will be displayed in the authenticator app like: <issuer> (<holder>) applicationName: 'Some Application' routes: login: '@package': 'Some.Package' '@controller': 'Login' '@action': 'twoFactor'
Login/TwoFacor.html
... <f:form action="authenticate"> <div class="form-group"> <label for="otp">2FA Code</label> <f:form.textfield name="__authentication[Yeebase][TwoFactorAuthentication][Security][Authentication][Token][OtpToken][otp]" id="otp" additionalAttributes="{autofocus: true, autocomplete: 'off'}" /> </div> <f:form.submit value="Enter" /> </f:form> ...
而不是使用默认的 UsernamePasswordProvider,将您的设置更改为使用以下提供者: Yeebase\TwoFactorAuthentication\Security\Authentication\Provider\TwoFactorAuthenticationProvider
强制双因素认证
默认情况下,2FA 可以按账户启用,如果不为已认证的账户启用,则不是必需的。为了 要求 用户使用双因素认证登录,可以设置 Yeebase.TwoFactorAuthentication.requireTwoFactorAuthentication
标志。这样一来,每次账户认证时都必须 指定 一次性密码。为了避免在给定账户尚未启用 2FA 时导致异常,可以配置一个 设置,允许用户初始化 2FA。
Settings.yaml
:
Yeebase: TwoFactorAuthentication: requireTwoFactorAuthentication: true routes: # ... setup: '@package': 'Some.Package' '@controller': 'TwoFactorAuthenticationSetup' '@action': 'index'
相应的设置控制器(示例)
TwoFactorAuthenticationSetupController.php
<?php declare(strict_types=1); namespace Some\Package\Controller; use Neos\Error\Messages\Message; use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\Controller\ActionController; use Neos\Flow\Security\Account; use Neos\Flow\Security\Context; use Neos\Flow\Security\Exception\AccessDeniedException; use Yeebase\TwoFactorAuthentication\Domain\ValueObjects\OneTimePassword; use Yeebase\TwoFactorAuthentication\Domain\ValueObjects\SecretWithHmac; use Yeebase\TwoFactorAuthentication\Exception\InvalidOtpException; use Yeebase\TwoFactorAuthentication\Service\TwoFactorAuthenticationService; class TwoFactorAuthenticationSetupController extends ActionController { /** * @var Account */ private $authenticatedAccount; /** * @Flow\Inject * @var Context */ protected $securityContext; /** * @Flow\Inject * @var TwoFactorAuthenticationService */ protected $twoFactorAuthenticationService; protected function initializeAction(): void { parent::initializeAction(); $this->authenticatedAccount = $this->securityContext->getAccountByAuthenticationProviderName('Some.Package:Default'); if ($this->authenticatedAccount === null) { throw new AccessDeniedException('...'); } } public function indexAction(): void { $twoFactorAuthenticationEnabled = $this->twoFactorAuthenticationService->isTwoFactorAuthenticationEnabledFor($this->authenticatedAccount); $this->view->assign('2faEnabled', $twoFactorAuthenticationEnabled); if (!$twoFactorAuthenticationEnabled) { $holder = $this->authenticatedAccount->getAccountIdentifier(); $qrCode = $this->twoFactorAuthenticationService->generateActivationQrCode($holder); $this->view->assignMultiple([ 'secretWithHmac' => SecretWithHmac::fromSecret($qrCode->getSecret()), 'qrCode' => $qrCode->renderSvg(200), ]); } } public function enableAction(SecretWithHmac $secretWithHmac, OneTimePassword $otp): void { try { $this->twoFactorAuthenticationService->enableTwoFactorAuthentication($this->authenticatedAccount, $secretWithHmac->getSecret(), $otp); } catch (InvalidOtpException $exception) { $this->addFlashMessage('Invalid One-time Password', 'Invalid OTP', Message::SEVERITY_ERROR); $this->redirect('index'); } $this->addFlashMessage('Two-Factor-Authentication was activated!', '2FA enabled', Message::SEVERITY_OK); $this->redirect('index'); } public function disableAction(): void { $this->twoFactorAuthenticationService->disableTwoFactorAuthentication($this->authenticatedAccount); $this->addFlashMessage('Two-Factor-Authentication was deactivated!', '2FA disabled', Message::SEVERITY_NOTICE); $this->redirect('index'); } }
相应的模板(示例)
TwoFactorAuthenticationSetup/Index.html
:
<h2>Two-Factor Authentication</h2> <f:if condition="{2faEnabled}"> <f:then> <ul> <li>2FA is active</li> </ul> <f:form action="disable"> <f:form.submit value="disable 2FA" /> </f:form> </f:then> <f:else> <ul> <li>2FA is not active</li> </ul> <f:form action="enable"> <div> {qrCode -> f:format.raw()} </div> <label for="otp">2FA Code</label> <f:form.hidden name="secretWithHmac" value="{secretWithHmac}" /> <f:form.textfield name="otp" id="otp" additionalAttributes="{autofocus: true, pattern: '\d\d\d\d\d\d'}" required="true" title="OTP (Format: ######)" /> <f:form.submit value="enable 2FA" /> </f:form> </f:else> </f:if>
为了允许用户最初设置 2FA,必须允许调用相应的操作,即使账户尚未启用 2FA。这可以通过提供的 ExcludeTwoFactorAuthenticationSetup
请求模式 实现,该请求模式禁用了上面配置的 setup
路由的 2FA 认证提供者
Settings.yaml
:
Neos: Flow: security: authentication: providers: # ... 'Some.Package:2FA': requestPatterns: 'Some.Package:2FASetup': pattern: 'Yeebase\TwoFactorAuthentication\Security\RequestPattern\ExcludeTwoFactorAuthenticationSetup'
注意: ExcludeTwoFactorAuthenticationSetup
将禁用配置控制器中所有操作的 2FA,因此控制器不应执行任何需要进一步检查的临界任务。
许可证
此包根据 MIT 许可证授权 - 详细信息请参阅 LICENSE 文件。
致谢
此包依赖于 google2fa 包 用于生成和验证密钥/OTP,以及 BaconQrCode 用于二维码渲染。