Coder Social home page Coder Social logo

2fa's Introduction

scheb/2fa

This bundle provides two-factor authentication for your Symfony application.

Build Status Code Coverage Latest Stable Version Monthly Downloads Total Downloads License

SchebTwoFactorBundle Logo

ℹ️ The repository contains bundle versions β‰₯ 5, which are compatible with Symfony 4.4 or later. The older (unsupported) versions are located in the scheb/two-factor-bundle repository.


The bundle is split into sub-packages, so you can choose the exact feature set you need and keep installed dependencies to a minimum.

Core features are provided by scheb/2fa-bundle:

  • Interface for custom two-factor authentication methods
  • Trusted IPs
  • Multi-factor authentication (more than 2 steps)
  • CSRF protection
  • Whitelisted routes (accessible during two-factor authentication)
  • Fully customizable conditions when to perform two-factor authentication
  • Future proof: Supports the authenticator-based security system, which will replace the current system in SymfonyΒ 6

Additional features:

  • Trusted devices (once passed, no more two-factor authentication on that device) (scheb/2fa-trusted-device)
  • Single-use backup codes for when you don't have access to the second factor device (scheb/2fa-backup-code)
  • QR codes to scan with your mobile device

Two-factor authentication methods:

Installation

Follow the installation instructions.

Documentation

Detailed documentation of all features can be found on the Symfony Bundles Documentation website.

Demo

This repository contains a small test application that can be quickly set-up locally to test two-factor authentication in a real Symfony environment. Check out the readme file in the app folder for more details.

Version Guidance

Version Status Symfony Version
1.x EOL >= 2.1, < 2.7
2.x EOL ^2.6, ^3.0, ^4.0
3.x EOL 3.4, ^4.0, ^5.0
4.x EOL 3.4, ^4.0, ^5.0
5.x EOL 4.4, ^5.0
6.x Bug & security fixes 5.4, ^6.0
7.x New features + Bug fixes 6.4, ^7.0

License

This software is available under the MIT license.

Security

For information about the security policy and know security issues, see SECURITY.md.

Contributing

Want to contribute to this project? See CONTRIBUTING.md.

Support Me

I'm developing this library since 2014. I love to hear from people using it, giving me the motivation to keep working on my open source projects.

If you want to let me know you're finding it useful, please consider giving it a star ⭐ on GitHub.

If you love my work and want to say thank you, you can help me out for a beer 🍻️ via PayPal.

2fa's People

Contributors

aaroncritchley avatar aturki avatar danielburger1337 avatar darookee avatar dfridrich avatar gnat42 avatar gnito-org avatar ionbazan avatar j0k3r avatar jeremywaguet avatar jmsche avatar jsawicki-internetcompany avatar keksa avatar lcobucci avatar lordjancso avatar maxhelias avatar mkrauser avatar montaniasystemab avatar philetaylor avatar quentinus95 avatar scheb avatar seldaek avatar sirwaddles avatar spomky avatar stephanvierkant avatar umpirsky avatar uneo7 avatar wouterj avatar xabbuh avatar zerkms avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

2fa's Issues

Add ability to influence under which condition it's allowed to flag a trusted device

From scheb/two-factor-bundle#279

I was thinking that it would be useful to be able to use whitelisting of ips but instead of bypassing 2fa completely, the whitelisted ips would be permitted to set as a trusted computer.

This would be excellent for where you want people to be able to set a computer as trusted but want to prevent people from accidently doing that when they arent at a specific location. In my case I can then allow users to set their work PC as trusted but not be able to set their home pc accidently.

I haven't yet looked into the code to see if i can easily be done with what is already in place, i wanted to get your thoughts on it first.

Idea:

Hmm, that would be a new feature. A new configuration option under scheb_two_factor.trusted_device and then some permission control on whether you can set the trusted device cookie or not. Would be something for v5 I'd guess, since it brings some changes on interfaces.

AWS AutoScalingGroup server change invalidates secret

Bundle version: 5.0
Symfony version: 5.0.10

Description
Hi, when I change server capacity in AWS (that is, when I change the server), the authenticator code becomes invalid, and I have to resync with a new secret, which is not valid for a production environment.

Is there a way to be able to change servers and keep the secret code valid? This is my firewall from security.yaml:

admin:
    pattern:    ^/admin
    anonymous: ~
    provider:    admin
    two_factor:
        auth_form_path: admin_2fa_login
        check_path: admin_2fa_login_check
        trusted_parameter_name: admin_trusted

Thank you.

Badge support for AuthenticationManager and API 2fa-auth

Context: I am currently migrating a project to the experimental AuthenticationManager as well as using this bundle instead of a homegrown 2fa solution.

We do have an API with an existing endpoint for logging in users which returns a special code to the client if 2fa is required, and then expects the login API call again with username, password, AND the 2fa code all present in one to do authentication in a single request.

This does not really play well with your API guidelines which require a two-request flow, which IMO is more complex as the client needs to keep even more state.

Anyway I ended up implementing this myself as it isn't that hard, but I also got pretty familiar with the new auth code as I had to do various authenticator implementations. So I am mostly posting this here for others who may need the help to figure this out.. and I am thinking if this bundle offered better support for it in the future it would maybe be good (could be a v6 thing for sure).

I now create this badge in my API authenticator, which gets added on the passport:

new TwoFactorAuthBadge($credentials['2fa_code'] ?? null)

The badge class is a very simple value object:

<?php

class TwoFactorAuthBadge implements BadgeInterface
{
    private $resolved = false;
    private ?string $twoFaCode;

    public function __construct(?string $twoFaCode)
    {
        $this->twoFaCode = $twoFaCode;
    }

    public function getTwoFaCode(): ?string
    {
        return $this->twoFaCode;
    }

    /**
     * @internal
     */
    public function markResolved(): void
    {
        $this->resolved = true;
    }

    public function isResolved(): bool
    {
        return $this->resolved;
    }
}

And the auth listener which checks the badge is also not that complex, which is why I think it might be worth including here:

<?php

use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Totp\TotpAuthenticatorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;

class TwoFactorAuthListener implements EventSubscriberInterface
{
    private TotpAuthenticatorInterface $totpAuthenticator;

    public function __construct(TotpAuthenticatorInterface $totpAuthenticator)
    {
        $this->totpAuthenticator = $totpAuthenticator;
    }

    public function checkPassport(CheckPassportEvent $event): void
    {
        /** @var Passport $passport */
        $passport = $event->getPassport();
        if (!$passport->hasBadge(TwoFactorAuthBadge::class)) {
            return;
        }

        /** @var TwoFactorAuthBadge $badge */
        $badge = $passport->getBadge(TwoFactorAuthBadge::class);
        if ($badge->isResolved()) {
            return;
        }

        /** @var User $user */
        $user = $passport->getUser();

        if (!$user->getTwoFaActive()) {
            $badge->markResolved();

            return;
        }

        if (null === $badge->getTwoFaCode()) {
            throw new MissingTwoFaCodeException();
        }

        if (false === $this->totpAuthenticator->checkCode($user, $badge->getTwoFaCode())) {
            throw new InvalidTwoFaCodeException();
        }

        $badge->markResolved();
    }

    public static function getSubscribedEvents(): array
    {
        return [CheckPassportEvent::class => ['checkPassport', 512]];
    }
}

There are also two special exceptions MissingTwoFaCodeException and InvalidTwoFaCodeException both extending AuthenticationException that I added to be able to render these correctly in onAuthenticationFailure.

Note: Doing this way, I did not configure 2fa on the API firewall at all, as it's handled purely within the Authenticator via the badge.

Set different templates per firewall

We use two different firewalls (backend, frontend) and need to set the 2fa template (google & totp) used per firewall.
As far as I can see this is not possible via configuration. Is there a way I'm not aware of or do I need to set up different TwoFactorFormRenderers?

Bundle version: 5.4
Symfony version: 4.4
PHP version: 7.4
Using authenticators (enable_authenticator_manager: true): YES / NO

Description

# security.yaml
security:
    firewalls:
        # backend firewall
        admin:
            pattern: ^/admin
            guard:
                provider: admin_user_provider
                authenticators: [App\Security\AdminFormAuthenticator]
            remember_me:
                secret: '%kernel.secret%'
                lifetime: 2592000 # 30 days in seconds
                name: remember_me
                path: /admin
            two-factor:
                provider: admin_user_provider
                auth_form_path: admin_2fa_login
                check_path: admin_2fa_login_check
            logout:
                path: admin_logout
                target: admin_login
            anonymous: true

        main:
            pattern: ^/
            guard:
                provider: frontend_user_provider
                authenticators: [App\Security\FrontendFormAuthenticator]
            remember_me:
                secret: '%kernel.secret%'
                lifetime: 2592000 # 30 days in seconds
                name: remember_me
                path: /
            two-factor:
                provider: frontend_user_provider
                auth_form_path: frontend_2fa_login
                check_path: frontend_2fa_login_check
            logout:
                path: frontend_logout
                target: frontend_login
            anonymous: true

Thanks for any pointers.

Supporting FOS\OAuthServerBundle / REST login with 2FA

Thanks @scheb for this 2FA implementation.
Its very nice.

But I have an REST API project. (no API Gateway project)
And when I want to use this 2FA there its very "interesting" to set up.

In theory it could be easy.

1.) OAuth 2.0 Login with Username and Password -> Getting Access Token but not fully authenticated
2.) Check if 2FA is needed and if yes, return JSON Error "Please fully authenticate via 2FA"
3.) Sending 2FA code to convert the temporary Access Token to a Fully Authenticated Access Token

And because many methods are private I cant use them directly.
This is my workaround.
I know. Its neither OAuth 2.0 default nor 2FA default. - but its working.

security:
    firewalls:
        api:
            pattern:   ^/api
            fos_oauth: true
            stateless: true
            two_factor: ~

And then overwrite the TokenController of FOS\OAuthServerBundle\Controller\TokenController.

<?php

namespace App\Controller;

use App\Entity\User;
use FOS\OAuthServerBundle\Controller\TokenController as BaseController;
use FOS\UserBundle\Model\UserManagerInterface;
use OAuth2\OAuth2;
use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorTokenInterface;
use Scheb\TwoFactorBundle\Security\TwoFactor\AuthenticationContext;
use Scheb\TwoFactorBundle\Security\TwoFactor\Handler\TwoFactorProviderHandler;
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\PreparationRecorderInterface;
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\TwoFactorProviderRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

class TokenController extends BaseController
{
    private $userManager;
    private $twoFactorProviderHandler;
    private $authenticationProvider;
    private $tokenStorage;
    private $providerRegistry;
    private $preparationRecorder;

    public function __construct(OAuth2 $server, UserManagerInterface $userManager,
                                TwoFactorProviderHandler $twoFactorProviderHandler,
                                AuthenticationManagerInterface $authenticationProvider,
                                TokenStorageInterface $tokenStorage,
                                TwoFactorProviderRegistry $providerRegistry,
                                PreparationRecorderInterface $preparationRecorder)
    {
        parent::__construct($server);
        $this->userManager = $userManager;
        $this->twoFactorProviderHandler = $twoFactorProviderHandler;
        $this->providerRegistry = $providerRegistry;
        $this->tokenStorage = $tokenStorage;
        $this->preparationRecorder = $preparationRecorder;
        $this->authenticationProvider = $authenticationProvider;
    }

    public function tokenAction(Request $request)
    {
	    if($request->get('grant_type') !== 'refresh_token')
	    {
            /** @var User $user */
            $user = null !== ($username = $request->get('username'))
                ? $this->userManager->findUserByUsernameOrEmail($username) : null;

            if ($user instanceof User)
            {
                $twoFactorToken = new UsernamePasswordToken($user, null, 'api', $user->getRoles());
                $twoFactorContext = new AuthenticationContext($request, $twoFactorToken, 'api');
                $newToken = $this->twoFactorProviderHandler->beginTwoFactorAuthentication($twoFactorContext);
                if ($newToken instanceof TwoFactorTokenInterface)
                {
                    try
                    {
                        $newToken = $newToken->createWithCredentials($request->get('2fa_code') ?? '');
                        $providerName = $newToken->getCurrentTwoFactorProvider();
                        $this->providerRegistry->getProvider($providerName)->prepareAuthentication($user);
                        $this->tokenStorage->setToken($newToken);
                        $this->preparationRecorder->setTwoFactorProviderPrepared($twoFactorContext->getFirewallName(), $providerName);

                        $newToken = $this->authenticationProvider->authenticate($newToken);
                        if ($newToken !== $twoFactorToken)
                        {
                            throw new \Exception();
                        }
                    }
                    catch(\Exception $exception)
                    {
                        return new Response(json_encode([
                            'error' => OAuth2::ERROR_INVALID_REQUEST,
                            'error_description' => 'Please specify 2fa_code!',
                        ]), Response::HTTP_BAD_REQUEST);
                    }
                }
            }
	    }

        return parent::tokenAction($request);
    }
}

This is only needed because I can't use the real workflow with \FOS\OAuthServerBundle\Security\Authentication\Token\OAuthToken or - for example - the \Scheb\TwoFactorBundle\Security\Authentication\Provider\TwoFactorAuthenticationProvider::isValidAuthenticationCode method.

Maybe this can be improved on the next version? πŸ‘
With Symfony 6 Auth-Badges it should be in general much better.

Custom TwoFactorProvider not calling prepareAuthentication

Bundle version: 5.2.1
Symfony version: 5.2.1
PHP version: 7.2.24

Description
Background
I am attempting to create a custom provider, one that uses Twilio Verify email 2fa.
The Verify API generates it's own verification code as a part of the service, one that I cannot see directly.
I can authenticate the code submitted via a form by sending a verification check to the Twilio Verify API.

I'm using Webpack Encore with a Vue.js front end; security.yaml configuration has a json_login and an entry for two_factor.

