authenticationsymfonylexikjwtauthbundle

Symfony 6: make legacy json login and lexik jwt coexist


I am working on a legacy symfony project and I want to integrate jwt token authentication for my rest section and maybe for a full migration later, but I am facing an issue. I defined my both firewalls in security.yaml and each of them are working fine when the other one is deactivated, but when I configure them both in the file, the first one defined in the file is taking over the authentication and the second one doesn't whatever authentication type it is.

Here's what I've done in the file so far:

security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
    Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
    App\Entity\User:
        algorithm: auto

# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
    # used to reload user from session & other features (e.g. switch_user)
    app_user_provider:
        entity:
            class: App\Entity\User
firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    main:
        lazy: true
        provider: app_user_provider
        json_login:
            check_path: app_login
            username_path: email
            password_path: password
            success_handler: AuthenticationSuccessHandler
        logout:
            path: app_logout
        two_factor:
            prepare_on_login: true
            prepare_on_access_denied: true
            authentication_required_handler: TwoFactorAuthenticationRequiredHandler
            success_handler: TwoFactorAuthenticationSuccessHandler
            failure_handler: TwoFactorAuthenticationFailureHandler
            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
            enable_csrf: true 
    restlogin:
        stateless: true
        pattern: ^/rest/login
        json_login:
            check_path: login_check
            username_path: email
            password_path: password
            success_handler: lexik_jwt_authentication.handler.authentication_success
            failure_handler: lexik_jwt_authentication.handler.authentication_failure
        # custom_authenticator: App\Security\RestLoginAuthenticator
    rest:
        pattern: ^/rest
        stateless: true
        jwt: ~    
    restsignup:
        pattern: ^/rest/signup
        stateless: true

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

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

# The path patterns shown here have to be updated according to your routes.
# IMPORTANT: ADD THESE ACCESS CONTROL RULES AT THE VERY TOP OF THE LIST!
access_control:
    # REST
    - { path: ^/rest/login, roles: PUBLIC_ACCESS }
    # - { path: ^/rest,       roles: IS_AUTHENTICATED_FULLY }
    # This makes the logout route accessible during two-factor authentication. Allows the user to
    # cancel two-factor authentication, if they need to.
    - { path: ^/, role: PUBLIC_ACCESS }
    - { path: ^/logout, role: PUBLIC_ACCESS }
    # 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: ^/admin, roles: ROLE_ADMIN }
    # - { path: ^/profile, roles: ROLE_USER }

Note: everything but restlogin, rest and restsignup is legacy code and I don't want to break anything already running.

And the routes structure is like this:

| /
  | login
  | register
  | logout
  | other legacy routes
  | rest/
       | login
       | signup
       | logout
       | other api routes

I thought the easiest way to solve this would be to tell the main firewall to ignore everything under the rest route and let the rest firewalls to handle this but I still get 401 errors when calling /rest/login despite the fact that I have set a public access in the access_control section. Please note that kind of the same behavior occurs as well when restlogin is placed above main in the file, the effect being that the getUser() function always returns null in the controller.

After many attempts I cannot figure out what I'm doing wrong in the configuration and didn't hear anywhere that symfony cannot support different authentication types in the same app.

Context: I am using lexik jwt authentication bundle 2.19 and symfony 6.2.

Does someone have any successfull experience using symfony built-in authentication and lexik jwt at the same time?


Solution

  • Solved by redefining the firewalls.