I'm working on a project with Symfony 5. I created the User entity, created the authentication flow on security.yaml and all works well: if user wants to access to protected area, login page was shown and authentication process works! So good!
Now, I want to build an API REST with FOSRest Bundle. I've created a specific controller for expose some routes:
/**
* @Rest\Route("/api")
*
*/
class APICustomController extends AbstractController
{
...
/**
* @Rest\Get("/shoes")
* @param Request $request
* @Method({"GET"})
*
* @return JsonResponse
*/
public function getShoes(Request $request){
....
return JsonResponse::fromJsonString(array('msg' => 'OK'));
}
}
Here my security.yaml
security:
enable_authenticator_manager: true
encoders:
App\Entity\User:
algorithm: auto
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_VIEWER: ROLE_USER
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: username
api_user_provider:
entity:
class: App\Entity\User
property: api_token
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
guard:
authenticators:
- App\Security\DealmapLoginAuthenticator
logout:
path: app_logout
api:
stateless: true
lazy: true
guard:
authenticators:
- App\Security\TokenAuthenticator
pattern: ^/api/
provider: api_user_provider
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, role: IS_AUTHENTICATED_FULLY }
I followed the steps given here: https://symfony.com/doc/current/security/guard_authentication.html
The problem is the call below
curl -X GET \
http://www.example.com/api/shoes \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-H 'x-auth-token: test'
it is protected by the main firewall (so it returns me the login page), and not by api as expected. I expect to receive an error message in json format.
What's wrong??
Thanks in advance
OK, I found the solution!
I'm posting it here in case someone might need it in the future.
The configuration was all correct, but the reason why the path /api/shoes was managed by the main firewall was due to the order of execution of the rules.
The firewall main handles all the rules, while the api one handles only the ones with the ^/api pattern, so the most stringent rules should go first in the firewall definition in the security.yaml, as below:
security:
enable_authenticator_manager: true
encoders:
App\Entity\User:
algorithm: auto
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_VIEWER: ROLE_USER
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: username
api_user_provider:
entity:
class: App\Entity\User
property: api_token
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api:
stateless: true
lazy: true
guard:
authenticators:
- App\Security\TokenAuthenticator
pattern: ^/api/
provider: api_user_provider
main:
lazy: true
provider: app_user_provider
guard:
authenticators:
- App\Security\DealmapLoginAuthenticator
logout:
path: app_logout
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, role: IS_AUTHENTICATED_FULLY }