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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With