5

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: userin 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_scopesentry. 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?

Hugo Nogueira
  • 1,298
  • 1
  • 12
  • 24

4 Answers4

2

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.

Community
  • 1
  • 1
mansoor.khan
  • 2,309
  • 26
  • 39
  • 1
    But how can I know in my frontend if my user is an admin or not? – Hugo Nogueira Feb 03 '16 at 11:34
  • In Twig template you can do this : `{% if is_granted('ROLE_ADMIN') %} {# Some text #} {% endif %}` Or implement an isGranted method in your User entity ( http://stackoverflow.com/questions/9080530/get-role-of-a-user-not-logged-in-twig-symfony2) – Delphine Feb 03 '16 at 15:01
  • Delphine, you didn't get my point. I use a REST Api and request data from my javascript frontend. I need to do a API request and then get what is my user role. This data is not coming when I do /me request. When we request the API token, the return (as mentioned above) comes with a "scope" parameter. But I don't know if it's the right way and looks like it's not working. – Hugo Nogueira Feb 04 '16 at 12:52
  • @hugomn How are you consuming the API? – mansoor.khan Feb 04 '16 at 13:01
  • I request an access token at the login, and then I use the access token in all subsequent requests. After I get the token, I need to know whether the user is an admin or not. – Hugo Nogueira Feb 04 '16 at 16:47
1

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;
}
vardius
  • 6,326
  • 8
  • 52
  • 97
  • You didn't get my point. I need a way to know what is the user's role in my frontend. So, when I request the token I need to know also in which roles user has been granted. We already have the 'scope' parameter in the token, but looks like it's not working as expected (I've put an example above). – Hugo Nogueira Feb 03 '16 at 11:33
  • can't you just get the current user data from api ? – vardius Feb 03 '16 at 12:36
  • No... when you request user details FOSOAuthServerBundle does not return scopes or rules. If you pay attention to my question, you'll see that /token request gives you the scopes, but it's not working las expected. – Hugo Nogueira Feb 04 '16 at 12:50
  • i mean with a different request, create a action where you return user by token – vardius Feb 04 '16 at 12:55
  • But when I return an user object, it doesn't have information about the user's roles. Also, I need to understand what is the "scopes" parameter in the token response used for. – Hugo Nogueira Feb 04 '16 at 16:49
1

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.

bblue
  • 545
  • 5
  • 24
0

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

Shawn Northrop
  • 5,826
  • 6
  • 42
  • 80