linksmart / thing-directory

Directory of Web of Things (WoT) Thing Descriptions. Maintained fork at https://github.com/TinyIoT/thing-directory
Apache License 2.0
21 stars 4 forks source link

Use oauth2 scopes for access control #28

Open farshidtz opened 4 years ago

farshidtz commented 4 years ago

Currently, the Keycloak plugin which uses the OpenID Connect protocol performs access control on endpoint/methods using user claims (username, group) or the clientID.

We may be able to use OAuth2 scopes for the same level of access control.

OAuth2 security scheme: https://www.w3.org/TR/wot-thing-description/#oauth2securityscheme

Set of authorization scope identifiers provided as an array. These are provided in tokens returned by an authorization server and associated with forms in order to identify what resources a client may access and how. The values associated with a form should be chosen from those defined in an OAuth2SecurityScheme active on that form.

Example: https://www.w3.org/TR/wot-thing-description/#example-15

... OAuth2 makes use of scopes. These are identifiers that may appear in tokens and must match with corresponding identifiers in a resource to allow access to that resource (or Interaction Affordance in the case of W3C WoT). For example, in the following, the status Property can be read by Consumers using bearer tokens containing the scope limited, but the configure Action can only be invoked with a token containing the special scope. Scopes are not identical to roles, but are often associated with them; for example, perhaps only those in an administrative role are authorized to perform "special" interactions. Tokens can have more than one scope. In this example, an administrator would probably be issued tokens with both the limited and special scopes, while ordinary users would only be issued tokens with the limited scope.

{
    ...
    "securityDefinitions": {
        "oauth2_sc": {
            "scheme": "oauth2",
            ...
            "flow": "code",
            "authorization": "https://example.com/authorization",
            "token": "https://example.com/token",
            "scopes": ["limited", "special"]
        }
    },
    "security": ["oauth2_sc"],
    ...
    "properties": {
        "status": {
            ...
            "forms": [{
                "href": "https://scopes.example.com/status",
                "scopes": ["limited"]
            }]
        }
    },
    "actions": {
        "configure": {
            ...
            "forms": [{
                "href": "https://scopes.example.com/configure",
                "scopes": ["special"]
            }]
        }
    },
    ...
}

From OAuth2 specs:

The authorization and token endpoints allow the client to specify the scope of the access request using the "scope" request parameter. In turn, the authorization server uses the "scope" response parameter to inform the client of the scope of the access token issued.

Or a more understandable version:

Scope is a mechanism in OAuth 2.0 to limit an application's access to a user's account. An application can request one or more scopes, this information is then presented to the user in the consent screen, and the access token issued to the application will be limited to the scopes granted.

There are also a few examples here.

farshidtz commented 4 years ago

In Keycloak, scopes can be associated with roles and users in the following way:

  1. Roles -> Create a role
  2. Client Scopes i. Create a scope ii. Edit the scope -> Scope -> Assign the role
  3. Clients -> Edit client -> Client Scopes -> Assign the role to Assigned Optional Client Scopes
  4. Users -> Role Mappings -> Assign the role

In the OAuth2 token request, when asking for the specific scope, the server will provide it in response only if the user has an associated role.

E.g. password grant:

curl --location --request POST '<token-endpoint>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=test-client' \
--data-urlencode 'username=tester' \
--data-urlencode 'password=******' \
--data-urlencode 'scope=limited'
{
    "access_token": "<access-token>",
    "expires_in": 300,
   ...
    "scope": "email limited profile"
}

Access Token Payload:

{
  "exp": 1592929675,
  "iat": 1592929375,
  "typ": "Bearer",
  ...
  "scope": "email limited profile",
}