
Scope Authorization Manager
On this page
Protecting User Management Using the Scope Authorization Manager
Introduction
In the tutorial on user management and SCIM we learned how to setup the user management profile and how to actually manage our accounts through basic CRUD (create, read, update, delete) operations using SCIM. In this tutorials we'll build on the knowledge (and even more - the configuration) acquired there, so it is highly recommended to start with that, but if you already have the user management module up and running and you are familiar with SCIM already, feel free to skip that and only apply the example configuration.
Authorization
With the prerequisites out of the way, there is a glaring (albeit conscious) omission in our current setup. By default the user management module is configured without an authorization manager, allowing any client with a valid access token to read, even create or update users! While this is fine for development, or even production setups where the user management concepts are protected by other means, in most scenarios we'll definitely want to control who gets to do what. In other words, we want to configure authorization.
The Scope Authorization Manager
Authorization in the user management module is done through configured authorization managers. Currently there are two types of authorization managers: a Scope Authorization Manager and the Axiomatics Authorization Manager. This tutorials will cover the Scope Authorization Manager.
The Scope Authorization Manager uses OAuth scopes as a base "unit" for making access decisions. The base concept is
rather simple and is probably familiar to most who have ever worked with OAuth scopes before. An OAuth client accesses
protected resources by providing an access token, and with this access token often one or more granted scopes. The names
of the scopes are chosen arbitrarily, but a good practice is to name them according to their intended use. For
authorization in the user management module this could be names like admin_read
or admin_write
. These names clearly
convey the purpose of the scopes. Let's create these scopes and assign them to the client we configured in the
tutorial on client credentials:
<config xmlns="http://tail-f.com/ns/config/1.0"><profiles xmlns="https://curity.se/ns/conf/base"><profile><id>oauth</id><type xmlns:as="https://curity.se/ns/conf/profile/oauth">as:oauth-service</type><settings><authorization-server xmlns="https://curity.se/ns/conf/profile/oauth"><scopes><scope><id>admin_read</id><description>Admin read scope</description></scope><scope><id>admin_write</id><description>Admin write scope</description></scope></scopes><client-store><config-backed><client><id>server-client</id><scope>admin_read</scope><scope>admin_write</scope></client></config-backed></client-store></authorization-server></settings></profile></profiles></config>
To merge it with your existing configuration, save it as an XML file (for the purpose of this example, we'll call it
user-management-scopes.xml
), and use the idsh
tool to merge it with your existing configuration:
user@localhost:~/identity-server/idsvr$ ./bin/idshuser connected from 127.0.0.1 using console on localhostuser@localhost> configureEntering configuration mode private[ok][2018-01-11 10:46:08][edit]user@localhost% load merge somewhere/user-management-scopes.xml[ok][2018-01-11 10:46:29][edit]user@localhost% commit
So far we've added the necessary scopes to the OAuth profile and to the client we'll use for user management. These scopes will be checked by the authorization manager when certain actions are triggered. Actions are events like "client with scope X is trying to read all users" or "client with scope Y is trying to update data on its authenticated subject (user)". When an action is triggered, the decision on whether to allow the action is decided by an authorization policy.
Authorization Policies
How does the Scope Authorization Manager use these scopes to decide permissions for our configured clients? The answer to that is through policies. A policy consists of a "policy action", which is similar to the action described as an event above. However, while user actions like "client presenting a token with scope X is trying to read all users" is always concrete, an action specified on a policy is allowed to be more broad, like "a client is trying to do a read operation on a user management endpoint". While this might seem a little confusing at first, it allows for customizing very powerful authorization rules with very little configuration.
Configuring a Policy
Any client with scope admin_read
should be allowed to read any data on the /Users
endpoint.
This would translate to this concrete action, which is triggered each time a client tries to read the /Users
endpoint
by sending a GET
request:
um:authorization-actions.user-management.users.admin.read
We could now setup a policy with an action matching the exact path, or we could define a broader policy like "any read operation on the user management profile, regardless of endpoint". This policy action would look like this:
um:authorization-actions.user-management.admin.read
Notice how we removed "users" from the action in the policy? Any requirements (such as scopes) will now be in effect for
all read operations, not just on /Users
. For the purpose of this example however, let's match on the exact action.
Setting up the Scope Authorization Manager and policies is probably most easy to do through the Admin UI, so let's use
that. After having logged in, click Authorization -> New Authorization
. Name it whatever you feel like. In this
example we'll name it test-authorization
. In the next step we'll set its type to scopes
and then we'll press the
Add Policy
button. Immediately we'll be asked for the name of the policy. This name is the "policy action" we talked
about before, and as we are going to setup this policy to match an exact action, well chose the name
um:authorization-actions.user-management.users.admin.read
from the drop down menu.

