使用Symfony2中的OAuth2 client_credentials进行身份验证

时间:2016-02-11 04:35:59

标签: symfony oauth-2.0

我想使用OAuth2和Symfony2(实际上是Symfony3)对我的API进行服务器到服务器身份验证。我正在使用FOSOAuthServerBundle

远程服务器不会代表任何用户发出请求,因此我认为client_credentials是正确的授权类型。

我创建了一个客户端,并且能够获取访问令牌。但是,我遇到了使用client_credentials令牌保护端点的问题。这就是我的security.yml

中的内容
firewalls:
    api:
        pattern:    ^/api/v1
        fos_oauth:  true
        stateless:  true
        anonymous:  false # can be omitted as its default value

access_control:
    - { path: ^/api/v1, roles: [ IS_AUTHENTICATED_FULLY ] }

当我尝试使用Bearer Authorization标头访问/api/v1中的内容时,出现错误

Full authentication is required to access this resource

以下是示例请求:

GET /app_dev.php/api/v1/user HTTP/1.1
Host: local.dev
Authorization: Bearer NDFhMzViZjQ2YjMyYjFlNzBjZTZiMTU2ZjdhY2I4ZmZhZjY2MmVkMjU3NzNjNDE2NGI2YzEzMWFjZGQ5MzE4NA

我认为问题是因为我使用的是client_credentials,所以没有用户,因此IS_FULLY_AUTHENTICATED不是真的。如果是这种情况,如何在没有用户的情况下对客户端进行身份验证?

注意:我也尝试删除

access_control:
    - { path: ^/api/v1, roles: [ IS_AUTHENTICATED_FULLY ] }

然后尝试使用以下内容访问控制器中的客户端详细信息:

$tokenManager = $this->get('fos_oauth_server.access_token_manager.default');
$accessToken = $tokenManager->findTokenByToken(
    $this->get('security.token_storage')->getToken()->getToken()
);
$client = $accessToken->getClient();

然后我收到错误:

Attempted to call an undefined method named "getToken" of class "Symfony\Component\Security\Core\Authentication\Token\AnonymousToken"

2 个答案:

答案 0 :(得分:2)

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:  false

access_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.html

access_control:
    - { path: ^/oauth/v2/token, roles: [ IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_FULLY ] }
    - { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }

答案 1 :(得分:0)

我想出了一种在控制器内部进行身份验证的方法,但我不确定这是否是实现我想要的最佳方式。似乎FOSOAuthServerBundle应该能够为我做所有这些检查。

<?php

namespace 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;
    }
}