Authenticate with OAuth2 client_credentials in Symfony2
I spend a day struggling with the same situation (also Symfony 3). In the end the solution was quite simple for me.
In my security.yml I defined the following:
firewalls: # disables authentication for assets and the profiler, adapt it according to your needs dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: anonymous: ~ # activate different ways to authenticate # http_basic: ~ # http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate # form_login: ~ # http://symfony.com/doc/current/cookbook/security/form_login_setup.html oauth_token: pattern: ^/oauth/v2/token security: false oauth_authorize: pattern: ^/oauth/v2/auth # Add your favorite authentication process here form_login: provider: userprovider check_path: /oauth/v2/auth_login_check login_path: /oauth/v2/auth_login #anonymous: true #allow all requests api: pattern: ^/api fos_oauth: true stateless: true anonymous: falseaccess_control: - { path: ^/oauth/v2/token, roles: [ IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_FULLY ] } - { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
There was a little error in there. I left the main in the firewall open for all urls:
main: anonymous: ~ # activate different ways to authenticate
By removing the main and its childeren or defining a proper "pattern" which does not override the pattern of the api firewall, my OAuth2 server was working correctly.
Later I found out that the order of the firewalls is very important. Simply defining the API firewall rules before the main(default) firewall rules works like a charm. So the following is correct:
firewalls: # disables authentication for assets and the profiler, adapt it according to your needs dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false oauth_token: pattern: ^/oauth/v2/token security: false oauth_authorize: pattern: ^/oauth/v2/auth # Add your favorite authentication process here form_login: provider: userprovider check_path: /oauth/v2/auth_login_check login_path: /oauth/v2/auth_login #anonymous: true #allow all requests api: pattern: ^/api fos_oauth: true stateless: true anonymous: false main: anonymous: ~ # activate different ways to authenticate # http_basic: ~ # http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate # form_login: ~ # http://symfony.com/doc/current/cookbook/security/form_login_setup.htmlaccess_control: - { path: ^/oauth/v2/token, roles: [ IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_FULLY ] } - { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
I figured out a way to authenticate inside the controller but I am not sure if this is the best way to accomplish what I want. It seems like FOSOAuthServerBundle
should be able to do all these checks for me.
<?phpnamespace ApiBundle\Controller;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\Security\Core\Exception\TokenNotFoundException;use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;use FOS\OAuthServerBundle\Security\Authentication\Token\OAuthToken;use Symfony\Component\HttpFoundation\JsonResponse;class UserApiController extends Controller{ /** * @Route("/user", name="user") */ public function indexAction(Request $request) { $authenticationErrorResponse = $this->checkAuthAndGetErrorResponse($request); if ($authenticationErrorResponse) { return $authenticationErrorResponse; } // all good, now do something } private function checkAuthAndGetErrorResponse(Request $request) { $tokenManager = $this->get('fos_oauth_server.access_token_manager.default'); $bearerToken = $this->get('fos_oauth_server.server')->getBearerToken($request); if (!$bearerToken) { return new JsonResponse(['status' => 400, 'message' => 'Bearer token not supplied'], 400); } $accessToken = $tokenManager->findTokenByToken($bearerToken); if (!$accessToken) { return new JsonResponse(['status' => 400, 'message' => 'Bearer token not valid'], 400); } if ($accessToken->hasExpired()) { return new JsonResponse(['status' => 400, 'message' => 'Access token has expired'], 400); } // may want to validate something else about the client, but that is beyond OAuth2 scope //$client = $accessToken->getClient(); return null; }}