Configuring a Rule
We now have a policy which will be considered each time a client tries to make a read operation at the user management's
/Users
endpoint. Without proper rules though, our authorization manager won't know what to do, and an authorization
manager that doesn't know what to do will by default deny authorization. For our policy to be effective we thus need to
provide our policy with a rule or two. Let's add one! We do this by pressing Add Rule
, and before we proceed we give
it a name. Since we want to require the admin_read
scope for read operations on our policy action, I'll name my rule
require-scope-admin_read
. In the next step, we'll setup a very simple rule. Our rule will mandate that any client with
the admin_read
scope is allowed access to the action our policy has defined. This would look something like this:

See how simple that was? First we added our admin_read
scope to the rule (Scope
), then we decided what to do with
it. In this case we said that the presence of that scope should allow the client access (Authorization Decision
),
and that the scope could be present on its own or together with any other scope (Applicability
). We could have added
more scopes and set applicability to all-of
too, for instance if we had wanted admin
and read
to be separate
scopes.
Configuring the User Management Profile to Use Our Authorization Manager
Last but not least, we'll need to actually use our now configured Authorization Manager. As we want to try it on the
user management module, we'll simply click User Management
and under the Authorization
section select our configured
scope authorization manager:

Testing Scope Authorization
With our Scope Authorization Manager configured, let's make sure that it actually works. What we've said is that any
client presenting a token with the admin_read
scope should be able to read (i.e. send a GET
request to) the /Users
endpoint. This would of course imply that any client presenting a token without the admin_read
scope isn't allowed.
Luckily, that implied rule is very easy to confirm. Just request an access token without asking for the admin_read
scope and use it for trying to read /Users
! The response you should see if everything is setup correctly is this:
{"detail": "Not authorized","status": "403","schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"]}
Let's now ask for a new access token, and this time we'll ask for the admin_read
scope while doing so (again, see the tutorial on client credentials for details on how to do this).
Providing our new new access token (with the admin_read
scope) in the GET
request to /Users
and voila, we are in!
{"totalResults": 2,"startIndex": 1,"itemsPerPage": 50,"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],"Resources": [{"id": "VVNFUjozNzYyYzYwZTJiMjE0ZGY2YTlkNDA3ZTdlMTE5M2I3Zg","userName": "eddie-the-tea-cooker","schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],"emails": [{"value": "eddie@example.org","primary": true}],"active": true,"meta": {"resourceType": "User","created": "2017-12-14T13:25:51Z","lastModified": "2017-12-14T13:25:51Z","location": "https://localhost:8443/user-management/Users/VVNFUjozNzYyYzYwZTJiMjE0ZGY2YTlkNDA3ZTdlMTE5M2I3Zg"}},{"id": "VVNFUjo1NmEzYWUwMWY5Zjk0MmJmYjczNmVjYzFkZGM4N2ZjYw","userName": "teddie-the-robot-vacuum-cleaner","schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],"emails": [{"value": "teddie@example.org","primary": true}],"active": true,"meta": {"resourceType": "User","created": "2017-12-14T13:35:13Z","lastModified": "2017-12-14T13:35:13Z","location": "https://localhost:8443/user-management/Users/VVNFUjo1NmEzYWUwMWY5Zjk0MmJmYjczNmVjYzFkZGM4N2ZjYw"}}]}