diff --git a/app/config/packages/scheb_2fa.yaml b/app/config/packages/scheb_2fa.yaml index beaf626b..bb34b29e 100644 --- a/app/config/packages/scheb_2fa.yaml +++ b/app/config/packages/scheb_2fa.yaml @@ -27,6 +27,7 @@ scheb_two_factor: server_name: Server Name # Server name used in QR code issuer: Issuer Name # Issuer name used in QR code window: 1 # How many codes before/after the current one would be accepted as valid + # leeway: 30 template: security/2fa.html.twig # Template used to render the authentication form totp: @@ -34,6 +35,7 @@ scheb_two_factor: server_name: Server Name # Server name used in QR code issuer: Issuer Name # Issuer name used in QR code window: 1 # How many codes before/after the current one would be accepted as valid + # leeway: 30 parameters: # Additional parameters added in the QR code image: 'https://my-service/img/logo.png' template: security/2fa.html.twig # Template used to render the authentication form diff --git a/doc/configuration.rst b/doc/configuration.rst index 6d7852d0..b95423bf 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -47,9 +47,13 @@ Bundle Configuration server_name: Server Name # Server name used in QR code issuer: Issuer Name # Issuer name used in QR code digits: 6 # Number of digits in authentication code - window: 1 # Depends on the version of Spomky-Labs/otphp used: - # Until v10: How many codes before/after the current one would be accepted - # From v11: Acceptable time drift in seconds + window: 1 # [DEPRECATED since v6.11, will be removed in v7] Use "leeway", if possible + # Behavior depends on the version of Spomky-Labs/otphp used: + # - Until v10: How many codes before/after the current one would be accepted + # - From v11: Acceptable time drift in seconds + leeway: 0 # Acceptable time drift in seconds, requires Spomky-Labs/otphp v11 to be used + # Must be less or equal than 30 seconds + # If configured, takes precedence over the "window" option template: security/2fa_form.html.twig # Template used to render the authentication form form_renderer: acme.custom_form_renderer # Use a custom form renderer service @@ -58,9 +62,13 @@ Bundle Configuration enabled: true # If TOTP authentication should be enabled, default false server_name: Server Name # Server name used in QR code issuer: Issuer Name # Issuer name used in QR code - window: 1 # Depends on the version of Spomky-Labs/otphp used: - # Until v10: How many codes before/after the current one would be accepted - # From v11: Acceptable time drift in seconds + window: 1 # [DEPRECATED since v6.11, will be removed in v7] Use "leeway", if possible + # Behavior depends on the version of Spomky-Labs/otphp used: + # - Until v10: How many codes before/after the current one would be accepted + # - From v11: Acceptable time drift in seconds + leeway: 0 # Acceptable time drift in seconds, requires Spomky-Labs/otphp v11 to be used + # Must be less or equal than the TOTP code's period + # If configured, takes precedence over the "window" option parameters: # Additional parameters added in the QR code image: 'https://my-service/img/logo.png' template: security/2fa_form.html.twig # Template used to render the authentication form diff --git a/doc/providers/google.rst b/doc/providers/google.rst index d8319875..5ab64ce3 100644 --- a/doc/providers/google.rst +++ b/doc/providers/google.rst @@ -142,9 +142,13 @@ Configuration Reference server_name: Server Name # Server name used in QR code issuer: Issuer Name # Issuer name used in QR code digits: 6 # Number of digits in authentication code - window: 1 # Depends on the version of Spomky-Labs/otphp used: - # Until v10: How many codes before/after the current one would be accepted - # From v11: Acceptable time drift in seconds + window: 1 # [DEPRECATED since v6.11, will be removed in v7] Use "leeway", if possible + # Behavior depends on the version of Spomky-Labs/otphp used: + # - Until v10: How many codes before/after the current one would be accepted + # - From v11: Acceptable time drift in seconds + leeway: 0 # Acceptable time drift in seconds, requires Spomky-Labs/otphp v11 to be used + # Must be less or equal than 30 seconds + # If configured, takes precedence over the "window" option template: security/2fa_form.html.twig # Template used to render the authentication form Custom Authentication Form Template diff --git a/doc/providers/totp.rst b/doc/providers/totp.rst index 66276f16..e354061a 100644 --- a/doc/providers/totp.rst +++ b/doc/providers/totp.rst @@ -143,9 +143,13 @@ Configuration Options enabled: true # If TOTP authentication should be enabled, default false server_name: Server Name # Server name used in QR code issuer: Issuer Name # Issuer name used in QR code - window: 1 # Depends on the version of Spomky-Labs/otphp used: - # Until v10: How many codes before/after the current one would be accepted - # From v11: Acceptable time drift in seconds + window: 1 # [DEPRECATED since v6.11, will be removed in v7] Use "leeway", if possible + # Behavior depends on the version of Spomky-Labs/otphp used: + # - Until v10: How many codes before/after the current one would be accepted + # - From v11: Acceptable time drift in seconds + leeway: 0 # Acceptable time drift in seconds, requires Spomky-Labs/otphp v11 to be used + # Must be less or equal than the TOTP code's period + # If configured, takes precedence over the "window" option parameters: # Additional parameters added in the QR code image: 'https://my-service/img/logo.png' template: security/2fa_form.html.twig # Template used to render the authentication form diff --git a/doc/troubleshooting.rst b/doc/troubleshooting.rst index 9874cd9a..d8c72214 100644 --- a/doc/troubleshooting.rst +++ b/doc/troubleshooting.rst @@ -20,8 +20,8 @@ it depends on your configuration). The bigger the time difference between server window, the higher the chance that the codes generated on server and from the app don't match up. When the time difference becomes larger than the time window, it becomes impossible to provide the right code. -To counteract the issue of time differences you could increase the ``window`` setting, then more codes around the -current time window will be accepted: +To counteract the issue of time differences you could increase the ``leeway`` or ``window`` (deprecated) setting, +then more codes around the current time window will be accepted: .. code-block:: yaml @@ -30,15 +30,23 @@ current time window will be accepted: # For TOTP totp: - window: 1 # Depends on the version of Spomky-Labs/otphp used: - # Until v10: How many codes before/after the current one would be accepted - # From v11: Acceptable time drift in seconds + window: 1 # [DEPRECATED since v6.11, will be removed in v7] Use "leeway", if possible + # Behavior depends on the version of Spomky-Labs/otphp used: + # - Until v10: How many codes before/after the current one would be accepted + # - From v11: Acceptable time drift in seconds + leeway: 0 # Acceptable time drift in seconds, requires Spomky-Labs/otphp v11 to be used + # Must be less or equal than the TOTP code's period + # If configured, takes precedence over the "window" option # For Google Authenticator google: - window: 1 # Depends on the version of Spomky-Labs/otphp used: - # Until v10: How many codes before/after the current one would be accepted - # From v11: Acceptable time drift in seconds + window: 1 # [DEPRECATED since v6.11, will be removed in v7] Use "leeway", if possible + # Behavior depends on the version of Spomky-Labs/otphp used: + # - Until v10: How many codes before/after the current one would be accepted + # - From v11: Acceptable time drift in seconds + leeway: 0 # Acceptable time drift in seconds, requires Spomky-Labs/otphp v11 to be used + # Must be less or equal than 30 seconds + # If configured, takes precedence over the "window" option You might want to configure a time synchronization service, such as ``ntpdate`` on your server to make sure your server time is always in sync with UTC. diff --git a/src/bundle/DependencyInjection/Configuration.php b/src/bundle/DependencyInjection/Configuration.php index 0d05f3a8..2190ff78 100644 --- a/src/bundle/DependencyInjection/Configuration.php +++ b/src/bundle/DependencyInjection/Configuration.php @@ -174,7 +174,11 @@ private function addTotpConfiguration(ArrayNodeDefinition $rootNode): void ->scalarNode('form_renderer')->defaultNull()->end() ->scalarNode('issuer')->defaultNull()->end() ->scalarNode('server_name')->defaultNull()->end() - ->integerNode('window')->defaultValue(1)->min(0)->end() + ->integerNode('window') + ->defaultValue(1)->min(0) + ->setDeprecated('scheb/2fa-totp', '6.11', 'The "%path%.%node%" option is deprecated. Use "leeway" instead, which requires spomky-labs/otphp v11 to be used.') + ->end() + ->integerNode('leeway')->defaultNull()->min(0)->end() ->arrayNode('parameters') ->scalarPrototype()->end() ->end() @@ -206,7 +210,11 @@ private function addGoogleAuthenticatorConfiguration(ArrayNodeDefinition $rootNo ->scalarNode('server_name')->defaultNull()->end() ->scalarNode('template')->defaultValue('@SchebTwoFactor/Authentication/form.html.twig')->end() ->integerNode('digits')->defaultValue(6)->min(1)->end() - ->integerNode('window')->defaultValue(1)->min(0)->end() + ->integerNode('window') + ->defaultValue(1)->min(0) + ->setDeprecated('scheb/2fa-google-authenticator', '6.11', 'The "%path%.%node%" option is deprecated. Use "leeway" instead, which requires spomky-labs/otphp v11 to be used.') + ->end() + ->integerNode('leeway')->defaultNull()->min(0)->end() ->end() ->end() ->end(); diff --git a/src/bundle/DependencyInjection/SchebTwoFactorExtension.php b/src/bundle/DependencyInjection/SchebTwoFactorExtension.php index 3573b336..a3bae79c 100644 --- a/src/bundle/DependencyInjection/SchebTwoFactorExtension.php +++ b/src/bundle/DependencyInjection/SchebTwoFactorExtension.php @@ -4,6 +4,9 @@ namespace Scheb\TwoFactorBundle\DependencyInjection; +use OTPHP\TOTP; +use ReflectionMethod; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -11,6 +14,8 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use function assert; +use function class_exists; +use function count; use function is_bool; use function is_string; use function trim; @@ -188,6 +193,11 @@ private function configureEmailAuthenticationProvider(ContainerBuilder $containe */ private function configureGoogleAuthenticationProvider(ContainerBuilder $container, array $config): void { + // Migration path for the "leeway" option, to be fully migrated in bundle version 7 + if (null !== $config['google']['leeway'] && !$this->isSpomkyOtphpVersion11Used()) { + throw new InvalidConfigurationException('The "leeway" option can only be set when spomky-labs/otphp v11 is used.'); + } + $loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('two_factor_provider_google.php'); @@ -196,6 +206,7 @@ private function configureGoogleAuthenticationProvider(ContainerBuilder $contain $container->setParameter('scheb_two_factor.google.template', $config['google']['template']); $container->setParameter('scheb_two_factor.google.digits', $config['google']['digits']); $container->setParameter('scheb_two_factor.google.window', $config['google']['window']); + $container->setParameter('scheb_two_factor.google.leeway', $config['google']['leeway']); if (null === $config['google']['form_renderer']) { return; @@ -209,6 +220,11 @@ private function configureGoogleAuthenticationProvider(ContainerBuilder $contain */ private function configureTotpAuthenticationProvider(ContainerBuilder $container, array $config): void { + // Migration path for the "leeway" option, to be fully migrated in bundle version 7 + if (null !== $config['totp']['leeway'] && !$this->isSpomkyOtphpVersion11Used()) { + throw new InvalidConfigurationException('The "leeway" option can only be set when spomky-labs/otphp v11 is used.'); + } + $loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('two_factor_provider_totp.php'); @@ -217,6 +233,7 @@ private function configureTotpAuthenticationProvider(ContainerBuilder $container $container->setParameter('scheb_two_factor.totp.window', $config['totp']['window']); $container->setParameter('scheb_two_factor.totp.parameters', $config['totp']['parameters']); $container->setParameter('scheb_two_factor.totp.template', $config['totp']['template']); + $container->setParameter('scheb_two_factor.totp.leeway', $config['totp']['leeway']); if (null === $config['totp']['form_renderer']) { return; @@ -225,6 +242,18 @@ private function configureTotpAuthenticationProvider(ContainerBuilder $container $container->setAlias('scheb_two_factor.security.totp.form_renderer', $config['totp']['form_renderer']); } + private function isSpomkyOtphpVersion11Used(): bool + { + if (!class_exists(TOTP::class)) { + return false; + } + + $parameters = (new ReflectionMethod(TOTP::class, 'verify'))->getParameters(); + + // Third parameter must be named "leeway" + return count($parameters) >= 3 && 'leeway' === $parameters[2]->getName(); + } + private function resolveFeatureFlag(ContainerBuilder $container, bool|string $value): bool { $retValue = $container->resolveEnvPlaceholders($value, true); diff --git a/src/bundle/Resources/config/two_factor_provider_google.php b/src/bundle/Resources/config/two_factor_provider_google.php index dfb07f26..0aa99d0d 100644 --- a/src/bundle/Resources/config/two_factor_provider_google.php +++ b/src/bundle/Resources/config/two_factor_provider_google.php @@ -25,6 +25,7 @@ ->args([ service('scheb_two_factor.security.google_totp_factory'), '%scheb_two_factor.google.window%', + '%scheb_two_factor.google.leeway%', ]) ->set('scheb_two_factor.security.google.default_form_renderer', DefaultTwoFactorFormRenderer::class) diff --git a/src/bundle/Resources/config/two_factor_provider_totp.php b/src/bundle/Resources/config/two_factor_provider_totp.php index fd5acc3b..92c6bb2d 100644 --- a/src/bundle/Resources/config/two_factor_provider_totp.php +++ b/src/bundle/Resources/config/two_factor_provider_totp.php @@ -26,6 +26,7 @@ ->args([ service('scheb_two_factor.security.totp_factory'), '%scheb_two_factor.totp.window%', + '%scheb_two_factor.totp.leeway%', ]) ->set('scheb_two_factor.security.totp.default_form_renderer', DefaultTwoFactorFormRenderer::class) diff --git a/src/google-authenticator/Security/TwoFactor/Provider/Google/GoogleAuthenticator.php b/src/google-authenticator/Security/TwoFactor/Provider/Google/GoogleAuthenticator.php index 9dbdab7d..0c8eaa42 100644 --- a/src/google-authenticator/Security/TwoFactor/Provider/Google/GoogleAuthenticator.php +++ b/src/google-authenticator/Security/TwoFactor/Provider/Google/GoogleAuthenticator.php @@ -19,6 +19,8 @@ public function __construct( private GoogleTotpFactory $totpFactory, /** @var 0|positive-int */ private int $window, + /** @var 0|positive-int|null */ + private null|int $leeway, ) { } @@ -31,7 +33,7 @@ public function checkCode(TwoFactorInterface $user, string $code): bool } /** @var non-empty-string $code */ - return $this->totpFactory->createTotpForUser($user)->verify($code, null, $this->window); + return $this->totpFactory->createTotpForUser($user)->verify($code, null, $this->leeway ?? $this->window); } public function getQRContent(TwoFactorInterface $user): string diff --git a/src/totp/Security/TwoFactor/Provider/Totp/TotpAuthenticator.php b/src/totp/Security/TwoFactor/Provider/Totp/TotpAuthenticator.php index 4ffedded..93f55784 100644 --- a/src/totp/Security/TwoFactor/Provider/Totp/TotpAuthenticator.php +++ b/src/totp/Security/TwoFactor/Provider/Totp/TotpAuthenticator.php @@ -19,6 +19,8 @@ public function __construct( private TotpFactory $totpFactory, /** @var 0|positive-int */ private int $window, + /** @var 0|positive-int|null */ + private null|int $leeway, ) { } @@ -31,7 +33,7 @@ public function checkCode(TwoFactorInterface $user, string $code): bool } /** @var non-empty-string $code */ - return $this->totpFactory->createTotpForUser($user)->verify($code, null, $this->window); + return $this->totpFactory->createTotpForUser($user)->verify($code, null, $this->leeway ?? $this->window); } public function getQRContent(TwoFactorInterface $user): string diff --git a/tests/DependencyInjection/SchebTwoFactorExtensionTest.php b/tests/DependencyInjection/SchebTwoFactorExtensionTest.php index 4a336967..9c944c42 100644 --- a/tests/DependencyInjection/SchebTwoFactorExtensionTest.php +++ b/tests/DependencyInjection/SchebTwoFactorExtensionTest.php @@ -6,6 +6,7 @@ use Scheb\TwoFactorBundle\DependencyInjection\SchebTwoFactorExtension; use Scheb\TwoFactorBundle\Tests\TestCase; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -48,9 +49,11 @@ public function load_emptyConfig_setDefaultValues(): void $this->assertHasNotParameter('scheb_two_factor.google.template'); $this->assertHasNotParameter('scheb_two_factor.google.digits'); $this->assertHasNotParameter('scheb_two_factor.google.window'); + $this->assertHasNotParameter('scheb_two_factor.google.leeway'); $this->assertHasNotParameter('scheb_two_factor.totp.issuer'); $this->assertHasNotParameter('scheb_two_factor.totp.server_name'); $this->assertHasNotParameter('scheb_two_factor.totp.window'); + $this->assertHasNotParameter('scheb_two_factor.totp.leeway'); $this->assertHasNotParameter('scheb_two_factor.totp.parameters'); $this->assertHasNotParameter('scheb_two_factor.totp.template'); $this->assertHasNotParameter('scheb_two_factor.trusted_device.lifetime'); @@ -85,9 +88,11 @@ public function load_fullConfig_setConfigValues(): void $this->assertHasParameter('AcmeTestBundle:Authentication:googleForm.html.twig', 'scheb_two_factor.google.template'); $this->assertHasParameter(8, 'scheb_two_factor.google.digits'); $this->assertHasParameter(2, 'scheb_two_factor.google.window'); + $this->assertHasParameter(null, 'scheb_two_factor.google.leeway'); $this->assertHasParameter('Issuer TOTP', 'scheb_two_factor.totp.issuer'); $this->assertHasParameter('Server Name TOTP', 'scheb_two_factor.totp.server_name'); - $this->assertHasParameter(2, 'scheb_two_factor.totp.window'); + $this->assertHasParameter(3, 'scheb_two_factor.totp.window'); + $this->assertHasParameter(null, 'scheb_two_factor.totp.leeway'); $this->assertHasParameter(['image' => 'http://foo/bar.png'], 'scheb_two_factor.totp.parameters'); $this->assertHasParameter('AcmeTestBundle:Authentication:totpForm.html.twig', 'scheb_two_factor.totp.template'); $this->assertHasParameter(true, 'scheb_two_factor.trusted_device.enabled'); @@ -102,6 +107,48 @@ public function load_fullConfig_setConfigValues(): void $this->assertHasParameter(['127.0.0.1'], 'scheb_two_factor.ip_whitelist'); } + /** + * @test + */ + public function load_fullConfigWithGoogleLeeway_setConfigValuesOrException(): void + { + $config = $this->getFullConfig(); + $config['google']['leeway'] = 20; + + try { + $this->extension->load([$config], $this->container); + } catch (InvalidConfigurationException $e) { + // When the option is not supported, an exception is thrown instead + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The "leeway" option can only be set'); + + throw $e; + } + + $this->assertHasParameter(20, 'scheb_two_factor.google.leeway'); + } + + /** + * @test + */ + public function load_fullConfigWithTotpLeeway_setConfigValuesOrException(): void + { + $config = $this->getFullConfig(); + $config['totp']['leeway'] = 30; + + try { + $this->extension->load([$config], $this->container); + } catch (InvalidConfigurationException $e) { + // When the option is not supported, an exception is thrown instead + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The "leeway" option can only be set'); + + throw $e; + } + + $this->assertHasParameter(30, 'scheb_two_factor.totp.leeway'); + } + /** * @test */ @@ -674,7 +721,7 @@ private function getFullConfig(): array enabled: true issuer: Issuer TOTP server_name: Server Name TOTP - window: 2 + window: 3 parameters: image: http://foo/bar.png template: AcmeTestBundle:Authentication:totpForm.html.twig diff --git a/tests/Security/TwoFactor/Provider/Google/GoogleAuthenticatorTest.php b/tests/Security/TwoFactor/Provider/Google/GoogleAuthenticatorTest.php index 0d8ce383..67769c76 100644 --- a/tests/Security/TwoFactor/Provider/Google/GoogleAuthenticatorTest.php +++ b/tests/Security/TwoFactor/Provider/Google/GoogleAuthenticatorTest.php @@ -32,7 +32,7 @@ protected function setUp(): void ->with($this->user) ->willReturn($this->totp); - $this->authenticator = new GoogleAuthenticator($this->totpFactory, 123); + $this->authenticator = new GoogleAuthenticator($this->totpFactory, 123, 42); } /** @@ -62,6 +62,36 @@ public function provideCheckCodeData(): array ]; } + /** + * @test + */ + public function checkCode_leewayGiven_leewayValueUsed(): void + { + $this->authenticator = new GoogleAuthenticator($this->totpFactory, 123, 42); + + $this->totp + ->expects($this->once()) + ->method('verify') + ->with('code', null, 42); + + $this->authenticator->checkCode($this->user, 'code'); + } + + /** + * @test + */ + public function checkCode_onlyWindowValueGiven_windowValueUsed(): void + { + $this->authenticator = new GoogleAuthenticator($this->totpFactory, 123, null); + + $this->totp + ->expects($this->once()) + ->method('verify') + ->with('code', null, 123); + + $this->authenticator->checkCode($this->user, 'code'); + } + /** * @test */ @@ -70,7 +100,7 @@ public function checkCode_codeWithSpaces_stripSpacesBeforeCheck(): void $this->totp ->expects($this->once()) ->method('verify') - ->with('123456', null, 123) + ->with('123456', null, 42) ->willReturn(true); $this->authenticator->checkCode($this->user, ' 123 456 '); diff --git a/tests/Security/TwoFactor/Provider/Totp/TotpAuthenticatorTest.php b/tests/Security/TwoFactor/Provider/Totp/TotpAuthenticatorTest.php index 163eb597..c55ed7c4 100644 --- a/tests/Security/TwoFactor/Provider/Totp/TotpAuthenticatorTest.php +++ b/tests/Security/TwoFactor/Provider/Totp/TotpAuthenticatorTest.php @@ -16,6 +16,7 @@ class TotpAuthenticatorTest extends TestCase { private MockObject|TwoFactorInterface $user; + private MockObject|TotpFactory $totpFactory; private MockObject|TOTP $totp; private TotpAuthenticator $authenticator; @@ -24,14 +25,14 @@ protected function setUp(): void $this->user = $this->createMock(TwoFactorInterface::class); $this->totp = $this->createMock(TOTPInterface::class); - $totpFactory = $this->createMock(TotpFactory::class); - $totpFactory + $this->totpFactory = $this->createMock(TotpFactory::class); + $this->totpFactory ->expects($this->any()) ->method('createTotpForUser') ->with($this->user) ->willReturn($this->totp); - $this->authenticator = new TotpAuthenticator($totpFactory, 123); + $this->authenticator = new TotpAuthenticator($this->totpFactory, 123, 42); } /** @@ -61,6 +62,36 @@ public function provideCheckCodeData(): array ]; } + /** + * @test + */ + public function checkCode_leewayGiven_leewayValueUsed(): void + { + $this->authenticator = new TotpAuthenticator($this->totpFactory, 123, 42); + + $this->totp + ->expects($this->once()) + ->method('verify') + ->with('code', null, 42); + + $this->authenticator->checkCode($this->user, 'code'); + } + + /** + * @test + */ + public function checkCode_onlyWindowValueGiven_windowValueUsed(): void + { + $this->authenticator = new TotpAuthenticator($this->totpFactory, 123, null); + + $this->totp + ->expects($this->once()) + ->method('verify') + ->with('code', null, 123); + + $this->authenticator->checkCode($this->user, 'code'); + } + /** * @test */ @@ -69,7 +100,7 @@ public function checkCode_codeWithSpaces_stripSpacesBeforeCheck(): void $this->totp ->expects($this->once()) ->method('verify') - ->with('123456', null, 123) + ->with('123456', null, 42) ->willReturn(true); $this->authenticator->checkCode($this->user, ' 123 456 ');