So far I'm able to use my login form and authenticate the user, the front end appears to behave properly, as it brings up my 2fa
form. (although, that's mostly due to how my vue/vuex/axios configuration is interpreting the response from Symfony).

I'm not certain, but it may be something to do with Roles, should it not switch to IS_AUTHENTICATED_2FA_IN_PROGRESS?
The log output below shows that the credentials are unset, is that okay?
Another detail I just noticed is that the provider I created is listed in twoFactorProviders, but it is not listed under preparedProviders

[credentials:Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken:private] => 
[providerKey:Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken:private] => main
[attributes:Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken:private] => Array ( )
[twoFactorProviders:Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken:private] => Array ( [0] => twilio_two_factor )
[preparedProviders:Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken:private] => Array ( ) )

Additional Context

I added some log output to show details for the Token when TwoFactorProviderPreparationListener::onLogin is fired. It indicates that it is a Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken and it appears to be the correct provider.

I created a class that implements Symfony\Component\Security\Guard\AbstractGuardAuthenticator, which provides the Guard authenticator (which provides the security token)

Profiler Screenshots

Events
image

Log
image

Security Token
image

config

# config/packages/scheb_2fa.yaml
scheb_two_factor:
    email:
        enabled: false
    security_tokens:
        - Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken
# config/packages/security.yaml
security:
    encoders:
        App\Entity\User:
            algorithm: auto

    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
        users_in_memory: { memory: null }
        # used to reload user from session & other features (e.g. switch_user)
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            lazy: true
            provider: app_user_provider
            guard:
                authenticators:
                    - App\Security\EmailAuthenticator
            logout:
                path: app_logout
            two_factor:
                auth_form_path: 2fa_login
                check_path: 2fa_login_check
                default_target_path: /dashboard
                always_use_default_target_path: false
                prepare_on_login: true
                prepare_on_access_denied: true
                authentication_required_handler: App\Security\TwoFactor\Handler\TwoFactorRequiredHandler
                success_handler: App\Security\TwoFactor\Handler\TwoFactorSuccessHandler
                failure_handler: App\Security\TwoFactor\Handler\TwoFactorFailureHandler
            json_login:
                check_path: app_login
                success_handler: App\Security\TwoFactor\Handler\LoginSuccessHandler

            # activate different ways to authenticate
            # https://symfony.com/doc/current/security.html#firewalls-authentication

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    role_hierarchy:
        ROLE_SUPER_ADMIN: [ROLE_USER,ROLE_ADMIN,ROLE_SUPER_ADMIN]
        ROLE_ADMIN: [ROLE_USER,ROLE_ADMIN]
        ROLE_USER: [ROLE_USER]

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/api/security/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api/security/2fa_check, roles: IS_AUTHENTICATED_2FA_IN_PROGRESS }
        # - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
        - { path: ^/api/admin, roles: IS_AUTHENTICATED_FULLY }
        - { path: ^/api/profile, roles: IS_AUTHENTICATED_FULLY }
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }

"Passing more than one Security attribute is not supported" in AccessDecisionManager

Bundle version: 5.1.3
Symfony version: 5.1.8

Description
I upgraded a project from SF4 to 5. I'm now getting this error when 2FA is enabled.

Passing more than one Security attribute to "Symfony\Component\Security\Core\Authorization\AccessDecisionManager::decide()" is not supported.

So far I've tracked it down to this. This seems to occur because in my security.yaml I have the following

        - { path: ^/practice/dashboard, roles: [ ROLE_UNVERIFIED, ROLE_USER ] }

If I remove one, it goes through fine. The error is coming from this commit

There was a bug in SF with this reported here
with a fix here

It seems like something needs to change in TwoFactorAccessDecider (around line 65). I haven't been able to figure out what other symfony components do in similar situations like this. If I disable 2FA my security.yml stuff works fine. So either this needs to use the symfony expression or multiple calls to decide as well. If I knew the correct solution I'd submit a PR (and still can if you guide me the way you'd want it solved).

Cant get the 2fa to work

Bundle version: 5.1
Symfony version: 5.1

Description

Hi, I cant get the 2fa to work.
It dosent trigger at all.

I have a login form on my /login page
That postes to /login and the guard will handle the rest and redirect to admin section.
I am not sure on how is it suppose to work either.

Is it suppose to take over when i poste my credentials to the login page?
Do i need to create a new login form and post it to another page?

I am kinda blank here so any help would be great.

Security.yaml

security:
    providers:
        security:
            id: App\Service\UserProviderService
    encoders:
        App\Security\User: plaintext
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~
            remember_me:
                secret:   '%kernel.secret%'
                lifetime: 604800 # 1 week in seconds
                path:     /
            logout:
                path: /logout
                success_handler: App\Handler\LogoutSuccessHandler
            two_factor:
                auth_form_path: /2fa_login    # The route name you have used in the routes.yaml
                check_path: /2fa_login_check  # The route name you have used in the routes.yaml
            guard:
                authenticators:
                    - App\Service\AuthenticationService
                entry_point: App\Service\AuthenticationService
    access_control:
        - { path: ^/logout, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/2fa_login, role: IS_AUTHENTICATED_ANONYMOUSLY } 
        - { path: ^/admin, roles: [ROLE_ADMINISTRATOR] }

scheb_two_factor.yaml

scheb_two_factor:
    backup_codes:
        enabled: false                 # If the backup code feature should be enabled
    google:
        enabled: true                  # If Google Authenticator should be enabled, default false
        server_name: pagedomain.com       # Server name used in QR code
        issuer: pagedomain            # Issuer name used in QR code
        digits: 6                      # Number of digits in authentication code
        window: 1                      # How many codes before/after the current one would be accepted as valid
        template: "security/2fa_form.html.twig"   # Template used to render the authentication form
    security_tokens:
        - Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken
        - Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken

Routing

2fa_login:
    path: /2fa_login
    defaults:
        _controller: "scheb_two_factor.form_controller:form"
2fa_login_check:
    path: /2fa_login_check
controllers:
    resource: '../src/Controller/'
    type:     annotation
    prefix:   /
dynamic_route:
    path: /{url}
    controller: App\Controller\DynamicRouteController::indexAction
    requirements:
        url: ".+"
login:
    path: /login
    controller: App\Controller\DynamicRouteController::indexAction

Additional Context

Ability to clear the trusted device cookie programmatically

Somewhat related to #70 - would it be possible to get a TrustedDeviceTokenStorage::clearCookie or similar, that would trigger TrustedCookieResponseListener's to clearCookie on the response?

It's not that important, but my use case is: when disabling 2FA for a user, I would rather clear the trusted_device cookie to clean things up. I can do the clearCookie myself of course but then I need to replicate all the cookie config there vs reusing the bundle's config, so it would be slightly cleaner if I could trigger this at the bundle level. TrustedDeviceTokenStorage::clearTokens or other name might be more appropriate.

Return value of Scheb\TwoFactorBundle\Security\TwoFactor\Trusted\JwtTokenEncoder::generateToken() must be an instance of Lcobucci\JWT\Token\Plain, instance of Lcobucci\JWT\Token returned

Bundle version: v5.4.2
Symfony version: 4.4.19
PHP version: 7.3.22
Using authenticators (enable_authenticator_manager: true): NO

Description
When attempting to sign in after supplying the token I receive the error on the 2fa_check route.

Return value of Scheb\TwoFactorBundle\Security\TwoFactor\Trusted\JwtTokenEncoder::generateToken() must be an instance of Lcobucci\JWT\Token\Plain, instance of Lcobucci\JWT\Token returne

scheb_two_factor.yaml

scheb_two_factor:
    trusted_device:
        enabled: true # If the trusted computer feature should be enabled
        cookie_name: trusted_device  # Name of the trusted device cookie
        extend_lifetime: false
        lifetime: 43200 # Lifetime of the trusted computer cookie
        cookie_secure: true # Set the 'Secure' (HTTPS Only) flag on the trusted_computer cookie
        cookie_same_site: strict # The same-site option of the cookie, can be "lax" or "strict"

    email:
        enabled: true # If email authentication should be enabled, default false
        mailer: App\Security\AuthCodeMailer # Use alternative service to send the authentication code
        sender_email: [email protected] # Sender email address
        sender_name: Me # Sender name
        digits: 6 # Number of digits in authentication code
        template: '@App/security/authentication-form.html.twig'

    totp:
        enabled: true
        issuer: Me
        window: 1
        template: '@App/security/authentication-form.html.twig'

    security_tokens:
        - Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken

    ip_whitelist:
        - 127.0.0.1 # localhost IPv4
        - 192.168.0.0/16 # Facility private IPv4 subnet

security.yaml

security:
    encoders:
        App\Entity\User: bcrypt

    providers:
        in_memory:
            memory: ~

        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        dev: #should not be affected by existing files
            pattern: ^/(_(profiler|wdt|error)|css|images|js)/
            security: false

        public: #prevents redirect to login
            pattern: ^(?!/order/|/settings/users/employee/\d+/qr-code).*\.(png|pdf)$|^/(about|css|images|js|mediaassets)|^/Scripts/.*\.(^php)$
            security: false

        main:
            anonymous: lazy
            user_checker: App\Security\UserChecker
            provider: app_user_provider
            switch_user: true
            logout:
                path:   /logout
                target: /login
                invalidate_session: true
            guard:
                authenticators:
                    - App\Security\LoginAuthenticator
            two_factor:
                auth_form_path: /2fa_login
                check_path: /2fa_check
                default_target_path: login_redirect #Where to redirect by default after successful authentication
                always_use_default_target_path: true #If it should always redirect to default_target_path
                auth_code_parameter_name: _auth_code
                trusted_parameter_name: _trusted
                multi_factor: false
                enable_csrf: true
                post_only: true

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    access_control:
        - { path: ^/login/redirect, roles: IS_AUTHENTICATED_FULLY } #used to prevent requested pages from being loaded before two-factor
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/about, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/2fa, roles: IS_AUTHENTICATED_2FA_IN_PROGRESS }
        - { path: ^/logout, roles: [IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_2FA_IN_PROGRESS] } #allow logged in users and those currently authenticating to logout
        - { path: ^/, roles: ROLE_USER }

