简体   繁体   中英

How to deal with ROLES and FOSOAuthServerBundle scopes

I have a basic api that authenticates users using FOSOAuthServerBundle. Users can have ROLE_USER and ROLE_ADMIN roles. Based on FOSOAuthServerBundle docs, the default behavior is to use scopes as roles, so I've thought that when I have a regular user, the bundle would return scope: user in the response, and when it's a admin user, would return scope: admin . But it's not working like this. The bundle is returning whatever is configured in the supported_scopes entry. Below is my config.yml .

fos_oauth_server:
    service:
        options:
            supported_scopes: user admin

My access_control section in security.yml is empty, and my firewalls section is below:

firewalls:
        users_create:
            pattern: ^/v1/users
            methods: [POST]
            security: false

        api:
            pattern:    ^/
            security: true
            fos_oauth:  true
            stateless:  true

access_control:
        # You can omit this if /api can be accessed both authenticated and anonymously

This way the bundle always return user admin as scope, even if the user does not have the ROLE_ADMIN role.

{
"access_token": "ZGQ2ODE5ZjAzNTZkOWY0OWMyNmZmODE4MjcwZTJmYjExNzY0NzQxOTRmMzk4NzA2Mjc2NjIyZmY1ZDgwMzk4NA"
"expires_in": 3600
"token_type": "bearer"
"scope": "user admin"
"refresh_token": "NmM5ZGFmNzBiNTNjYmQzMTQ1MTk0ODJjOTAxMWU0YWIwMzM1MzgyODg4ZTAzNTI5ZTk2MDc3OGU2MTg0MWZiMA"
}

What I'm I missing? Isn't the user role attached to token scope? Is there any better way to know if my user is an admin or not?

From the doc , the default behaviour is to map scopes with roles. In your case, the roles would be ROLE_USER and ROLE_ADMIN.

Now to restrict usage, you'd edit your security.yml file somewhat like this:

# app/config/security.yml
security:
    access_control:
        - { path: ^/api/super/secured, role: ROLE_ADMIN }
        - { path: ^/api/general, role: ROLE_USER }

To restrict access inside controller, you can use this:

if ($this->get('security.context')->isGranted('ROLE_ADMIN')) {
    // the user has the ROLE_ADMIN role, so act accordingly
}

Again from the doc ,

Now, clients will be able to pass a scope parameter when they request an access token.

Hope this helps.

UPDATE:

Look at this answer here to a similar question and this article on setting up FOSOAuthServerBundle . Pay close attention to the configuration part.

FOSOAuthServerBundle authenticate your user, based on token, so you dont have to worry about scopes, it is a different thing then roles. Inside your controller you can get $this->getUser() to get the current authenticated user. If this works then checking if isGranted works as well.

http://symfony.com/doc/current/book/security.html

public function helloAction($name)
{
    // The second parameter is used to specify on what object the role is tested.
    $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');

    // Old way :
    // if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
    //     throw $this->createAccessDeniedException('Unable to access this page!');
    // }

    // ...
}

In case of $this->getUser() doesnt work you will have to set fetch to EAGER on the AccessToken entity.

class AccessToken extends BaseAccessToken
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Client")
     * @ORM\JoinColumn(nullable=false)
     */
    protected $client;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\User", fetch="EAGER")
     */
    protected $user;
}

There is an issue on github that dates back to 2013 for this . If you read that issue and follow the links you will eventually end up with the user Spomky creating his own library and Symfony bundle and being suggested as a maintainer of the FOSAuthServerBundle. It appears the FOS organisation will merge Spomky's work into the next major version of FOSOAuthServerBundle once it is stable.

You can return the user account information along with the token.

Here is my approach to deal with a similar situation, I have created a proxy route that takes the username and password as request parameters, adds the oauth client info to the request and forwards the request to the Oauth Token Action. Then I take that info and return the user info along with the token.

/**
 * @REST\Post("/authorize")
 */
public function loginAction(Request $request){
    $request->request->add( array(
        'grant_type' => 'password',
        'client_id' => $this->container->getParameter('oauth_client_id') . "_" . $this->container->getParameter('oauth_client_random_id'),
        'client_secret' => $this->container->getParameter('oauth_client_secret')
    ));
    $tokenAction = $this->get('fos_oauth_server.controller.token')->tokenAction($request);
    $tokenObject = json_decode( $tokenAction->getContent() );

    if(key_exists('error_description', $tokenObject)){
        return $this->view(["error" => $tokenObject->error_description], 401);
    };


    $user = $this->getDoctrine()->getRepository('\App\Entity\Oauth\AccessToken')->findOneBy( array('token' => $tokenObject->access_token ) )->getUser();
    $return = array(
        "roles" => $user->getUser()->getRoles(),
        "access_token" => $tokenObject->access_token,
        "expires_in" => $tokenObject->expires_in,
        "token_type" => $tokenObject->token_type,
        "scope" =>$tokenObject->scope,
        "refresh_token" => $tokenObject->refresh_token
    );
    return $this->view($return);
}

I use this route for authentication / token generation, rather than using the route that comes with the OAuthBundle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM