bshaffer / oauth2-server-php

A library for implementing an OAuth2 Server in php
http://bshaffer.github.io/oauth2-server-php-docs
MIT License
3.26k stars 950 forks source link

User permissions and scopes #353

Open tnguyen444 opened 10 years ago

tnguyen444 commented 10 years ago

I am trying to restrict certain parts of my api to users that are part of certain groups. I know I would do this with scopes. Would the best way to do this be create different clients for different user groups and set the various scopes in the oauth_clients table? Or is there a way to have more granularity to have scopes per user?

bshaffer commented 10 years ago

There are two quick ways to do this:

  1. When the user is authorizing a client (AKA the Authorization Code grant type), validate the scope requested by the client with the scope available to the user. If the user cannot access the requested scope, return the proper error message or remove the scope from the token.
    /* Authorization Controller */
    // in this case, $user is just a generic object represented in your application for the logged-in user
    if (!$server->getScopeUtil()->checkScope($request->getParameter('scope'), $user->getScope())) {
        die('not enough privileges');
   }
  1. When the resource is requested, use the ScopeUtil class to verify the user after the token itself, i.e.
    /* Resource Controller */
    if ($server->verifyResourceRequest($request)) {
        $token = $server->getToken();
        $user = $server->getStorage('user_credentials')->getUserDetails($token['user_id']);
        if (!$server->getScopeUtil()->checkScope('required_scope_for_this_api', user['scope'])) {
            die('not enough privileges');
        }
    }

So, to answer your question, there is not a lot of support in the library currently for user scopes. There is validation against user scopes when using the UserCredentials grant type, but this will have no effect when using AuthorizationCode.

dashohoxha commented 10 years ago

I'd like to share my experience with Drupal, even if this is not totally relevant.

In Drupal there is the module 'oauth2_server' which handles authentication/authorization, and there is the module 'services' which handles endpoints, resources, etc. Drupal has a granular and strong permission system and the module 'services' takes advantage of that to define which of the users (or groups of users) can access a certain resource. So, the job of 'oauth2_server' is just to authenticate the users (the access_token is used as a means of authentication), and further on the permission system of Drupal is used to decide what the authenticated user can and cannot do.

This way it seems that the scopes are not very useful. Well, at least in Drupal, with the server side workflow, I didn't find them very useful. I even find them confusing and misleading and I don't see how they can be used properly. You can avoid using them at all and everything would be fine. Please correct me if I am missing something.

In general, I think that the way that scopes are used depends on how they are integrated with the user permissions of the system that is implementing the services and the oauth2.

bshaffer commented 10 years ago

You don't always want to grant a client (third party) access to all of what a user can do. Scope is meant to LIMIT the functions the user grants to the client. So, grant the client a subset of the user's permission set. 

The scope / user relation is something we have struggled with in using this library. I know @bojanz has some requests as far as implementation here goes. It's complicated, and typically I've left this to the consuming applications, but it's becoming clear there is more we can do in the library itself. 

Please let me know if you have ideas on how we can do this better.  — Brent Shaffer

On Wed, Mar 19, 2014 at 10:30 AM, Dashamir Hoxha notifications@github.com wrote:

I'd like to share my experience with Drupal, even if this is not totally relevant. In Drupal there is the module 'oauth2_server' which handles authentication/authorization, and there is the module 'services' which handles endpoints, resources, etc. Drupal has a granular and strong permission system and the module 'services' takes advantage of that to define which of the users (or groups of users) can access a certain resource. So, the job of 'oauth2_server' is just to authenticate the users (the access_token is used as a means of authentication), and further on the permission system of Drupal is used to decide what the authenticated user can and cannot do. This way it seems that the scopes are not very useful. Well, at least in Drupal, with the server side workflow, I didn't find them very useful. I even find them confusing and misleading and I don't see how they can be used properly. You can avoid using them at all and everything would be fine. Please correct me if I am missing something.

In general, I think that the way that scopes are used depends on how they are integrated with the user permissions of the system that is implementing the services and the oauth2.

Reply to this email directly or view it on GitHub: https://github.com/bshaffer/oauth2-server-php/issues/353#issuecomment-38073220

dashohoxha commented 10 years ago

Brent, you are right: scope is meant to LIMIT the normal permissions of the user, depending on the client that has the access_token. I missed this point, maybe because all the clients that I intend to use are trusted ones (not third party clients). I have even checked the option "Automatic authorization", which means that the users are not prompted for approving the client, and the authorization is done automatically (immediately after authenticating the user).