To Reproduce

Steps to reproduce the behavior:

  1. Go to /login
  2. Provide Username and Password Credentials and click submit
  3. Prompted for 2FA Code at /2fa_login
  4. Check Email and receive 2FA code
  5. Supply 2FA code into form field and click Submit
  6. See Error above at /2fa_check.

Additional Context
Looking at the composer dependencies for scheb/2fa-trusted-device, it appears that the dependency signatures were changed in lcobucci/jwt version from 3.4 to 4.0 which requires php ^7.4 | ^8.0.

So the composer dependencies for scheb/2fa-trusted-device need to be updated to lcobucci/jwt: ^4.0

https://github.com/lcobucci/jwt/blob/4.0.0/src/Builder.php#L76

   public function getToken(Signer $signer, Key $key): Plain;

https://github.com/lcobucci/jwt/blob/3.4/src/Builder.php#L521

    /**
     * Returns the resultant token
     *
     * @return Token
     */
    public function getToken(Signer $signer = null, Key $key = null)

Integrate with new "Authenticators" security

Integrate the bundle with the new "authenticator" security system, which was introduced as an experimental feature in Symfony 5.1.

The old approach doesn't work anymore, which was: retrieving all "authentication listeners" from security.authentication.manager to decorate them and effectively wrap away the authenticated token before it becomes visible to the security system. There are no more listeners that could be used as a hook-in point

Potentially the new concept of "badges" and event-based authentication can help. Could potentially lead the bundle towards a cleaner, less hacky integration. Currently unclear, needs more investigation.

References:

Notes:

Token is created by the authenticator:
https://github.com/wouterj/symfony/blob/b1e040f311e16f4888f42564a6d5da0a489c929a/src/Symfony/Component/Security/Http/Authentication/AuthenticatorManager.php#L169

Example:
https://github.com/wouterj/symfony/blob/b1e040f311e16f4888f42564a6d5da0a489c929a/src/Symfony/Component/Security/Http/Authenticator/AbstractAuthenticator.php#L35

Limit unsuccessful logins

Hi,

Would it be possible to add a configuration option that would limit the number of invalid login within a time period ? Since the bundle is already managing security and login it would.make sense to add this feature.

Have a great day,
Samuel

Two-factor authentication form is not shown after login

Bundle version: 5.1.0
Symfony version: 5.1.3

Description
I'm attempting to get the two-factor authentication form to appear for a user which has a googleAuthenticatorSecret. The secret has been created by the service auto-injected for the GoogleAuthenticatorInterface.

However, when I attempt to log in, I'm logged normally, without ever being sent to 2fa_login route to inout the two factor code. The debug toolbar indicates that I do receive the Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorToken, though.

I've tried following https://github.com/scheb/2fa/blob/5.x/doc/troubleshooting.md#two-factor-authentication-form-is-not-shown-after-login and discovered the following:

  1. Is a TwoFactorToken present after the login?: Yes (or so the debug toolbar says)
  2. Try accessing a page that requires the user to be authenticated. Does it redirect to the two-factor authentication form?: No
  3. On login, do you reach the end (return statement) of method Scheb\TwoFactorBundle\Security\Authentication\Provider\AuthenticationProviderDecorator::authenticate()?: Yes
  4. On login, is method Scheb\TwoFactorBundle\Security\TwoFactor\Handler\TwoFactorProviderHandler::getActiveTwoFactorProviders() called?: Yes
  5. Does Scheb\TwoFactorBundle\Security\TwoFactor\Handler\TwoFactorProviderHandler::getActiveTwoFactorProviders() return any values?: Yes:
^ array:1 [β–Ό
  0 => "google"
]

The only time I'm able to trigger the two factor authentication form is when I start of by going to the 2fa_login route (/2fa). In that case, I get redirected to the 2fa input form after providing my username and password, and the authentication appears to work as expected.

The content of my security.yaml file is as follows:

security:

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

    encoders:
        FOS\UserBundle\Model\UserInterface: auto

    role_hierarchy:
        ROLE_EMPLOYEE:    [ ROLE_USER, ROLE_ALLOWED_TO_SWITCH ]
        ROLE_ADMIN:       [ ROLE_USER, ROLE_EMPLOYEE ]
        ROLE_SUPER_ADMIN: [ ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_token_generator: security.csrf.token_manager
            logout:       true
            anonymous:    lazy
            remember_me:
                secret:   '%secret%'
            user_checker: App\Security\UserChecker
            two_factor:
                auth_form_path: 2fa_login    # The route name you have used in the routes.yaml
                check_path: 2fa_login_check  # The route name you have used in the routes.yaml

    access_control:
        # This makes the logout route accessible during two-factor authentication. Allows the user to
        # cancel two-factor authentication, if they need to.
        - { path: ^/logout, role: IS_AUTHENTICATED_ANONYMOUSLY }
        # This ensures that the form can only be accessed when two-factor authentication is in progress.
        - { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/system_task, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/docs, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/nav/employee, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/teams$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/team/[0-9]+/procedure$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/procedure/[0-9]+/spec, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/template/[0-9]+, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/attachment/view/[0-9]+, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/page_template/[0-9]+, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/procedure/redirect/[0-9]+, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/dashboard, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/organization/new, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, role: ROLE_USER }

Move "prepared" status into security token

Currently the "prepared" status of a 2fa provider is stored in the session. Move it into the security token to simplify things.

This hasn't been done like that in v4, because it requires additional methods on TwoFactorTokenInterface and this would be a BC break.

TwoFactorPassport is not compatible with Symfony LoginSuccessEvent

I wanted to write a custom LoginSuccessEventListener that listens to Symfony\Component\Security\Http\Event\LoginSuccessEvent. This Symfony event has a getUser(): UserInterface method and it tries to get the User from the $this->passport. The passport has to implement UserPassportInterface.

The TwoFactorPassport which is in: vendor/scheb/2fa/src/bundle/Security/Http/Authenticator/Passport/TwoFactorPassport.php does not implement the UserPassportInterface interface. TwoFactorPassport is implementing PassportInterface but not UserPassportInterface.

The consequence of this is that the TwoFactorPassport is not compatible with the LoginSuccessEvent.

My request: Can you implement the UserPassportInterface interface for the TwoFactorPassport?

LogoutHandlerInterface is deprecated since Symfony 5.1

Bundle version: 5.0.0
Symfony version: 5.1.0

Description
User Deprecated: The "Scheb\TwoFactorBundle\Security\Authentication\RememberMe\RememberMeServicesDecorator" class implements "Symfony\Component\Security\Http\Logout\LogoutHandlerInterface" that is deprecated since Symfony 5.1.

Configurable custom form renderer

It should be possible to configure a custom renderer service for each authentication provider (GA, TOTP, email) to customize form rendering with additional logic. Use-cases for this

  • Have a different template per firewall, when 2fa is used in multiple firewalls. See #58.
  • Inject additional template variables into the template
  • Instead of rendering a template, return any kind of Response

Support for JSON needed for the 2fa_login endpoint

I'm using scheb/2fa in a Restful API; POST payloads are in json format in my API. Unfortunately, scheb/2fa does not support a JSON formatted payload in the 2fa_login endpoint.

I was looking at the source code and noticed that Scheb\TwoFactorBundle\Security\Http\Authenticator\TwoFactorAuthenticator has a method authenticate() that is initializing the object TwoFactorCodeCredentials. The TwoFactorCodeCredentials accepts a string code in the constructor. Currently, this string code is fetched with the help of TwoFactorFirewallConfig, to be precise method getAuthCodeFromRequest().

The method getAuthCodeFromRequest(Request $request) lives in class Scheb\TwoFactorBundle\Security\TwoFactor\TwoFactorFirewallConfig. In the definition of this method I can see that it uses arameterBagUtils::getRequestParameterValue($request, $this->getAuthCodeParameterName()) ?? ''.

The ParameterBagUtils lives in namespace Scheb\TwoFactorBundle\Security\Http.
In the ParameterBagUtils class I can see the method getRequestParameterValue.

The getRequestParameterValue method is not flexible enough to support for a json formatted Request payload
The improvement here would be to add support for json formatted Request payload; if my idea would be honored then I believe that scheb/2fa would be more restful.

2fa stopped working after upgrade to symfony 5.2

Bundle version: 5.2.0
Symfony version: 5.2.0

Description
After upgrading from Symfony 5.1.9 to Symfony 5.2.0 the 2fa stopped working:

two-factor-auth via email:

  • no token is generated
  • no email is send

two-factor-auth via google authenticator:

  • token is never accepeted as valid token

After downgrading to Symfony 5.1.9 everything started to work as before.

Are there any configuration changes needed for symfony 5.2.0? I am not sure if this behavior is a bug, or some configuration i need to adjust for Symfony 5.2.0.

Thank you!!

Auto-enabling trusted_device when remember me is on

I am trying to keep the 2FA page as simple as possible and so would rather not add yet another checkbox people have to think about. I therefore figured I would enable the trusted_device flag when remember me is active, as that kind of has the same intent.

It seems count($token->getAttribute('remember_me_cookie')) > 0 can be used to check for the remember me presence, but that is not accessible in the template, so can't easily be done without overriding the whole form controller. If this request sounds reasonable to you it'd be nice to expose this as a flag to the template perhaps?

Enable 2FA form?

Bundle version: latest
Symfony version: 5.2
PHP version: 7.4

Hello,

I am trying to setup your bundle, but it seems I am facing some configuration issues.
Basically, I want to enable the TOTP algo, but I don't understand which file you are talking about in https://github.com/scheb/2fa/blob/5.x/doc/providers/totp.md ?
Is it something like a file to create ./config/packages/scheb_2fa.yaml (should I still use the parameter of your old bundle scheb_two_factor)? Anyhow, it is not referenced by flex. So should I create something myself ?

Additionally, I tried to access the controller behind my route: /login/2fa
It says "User is not in a two-factor authentication process.".
I know it is one of the FAQ issue, but I have no clue where is the form to enable this access for my user. (I tried to add a dummy totpSecret in my database for a given user, but it didn't work )
Is there something already prepared like a ready-to-use layout/form ?

Thanks a lot for your feedback

2FA Bundle with JWT Bundler

Bundle version: 5.3.1
Symfony version: 4.4
PHP version: 7.4
Using authenticators (enable_authenticator_manager: true): YES

Description
Hi, thank you for your great work! I want to use 2FA via Email. Now I have 3 steps in my API:

  1. User register with email and password.
  2. Login user and get the JWT token.
  3. Use the API with the given Bearer Token.

But, I would like to add 2 more steps:

  1. User register with email and password.
  2. Login user with email and password.
  3. If success: send an E-Mail with 2FA code to user
  4. Login User but with third parameter for 2FA from Email. email, password, 2fa_token
  5. If success - response with a bearer token.
  6. Use the API with the given Bearer Token.

My security.yml

security:
  providers:
    app_user_provider:
      id: app.security.user_provider

  firewalls:
    login:
      pattern: ^/api/v1/login
      stateless: true
      anonymous: true
      provider: app_user_provider
      json_login:
        check_path: /api/v1/login
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure

    register:
      pattern: ^/api/v1/register
      stateless: true
      anonymous: true
      provider: app_user_provider

    api:
      pattern: ^/api/v1
      stateless: true
      provider: app_user_provider
      guard:
        authenticators:
          - lexik_jwt_authentication.jwt_token_authenticator
      two_factor:
        prepare_on_login: true
        prepare_on_access_denied: true
        check_path: 2fa_login_check

    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js)/
      security: false

    main:
      anonymous: true

  access_control:
    - { path: ^/api/v1/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api/v1/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api/v1,       roles: IS_AUTHENTICATED_FULLY }

Additional Context
I tried to use an example from here: https://github.com/scheb/2fa/blob/5.x/doc/api.md

But it doesn't work:
Bildschirmfoto 2021-01-22 um 13 15 36

The event doesn't provide to get the token interface.

How can I do this? Thank you a lot in advance.

The road to a authenticator-based two-factor authentication

The bundle is supporting authenticator-based security since v5

(Shamelessly stealing the title from Wouter's symfony/symfony#39308)

I create this issue to let everyone know what the plan is with bundle's next major version and authenticator-based security.

The bundle is supporting authenticator-based security since 5.0.0. It's an experimental feature in Symfony, also the bundle's implementation has been experimental. I've continously adopted new features arriving with Symfony 5.x minor releases to make the authenticator intgeration better and cleaner. Because of that, if you want to use authenticator-base security with the current bundle version (at time of writing 5.7.0), it requires at least Symfony 5.2, with #61 that will be Symfony 5.3.

Symfony maintainers plan to remove the old security system and make authenticator-based security officially the new security system in Symfony 6, planned for November 2021. I plan to have the next major bundle version (also 6) to be released close to that. That version will be the one supporting Symfony 6, bundle version 5 will not support Symfony 6.

The goal for this upcoming major version is to have finally a clean implementation for two-factor authentication. I want to remove any kind of "hacks" the bundle does to make two-factor authentication work. These are mostly DIC hacks to decorate (internal) services from Symfony's security to inject some extra 2fa-bundle logic. Furthermore, since bundle version 6 is targeting Symfony 6, I'll remove support for the old security system. That allows me to remove a big chunk of code that's no longer needed. Also various compatibility layers for older Symfony versions can be removed.


Summary:

  • Bundle version 6 releases close to Symfony 6
  • That bundle version will target Symfony 6
  • To support early testing, I'm likely going to release a v6-beta version of the bundle, which is supporting Symfony's respective beta and RC versions
  • Bump up minimum supported PHP version to 8.0 (*) and adopt new language features (i.e. property types)
  • Drop support for the old Symfony security system, only the authenticators-based system will be supported
  • Symfony 5.4 will be supported as a migration path, but only authenticator security. If you try using Symfony 5.4 and the old security system, there will be an error
  • Clean up code that is no longer needed and remove compatibility layers

* Symfony 6 will require PHP 8.0 (symfony/symfony#40389)


Todo:

  • Remove classes related to old security system
  • Remove Symfony compatibility layers for Symfony <= 5.4
  • Remove non-authenticators config from integration tests and app readme
  • Make sure the bundle errors when used on Symfony 5.4 with old security system
  • Add passport to the authentication context
  • Update build matrix (unit + integration test suite)
  • Upgrade notes, mention switch to authenticator security
  • Beta release: Allow Symfony beta-level releases (composer.json in repo root + sub-packages + app folder )
  • Stable release: Only Symfony stable-level releases (composer.json in repo root + sub-packages + app folder)

Ability to whitelist certain users

Hey,

I'd like to have the ability to exclude certain users or user groups from two factor authentication.
There is already a PreferredProviderInterface, which could be extended to skip two factor authentication for users where getPreferredTwoFactorProvider() returns null. Another possible approach would be to provide a more generic TwoFactorAuthWhitelistProvider interface, which gets passed the authentication context and then decides if two factor auth is required or not:

interface TwoFactorAuthWhitelistProvider
{
    /*
     * Return false to exempt this login request from two factor authentication.
     */
    function shouldPerformTwoFactorAuth(AuthenticationContextInterface $context): bool;
}

This is basically a generalization of the IpWhitelistProviderInterface.

What do you think?

Symfony v5.3.0-BETA1 issue with remember me.

Bundle version: dev-master
Symfony version: v5.3.0-BETA1
PHP version: 8.0.6-dev
Using authenticators (enable_authenticator_manager: true): YES

Description

After upgrading (as a test) to Symfony v5.3.0-BETA1 on a normally working large application, the first issue I come against is this:

Screenshot 2021-04-21 at 19 57 48

To Reproduce

Have a working site, with Scheb/2fa 5.x and upgrade to Symfony v5.3.0-BETA1

Additional Context

Im happy to debug for you - let me know what you might need.

Trusted Devices do not work with Symfony 4

Bundle version: 5.0.0
Symfony version: 5.1.1

Description
If I see it correctly, the trusted device libraries does not work with Symfony before 5, because it depends on the new LoginSuccessEvent and Passports.

I think it would be great to mention this in the compatibility or make it work with the "old" security system as well.

Additional Context
Thank you very much for your work on this library, very much appreciated. It works quite well for me even though I am using it in an REST API, which is outside of the scope of your project, if I understand it correctly :-)

Support endroid/qr-code version 4

Description

Update scheb/2fa-qr-code to support endroid/qr-code version 4. If reasonable, keep compatibility with version 3 within the bundle's version 5.x.

BC break in 5.4.0 release

Bundle version: 5.4.0

Description

Hi @scheb, it looks like that the 5.4.0 release introduces a BC break because of the added constructor argument to the TwoFactorFirewallConfig.

Instantiating the class will break when using the old signature with 3 arguments instead of 4.

This issue occurred in the Contao CMS. For more context, please have a look at contao/contao#2680

For the Contao CMS, we already added a conflict to the composer.json to prevent installing the 5.4.0 from now and we will roll out a fix soon, so there won't be any long-term issues.
However, if you consider this issue as severe, maybe we can bring a fix into scheb/2fa? 😊

Installing 2 factor

Bundle version: 5.1
Symfony version: 5.1.7

Description
My symfony craches with no error.
In my php error log on the server i get this error.

There is no extension able to load the configuration for "scheb_two_factor" in /config/packages/scheb_two_factor.yaml"
Looked for namespace "scheb_two_factor" Looked for namespace "scheb_two_factor", found ""framework", "sensio_framework_extra", "doctrine", "doctrine_migrations", "security", ..........

Argument #3 ($token) must be of type TokenInterface, null given

scheb/2fa: v5.4.1
symfony: v5.2.2
PHP version: 8.0.3-dev (unrelated, had issues before 8)
Using authenticators (enable_authenticator_manager: true): YES

Description

I use Sentry.io to capture unhandled exceptions.

VERY rarely I get this same exception. Previously I just ignored it, today I had time to report to you for your thoughts.

The request is a POST to the /2fa_check route with a param of _auth_code and a 6digit user supplied code when the exception is thrown.

TypeError: Scheb\TwoFactorBundle\Security\Http\Authenticator\TwoFactorAuthenticator::dispatchTwoFactorAuthenticationEvent(): Argument #3 ($token) must be of type Symfony\Component\Security\Core\Authentication\Token\TokenInterface, null given, called in /var/www/current/vendor/scheb/2fa/src/bundle/Security/Http/Authenticator/TwoFactorAuthenticator.php on line 169
#17 /var/www/current/vendor/scheb/2fa/src/bundle/Security/Http/Authenticator/TwoFactorAuthenticator.php(174): Scheb\TwoFactorBundle\Security\Http\Authenticator\TwoFactorAuthenticator::dispatchTwoFactorAuthenticationEvent
#16 /var/www/current/vendor/scheb/2fa/src/bundle/Security/Http/Authenticator/TwoFactorAuthenticator.php(169): Scheb\TwoFactorBundle\Security\Http\Authenticator\TwoFactorAuthenticator::onAuthenticationFailure
#15 /var/www/current/vendor/symfony/security-http/Authentication/AuthenticatorManager.php(235): Symfony\Component\Security\Http\Authentication\AuthenticatorManager::handleAuthenticationFailure
#14 /var/www/current/vendor/symfony/security-http/Authentication/AuthenticatorManager.php(190): Symfony\Component\Security\Http\Authentication\AuthenticatorManager::executeAuthenticator
#13 /var/www/current/vendor/symfony/security-http/Authentication/AuthenticatorManager.php(145): Symfony\Component\Security\Http\Authentication\AuthenticatorManager::executeAuthenticators
#12 /var/www/current/vendor/symfony/security-http/Authentication/AuthenticatorManager.php(125): Symfony\Component\Security\Http\Authentication\AuthenticatorManager::authenticateRequest
#11 /var/www/current/vendor/symfony/security-http/Firewall/AuthenticatorManagerListener.php(42): Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener::authenticate
#10 /var/www/current/vendor/symfony/security-http/Firewall/AbstractListener.php(26): Symfony\Component\Security\Http\Firewall\AbstractListener::__invoke
#9 /var/www/current/vendor/symfony/security-bundle/Security/LazyFirewallContext.php(60): Symfony\Bundle\SecurityBundle\Security\LazyFirewallContext::__invoke
#8 /var/www/current/vendor/symfony/security-http/Firewall.php(113): Symfony\Component\Security\Http\Firewall::callListeners
#7 /var/www/current/vendor/symfony/security-http/Firewall.php(86): Symfony\Component\Security\Http\Firewall::onKernelRequest
#6 /var/www/current/vendor/symfony/event-dispatcher/EventDispatcher.php(270): Symfony\Component\EventDispatcher\EventDispatcher::Symfony\Component\EventDispatcher\{closure}
#5 /var/www/current/vendor/symfony/event-dispatcher/EventDispatcher.php(230): Symfony\Component\EventDispatcher\EventDispatcher::callListeners
#4 /var/www/current/vendor/symfony/event-dispatcher/EventDispatcher.php(59): Symfony\Component\EventDispatcher\EventDispatcher::dispatch
#3 /var/www/current/vendor/symfony/http-kernel/HttpKernel.php(133): Symfony\Component\HttpKernel\HttpKernel::handleRaw
#2 /var/www/current/vendor/symfony/http-kernel/HttpKernel.php(79): Symfony\Component\HttpKernel\HttpKernel::handle
#1 /var/www/current/vendor/symfony/http-kernel/Kernel.php(195): Symfony\Component\HttpKernel\Kernel::handle
#0 /var/www/current/public/index.php(31): null

To Reproduce

Sorry I cannot tell you how its produced.

Additional Context
https://sentry.io/share/issue/66fe1c30a92046f6b7f833e31f3695ac/

How to change default path 2fa

Bundle version: x.y.z
Symfony version: x.y.z

Description

  • Hello, Currently I have a path that is localhost/admin/2fa. But I want to change 2fa to another-name -> localhost/admin/another-name. How can I do it, I tried to change it in routes.yaml, but it didn't work. Could you please help me? Thank you

Additional Context

  • My routes.yaml file
2fa_login:
    path: '/%eccube_admin_route%/2fa'
    defaults:
        # "scheb_two_factor.form_controller" references the controller service provided by the bundle.
        # You don't HAVE to use it, but - except you have very special requirements - it is recommended.
        _controller: Customize\Controller\Admin\AdminController::get2FA

2fa_login_check:
    path: '/%eccube_admin_route%/2fa_check'

Logout path not accessible during two-factor authentication when using CSRF protection

Bundle version: 5.1.1
Symfony version: 4.4.11

Description
I'm using CSRF protection on my logout URL. This is done by using csrf_token_generator: security.csrf.token_manager instead of true on the logout setting of the firewall. The logout url looks something like: https://mysite/logout?_csrf_token=9UF0Jk4bgAMuTBQcUGkKVNila9U1Vv5vrzC5-3WmQZc

When on the login page of the two-factor authentication, I can't get back to the login page. In the TwoFactorAccessDecider this part is not working with the GET variable, it only works on absolute path's.

// Let the logout route pass
$logoutPath = $this->makeRelativeToBaseUrl($this->logoutUrlGenerator->getLogoutPath(), $request);
if ($this->httpUtils->checkRequestPath($request, $logoutPath)) {
    return true;
}

To Reproduce
Steps to reproduce the behavior:

  1. Enable csrf_token_generator on the logout URL.
  2. Enable 2FA
  3. Login to get to the 2FA login
  4. Press back to return to the login screen
  5. Access is denied and page refreshes

Add digest method in configuration

Hi, in the GoogleTotpFactory:createTotpForUser method, the digest way is in hard 'sha1'.
Is it possible to put this value in the configuration, in the google section?

Thank you

PHP 8.0 build

Add a proper build on PHP8.0 (not nightly) as soon as it's released and made available on Travis-CI.

Integrate bundle with new Symfony 5.2 features

Symfony 5.2 contains multiple of my pull requests, which allow for a way less hacky integration with Symfony security.

symfony/symfony#37336 and symfony/symfony#37337
To replace the hacky AccessListenerCompilerPass for injecting a firewall-level listener at the right position. Keep AccessListenerCompilerPass for backwards compatibility with Symfony versions < 5.2.

symfony/symfony#37359
Affects only the experimental "authenticator" system. Replace AuthenticatorDecoratorCompilerPass, hook into the new AuthenticationTokenCreatedEvent instead to replace the security token with a TwoFactorToken. The bundle's minimum supported Symfony version for using the new authenticator system will become 5.2 then, as I'm not intending to keep the existing Symfony 5.1 integration (it's an experiemental feature, so BC breaks are to be expected).

TODO: Remove entry point workaround, when Symfony 5.2.1 is released.

Cannot Implementing a custom two-factor authenticator

Bundle version: x.y.z
Symfony version: x.y.z

Description

  • Hi. Currently, I cannot implement a custom two-factor authenticator. I tried to configure my own two-factor authenticator follow your redirection, but it didn't work. I think, maybe it's wrong in my service file. Could you please help me? Thank you

Additional Context

  1. I created a service, then implement Scheb\TwoFactorBundle\Security\TwoFactor\Provider\TwoFactorProviderInterface and add requires methods to follow your direction
  2. I register it in my service file:
    role.custom_two_factor_authenticator_service:
        alias: Plugin\Role\Service\CustomTwoFactorAuthenticatorService
        tags: ['scheb_two_factor.provider']

-> Maybe, I'm wrong when registering it in the service file or miss some steps. Could you help me out, please? Thank you

[idea] Auto close PR on sub-repositories

Some developers may think that PRs have to be submitted in sub-repositories.
To avoid that, ou should automatically close these PRs and warn the devlopers.

In general, I use Stale, a free bot from the marketplace, with the following configuration:

daysUntilStale: 1
daysUntilClose: 1
staleLabel: wontfix
markComment: >
  This issue has been automatically marked as stale because this repository
  is a read-only repository and nobody will take care of it.
  **Please submit it to the main repository.**
  Thank you for your contributions.
closeComment: false

That configuration has to be placed in the .github/stale.yml of each split folder (e.g. src/backup-code/.github/stale.yml)

Lazy firewall causes provider to not be prepared

Bundle version: 5.2.0
Symfony version: 5.2.0 (lazy was introduced in 5.1)
PHP version: 8.0.1-dev
Using authenticators (enable_authenticator_manager: true): YES

Description

Every thing works perfect. Until you add lazy: true in your firewall.

To Reproduce

Every thing works perfect. Until you add lazy: true in your firewall.

After you add that, and attempt to login, on providing totp code you get An authentication exception occurred

If you debug that you will get the real reason The two-factor provider "totp" has not been prepared.

If you toggle lazy: false everything works fine

back to lazy: true and login is broken again.

https://symfony.com/doc/current/security.html

New in version 5.1: The lazy: true option was introduced in Symfony 5.1. Prior to version 5.1, it was enabled using anonymous: lazy

The lazy anonymous mode prevents the session from being started if there is no need for authorization (i.e. explicit check for a user privilege). This is important to keep requests cacheable (see HTTP Cache).

New Authenticator Manager with "Two-Factor Authentication in an API" approach does not prepare TOTP provider

Bundle version: 5.2.1
Symfony version: 5.2.1
PHP version: 7.4
Using authenticators (enable_authenticator_manager: true): YES

Description
So I use this approach from docs: https://github.com/scheb/2fa/blob/5.x/doc/api.md to implement 2FA in JSON api for Single-Page App. Handlers are basically the same as in docs.

    enable_authenticator_manager: true
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: .*
            lazy: true
            provider: app_users
            logout:
                path: api_logout
                invalidate_session: true
            two_factor:
                auth_form_path: api_2fa_login
                check_path: api_2fa_check
                prepare_on_login: true
                prepare_on_access_denied: true
                authentication_required_handler: App\Security\TwoFactorRequiredHandler
                success_handler: App\Security\TwoFactorSuccessHandler
                failure_handler: App\Security\TwoFactorFailureHandler
            json_login:
                check_path: api_login
                success_handler: App\Security\LoginSuccessHandler
    access_control:
        - { path: '^/logout', roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: '^/login', roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: '^/2fa', roles: IS_AUTHENTICATED_2FA_IN_PROGRESS }
        - { path: '^/', roles: IS_AUTHENTICATED_ANONYMOUSLY }

Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken is also enabled in config.

Sending a code to check_path results in AuthenticationException with the following message:

The two-factor provider "totp" has not been prepared.

I've seen a recent bugreport (#39) with lazy firewalls but changing lazy: true to false does not fix it (I'm also on newest version). So I guess it has something to do with prepare_on_login.

I reproduced it also on a simple website-skeleton so it's not something weird in my real app.

Removing enable_authenticator_manager fixes the issue.

Access URL /2FA with a logged in user without throwing a 403 error

Bundle version: 5.4.2
Symfony version: 4.4.19
PHP version: 7.4.19
Using authenticators (enable_authenticator_manager: true): NO (becaise of 4.4 SF version)

Description
Some of my users have a favorite link in theirs browser, and they are using every day to reach my website pointing on http://mywesite/2fa

In case of the user is not logged in, the system will redirect him to the login webpage, perfect. But in case of the user is already logged in, he will hit a 403 error (forbidden) because of the security.yml rule :

    # This ensures that the form can only be accessed when two-factor authentication is in progress
    - { path: ^/(%app.locales%)/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS, requires_channel: "%requires_channel%" }

Is there a way to redirect to a specific route if the user is already logged in and try to reach the "2fa"URL?

Additional Context
None

Trusted device is ignored

Bundle version: 5.0
Symfony version: 5.0.10

Description
Hi,

I was successful at implement the bundle and everything works as a charm. However, I am trying to implement the trusted_device functionality and it seems it is ignoring it. I debugged the program when log in and it enters in the function getTrustedTokenVersion I implemented in my provider but only to check if there is a trusted_device. It looks like the program neither write the field trustedVersion on the database nor generates any cookie.

This is my firewall in security.yaml:

admin:
         pattern:    ^/[A-Za-z]{2}/admin
         anonymous: ~
         provider:    admin
         two_factor:
             auth_form_path: admin_2fa_login
             check_path: admin_2fa_login_check
         form_login:
             use_referer: true
             default_target_path: admin_home
             login_path:    admin_login
             check_path:    admin_login_check
         logout:
             path: admin_logout
             target: admin_login
             invalidate_session: false

This is my provider in security.yaml:

admin:
            entity:
                class: App\Entity\Admin
                property: user

This is my scheb_two_factor.yaml:

scheb_two_factor:

  trusted_device:
    enabled: true                 # If the trusted device feature should be enabled
    lifetime: 5184000              # Lifetime of the trusted device token
    cookie_name: wlAdmin_trust    # Name of the trusted device cookie
    cookie_secure: false           # Set the 'Secure' (HTTPS Only) flag on the trusted device cookie
    cookie_same_site: "lax"        # The same-site option of the cookie, can be "lax", "strict" or null
    cookie_domain: ".mydomain.com"  # Domain to use when setting the cookie, fallback to the request domain if not set
    cookie_path: "/"

  google:
    enabled: true                  # If Google Authenticator should be enabled, default false
    server_name: "Name of the server"       # Server name used in QR code
    issuer: "Name of the issuer"            # Issuer name used in QR code
    digits: 6                      # Number of digits in authentication code
    window: 1                      # How many codes before/after the current one would be accepted as valid
    template: A_General/Default/2fa_form.html.twig   # Template used to render the authentication form

Composer install warning

  • Installing scheb/2fa-qr-code (v5.1.1): Loading from cache

Package twig/extensions is abandoned, you should avoid using it. No replacement was suggested.
Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead.
Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead.

I dont know where I should post this. issue.

Not redirecting to 2fa form when firewall is lazy

Bundle version: 5.x
Symfony version: 5.1
PHP version: Any
Using authenticators (enable_authenticator_manager: true): No

Description

When the lazy option is enabled on the firewall, 2fa-bundle potentially fails to redirect the user to the 2fa form.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.