User management with SCIM

User management with SCIM

operate

User management with SCIM

SCIM logo

Introduction

In this tutorials, we'll setup the user management module in Curity, and with that done, proceed to create some users using the SCIM interface. We will also look at some common operations like listing users, updating an individual user and using filters to narrow down our search results to exactly what we want.

SCIM

SCIM (System for Cross-domain Identity Management) is a standardized way of representing resources, with users being the main focus. Pretty much any resource could be represented by the SCIM format and protocol though.

The SCIM standard has seen a couple of revisions since its first release, and the current version is SCIM 2, which is what we'll use in this tutorials while referring to it simply as SCIM.

As SCIM is built on top of HTTP and RESTful principles, some familiarity with these technologies will greatly ease the learning process. Luckily, if you're reading this, you've probably used a REST API or two. Great!

Configuring Curity

This guide uses the provided example_config.xml file as a base configuration. This ensures that a data source is setup, as well as an authentication profile and an OAuth profile. These components are needed for us to build the user management profile on top of, but if you have got all those setup by your own configuration that should work perfectly fine as well. So what do we need to add for user management? Let's have a look:

  • A user management endpoint (we'll use /user-management in this example)
  • A reference to the data source where we will store our users, aptly named user-account-data-source
  • A reference to a credential manager (if we want to be able to set and update passwords for our users)
  • Optionally, an authorization manager for deciding what client gets to do what at the configured user-management endpoint (reading, writing, and so on). While you'll want this for production, let's leave it out for now so we can focus on some actual user management!
  • Last but not least, we need a configured client to use for sending authenticated queries to our user management endpoint. Configuration for several clients is included in example_config.xml. See the tutorial on client credentials for how to obtain an access token and use that for all the example requests in this guide.

Granted these requirements we could either use the CLI, the admin UI or XML configuration to set this up. Here we've used the latter, and an example of a minimal XML configuration is seen below:

<config xmlns="http://tail-f.com/ns/config/1.0">
    <environments xmlns="https://curity.se/ns/conf/base">
        <environment>
            <name>demo</name>
            <services>
                <service>
                    <id>admin</id>
                    <endpoints>user-management</endpoints>
                </service>
            </services>
        </environment>
    </environments>
    <profiles xmlns="https://curity.se/ns/conf/base">
        <profile>
            <id>user-management</id>
            <type xmlns:um="https://curity.se/ns/conf/profile/user-management">um:user-management-service</type>
            <settings>
                <user-management-service xmlns="https://curity.se/ns/conf/profile/user-management">
                    <api-authentication>
                        <oauth-service>oauth</oauth-service>
                    </api-authentication>
                    <user-account-data-source>DefaultDataSource</user-account-data-source>
                    <credential-management>
                        <credential-manager>DefaultCredentialManager</credential-manager>
                    </credential-management>
                </user-management-service>
            </settings>
            <endpoints>
                <endpoint>
                    <id>user-management</id>
                    <uri>/user-management</uri>
                    <endpoint-kind>um-api</endpoint-kind>
                </endpoint>
            </endpoints>
        </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.xml), and use the idsh tool to merge it with your existing configuration:

user@localhost:~/identity-server/idsvr$ ./bin/idsh

user connected from 127.0.0.1 using console on localhost
user@localhost> configure
Entering configuration mode private
[ok][2017-12-14 10:46:08]

[edit]
user@localhost% load merge somewhere/user-management.xml
[ok][2017-12-14 10:46:29]

[edit]
user@localhost% commit

We should now have Curity setup with the user management endpoint found at /user-management. This is the "base path" for all user management operations and won't (currently) respond with anything unless it's followed by the resource type we are interested in. As this tutorials promised user management, we'll naturally be most concerned with users, which conveniently enough can be found at the /Users endpoint (under the base path, so /user-management/Users). To verify that it's working, send a GET request to your new endpoint and you should get a pretty sparse response:

{
    "totalResults": 0,
    "startIndex": 1,
    "itemsPerPage": 50,
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:ListResponse"
    ],
    "Resources": []
}

Voila! We are up and running. Now, let's add some users.

Creating new users using SCIM

Create user request

Just like we used GET to fetch our (still non-existing) users, creating a user is as simple as following the same REST conventions and sending a POST request to the /user-management/Users endpoint with the request payload being the data on the user to be created. Let's create a user, using only the bare minimum attributes:

{
    "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
    ],
    "userName": "teddie-the-robot-vacuum-cleaner",
    "emails": [
        {
            "value": "teddie@example.org",
            "primary": true
        }
    ]
}

Looking at the above (quite minimal) request, we see some core components in action. Every request (and response for that matter) is defined by a schema. For a simple request like this to the /Users endpoint this could seem a little superfluous, but for more advanced scenarios involving several resource types and schemas, this becomes an absolute necessity.

Create user response

If everything worked as it should, the server will respond with HTTP status 201 Created, and a response body showing the just created user:

{
    "emails": [
        {
            "value": "teddie@example.org",
            "primary": true
        }
    ],
    "meta": {
        "resourceType": "User",
        "created": "2017-12-13T14:39:33Z",
        "lastModified": "2017-12-13T14:39:33Z",
        "location": "https://localhost:8443/user-management/Users/VVNFUjplNzA0MmQ0ZDYxNDM0ZGQ3YjdlZTg2MmZlNzJlMzhhMA"
    },
    "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
    ],
    "active": false,
    "id": "VVNFUjplNzA0MmQ0ZDYxNDM0ZGQ3YjdlZTg2MmZlNzJlMzhhMA",
    "userName": "teddie-the-robot-vacuum-cleaner"
}

Let's have a look at what we've got here. First of all, it's obvious that the response contained a lot more information than we sent in the POST request. This is because some values (like "created" only is known to the server actually creating the user). Other values like "active" are just determined by the server to have a default, like "false" in this case.

The "id" attribute exists for any SCIM resource, and is - just like some of the other attributes - created by the server along with the resource. According to the SCIM specification, any ID must be globally unique, regardless of resource type. This is possible to enforce largely just because the client has no say in what ID the server should assign to the client.

Go ahead, change some of the details in the request and create a couple of users. It will make searching for them in the next section feel a little more meaningful! We'll add at least one more user whose active status will be set to true.

When using SCIM for authentication, only users that have an active attribute set to true are normally considered

Listing existing users

Remember how we previously sent a GET request to the /Users endpoint only to have it come back with an almost empty response. Not any more! Repeat the request now and you should see a listing of all the users you created above. At minimum you should be able to see both Teddie and Eddie in the response:

{
    "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": false,
            "meta": {
                "resourceType": "User",
                "created": "2017-12-14T13:35:13Z",
                "lastModified": "2017-12-14T13:35:13Z",
                "location": "https://localhost:8443/user-management/Users/VVNFUjo1NmEzYWUwMWY5Zjk0MmJmYjczNmVjYzFkZGM4N2ZjYw"
            }
        }
    ]
}

Searching users with a provided filter

Most of the times we don't want to simply list all users. In a production system there are likely several thousands, so we're likely to want to list only those according to a given search criteria. In the SCIM protocol, this is called a filter. If you are familiar with SQL databases you'll rightly recognize the similarities to the syntax of the query language used for filtering in SCIM. See the specification on filter for a complete document on the possible options. Note however that not all options for filtering may be supported.

For the most common use cases, adding a filter is a simple as adding the filter parameter to the GET query:

https://localhost:8443/user-management/Users?filter=active eq true

In the example above, we've narrowed the search to only include users whose active attribute is set to true. Now only Eddie shows up in the response:

 {
     "totalResults": 1,
     "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"
             }
         }
     ]
 }

Use and, or and not and other operators to build more complex queries.

With filtering out of the way, let's see how we can make Teddie active as well.

Updating a user

When it comes to updating a user SCIM provides us with two options. One is to use the HTTP PUT method to replace all values on an existing resources, and the other way is to use the HTTP PATCH method to update only what is needed. Since all we want is to make Teddie active, the PATCH method seems like the most appropriate. Both methods are used directly on the URL of the actual user, which you might remember being found in the meta.location attribute in the response we saw previously.

Sending a PATCH request

An example PATCH request to update user Teddie, found at

https://localhost:8443/user-management/Users/VVNFUjo1NmEzYWUwMWY5Zjk0MmJmYjczNmVjYzFkZGM4N2ZjYw

could look something like this:

{
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ],
    "Operations": [
        {
            "op": "replace",
            "path": "active",
            "value": true
        }
    ]
}

Looking at the attributes sent in the query, we can see that we first specify the type of operation we want to perform. In this case we want to update an existing value, so the replace operation is what we want (add and remove is also available). Next we see the path to the attribute we want to operate on. In this case the active attribute is on the top level of the resource, but specifying a nested path is also possible using dots in the attribute path (like name.lastName). Last we provide the new value that we want set, in our case true.

Again we get the user back in the response. This time we can see that the value of active now is set to true!

{
    "emails": [
        {
            "value": "teddie@example.org",
            "primary": true
        }
    ],
    "meta": {
        "resourceType": "User",
        "created": "2017-12-14T13:35:13Z",
        "lastModified": "2017-12-14T14:46:55Z",
        "location": "https://localhost:8443/user-management/Users/VVNFUjo1NmEzYWUwMWY5Zjk0MmJmYjczNmVjYzFkZGM4N2ZjYw"
    },
    "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
    ],
    "active": true,
    "id": "VVNFUjo1NmEzYWUwMWY5Zjk0MmJmYjczNmVjYzFkZGM4N2ZjYw",
    "userName": "teddie-the-robot-vacuum-cleaner"
}

Repeating our GET search query with the filter should now return both users.

Conclusion

By now we've seen how to setup user management in Curity and with that up and running how to create, search and modify users. While this covers the most common needs in most user management systems, more advanced features are certainly available. Have a look at the official specifications for both the SCIM schema as well as the protocol for further reading.

Was this page helpful?