Actually it is possible (in the Drupal implementation) to set a list of scopes for each resource, and the authorization will fail if the client does not have all of these scopes (this happens before going to Drupal permission checking). However this is not very useful (in Drupal) because you can select only one oauth2 server per service (and each resource of this service), and all the clients of this oauth2 server have the same scopes (which are defined on the oauth2 server). This implies that either all the clients access all the available resources, or all the clients access none of the available resources. The real decision about using a resource is still left to the permission system of Drupal (which is not proper usage of scopes).

I see two possible solutions to this (which have also been discussed in a previous discussion). Either allow more than one oauth2 server per service and select which server can be used for each resource, or allow different scopes for each client (and then only the clients that have the right scopes will access a certain resource). I think bojanz is in favor of the second solution.

dashohoxha commented 10 years ago

Another solution (Drupal specific) that comes to my mind is this:

Drupal has a function user_access('permission-name') which returns TRUE when the logged-in user has that permission. It can be used to decide what a user can or cannot do. Similar to this, we can have a function oauth2_scope('scope-name') which returns TRUE when the access_token that was used for authentication has that scope. Using this the application can make decisions about what resources can be used and what not.

So, it is not the oauth2_server that enforces the authorization (decides about what should be used and what not), it only provides the information of the available scopes to the other modules (for example the module 'services') and to the application, and let them decide about it.

I think that this solution would be simpler to implement and more flexible, for all the parties that are interacting with each-other (the oauth2-server-php library, the oauth2_server module, and the services module).

I hope that @bojanz is following this discussion and is taking note of these ideas.

tnguyen444 commented 10 years ago

I was going to use scopes to limit resources that are accessible on my server (api). However, the api needs to return roles that a user belongs to so that the client (CMS) can use it's permission system to do this. What's the best way to do this? When we return the token, can we return the roles that are associated to a user or is the outside the proper use of OAuth? Or should I use the getUserDetails method to do this via a separate api call?

dashohoxha commented 10 years ago

I think this should be done with a separate api call (for example /api/me or /api/user_profile or /api/user_details). This is service/resource management and is not responsibility of an oauth2 server.

As far as I can understand, the job of an ouath2 server is to get an access_token from the request and to return information about the user and client (app) that are related to it. Also it should manage the access_token, its life-cycle and everything related to it. It can do other things as well, but in my opinion they are responsibility of other parts of the system and it doesn't have to do them.

On Thu, Mar 20, 2014 at 9:48 PM, tnguyen444 notifications@github.comwrote:

I was going to use scopes to limit resources that are accessible on my server (api). However, the api needs to return roles that a user belongs to so that the client (CMS) can use it's permission system to do this. What's the best way to do this? When we return the token, can we return the roles that are associated to a user or is the outside the proper use of OAuth? Or should I use the getUserDetails method to do this via a separate api call?

— Reply to this email directly or view it on GitHubhttps://github.com/bshaffer/oauth2-server-php/issues/353#issuecomment-38219162 .

tnguyen444 commented 10 years ago

Going back to using scopes to limit what can be accessed from my api, is there any way to set this for a token in the oauth_access_tokens.scope column? I want to, whenever a user is authenticated via the password grant_type, and a token is create, to retrieve their roles in the database, and set their scope based on this, in this column. Is this possible and is then even what this column is intended to be used for or is using the getUserDetails method the best way?

Footnote: As, I examine the code, it seems as though the column is to be designated for the token request's scope, not the user scope. Am I right?

bshaffer commented 10 years ago

@tnguyen444 You are correct, that column is for token scope... but this can be equal to or less than the user's scope, and that is no problem.

In fact, if your OAuth2\Storage\UserCredentialsInterface returns an array with scope set for the call to getUserDetails, this scope will be used in the access_token automatically (see the UserCredentials Grant Type)

hkdobrev commented 10 years ago

@bshaffer

there is not a lot of support in the library currently for user scopes

it's becoming clear there is more we can do in the library itself

Is there anything new on this topic?


I am using the UserCredentials GrantType and I am going to use what @tnguyen444 and @bshaffer have last suggested for now.

I think some basic implementation should be included at least in the demo app. And if it comes out be good and useful it could be moved to the library in some form.