Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony 4: Do not redirect to /login for matching routes

I have a Symfony Application that has two primary sections. / (everything under root e.g. /userProfile) and /api/ (everything under the /api route e.g. /api/userInfo/3).

I'm using Twig to render the actual pages under root, and a simple JsonResponse to render everything under /api. Currently, when a user tries to access any page / resource when they are not logged in, they are redirected to /login and then to the page / resource they requested.

I'd like to alter this behavior for all resources under /api. What I'd like /api/whatever to do is either return the requested resource (if logged in), or return a 401 if not logged in. I'd like to continue to redirect to /login for all other routes.

(NOTE: the /api routes are not "RESTful" API routes per ce. They are "internal" API routes that the UI uses to request data it needs to render the various pages. So it's safe to assume that a user would have logged in via the normal login form prior to their client requesting one of these routes.)

Here's my security.yaml:

security:
    providers:
        db_provider:
            id: database_user_provider
    encoders:
        App\Utility\Security\DatabaseUser: bcrypt
    access_decision_manager:
        strategy: unanimous
        allow_if_all_abstain: false
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            context: main
            form_login:
                provider: db_provider
                login_path: login
                check_path: process_login
                default_target_path: manage
                use_referer: true
                failure_handler: App\Utility\Security\AuthenticationFailureHandler
            user_checker: App\Utility\Security\UserChecker
            anonymous: ~
            logout:
                path: logout
                target: login
            access_denied_handler: App\Utility\Security\AccessDeniedHandler  #Turn this off in dev.

    # 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: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/forgot, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/%app.locales%, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, roles: ROLE_USER }

I've tried adding a new firewall context of api:

api:
    pattern: ^/api
    context: main

    # If I don't include this (from "main" firewall), I get an error stating no UserAuthenticationListener has been provided for secuirty.firewall.api.
    form_login:
        provider: db_provider
        login_path: login
        check_path: process_login
        use_referer: true
        failure_handler: App\Utility\Security\AuthenticationFailureHandler

Symfony complains if I do not include login_path and check_path.

So, how do I tell Symfony to simply fail and return a 401 when a user is not logged in (or session has expired), when (and only when) they are accessing a route under /api?

Thanks.

like image 287
mcmurphy Avatar asked Jan 17 '26 23:01

mcmurphy


1 Answers

To get this working, move the api firewall above main.

We can then set it up on security.yaml with the following structure:

api:
  pattern: ^/api
  context: main
  stateless: false
  anonymous: true
  guard:
     authenticators:
        - App\Security\ApiAuthenticator
  provider: db_provider

The other thing we need is an implementation of AuthenticatorInterface. e.g. the App\Security\ApiAuthenticator

We only need to implement the following two methods from the interface: start and supports

public function start(Request $request, AuthenticationException $authException = null)
{
    return new Response('', Response::HTTP_UNAUTHORIZED);
}

public function supports(Request $request)
{
    return false;
}

Note: The above implementation was tested on Symfony 4 (4.3)

like image 105
Adoni Avatar answered Jan 19 '26 17:01

Adoni



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!