/images/resources/tutorials/advanced/oauth-entitlements-introspection.png

Entitlements in Introspection

On this page

It is very common to need some form of permissions or entitlements when determining if a user should be able to call an API. This can be done using OAuth scopes, but often additional rights need to be verified. These permissions are often needed in the API gateway, so that it can check the user's right before forwarding the call to the back-end. This is also a convenient location when the API gateway is introspecting the token to convert it from a phantom token to a JWT (i.e., from an opaque GUID used on the Internet to a corresponding JSON Web Token used internally).

To save the rights of a user, the SCIM 2 user account model can be used. This schema defines a standard way of representing these user-specific entitlements. In that specification, it stipulates that a user may have a (possibly empty) set of rights that look like this (when encoded as JSON):

json
12345678
{
"entitlements": [
{"value": "a"},
{"value": "b"},
{"value": "c"}
],
// ...
}

The Curity Identity Server supports the SCIM 2 standard and makes the user data available when the token is introspected. As of version 7.0 of the Curity Identity Server, a GraphQL API is also available for User Management. The following walk-through will explain how these features can be combined to solve this need. To follow along, make sure that the following is already setup:

  • A Token Service with a client that can get an access token (e.g., via the code flow or assisted token flow) and another that can introspect it. The first will be the one used by a Web or mobile app and the second will be the one used by the API gateway.
  • An authentication profile that allows the user to login via an HTML Form Authenticator
  • Other basic facilities like a Data Source (DS) and cryptographic keys

Setting Entitlements for a User

The SCIM 2 API can be used to set entitlements of users through a standard mechanism. This API supports GET, POST, PUT, and PATCH on users and groups of users. This provides a known protocol that a customer support portal, for instance, can leverage to update all user attributes, not just entitlements.

In addition to the SCIM 2 API, the Curity Identity Server can be configured to expose a GraphQL API for User Management (version 7.0 and later). This could be used as an alternative mechanism to manage users and with that set entitlements.

Configuring a New User Management Profile

If a user management profile has been created, then this API is available. If not, however, it can be setup by performing the following steps in the Admin UI:

  1. From the Profiles menu, click Add Profile to add a User Management profile.

  2. Give it some name, e.g., user-management, and a URL Prefix, e.g., /um. Then click Next.

  3. Select the Token Service that will be used. This defines the Token Service that a client, like the customer support portal, must obtain a token from in order to call the SCIM or GraphQL API.

    a) Optionally select an existing Authorization Manager or create a new one.

  4. On the Data Sources page, first choose the type of User Data Store, data-sources or an account-manager. If the GraphQL API is to be used and linked accounts are used, choose the account-manager option here. Otherwise choose data-sources.

    a) If data-sources is chosen, select the Token Data Source where tokens are stored. Also select the User Account Data Source. In a simple deployment, these two data sources will be the same. Optionally select a Devices Data Source and a Credential Manager.

    b) If account-manager is chosen, select the Token Data Source where tokens are stored. Also select the Account Manager. Optionally select a Credential Manager.

  5. Next select what nodes that the User Management profile should be deployed to. In a simple deployment this is probably the default node. Then click next and Commit the changes.

Optionally add GraphQL Endpoint
  1. Navigate to Profiles and click the User Management Profile
  2. In the left side menu, choose Endpoints
  3. Click New Endpoint
  4. Give it a name, e.g., um-graphql and set a path, e.g., /um/graphql
  5. Click Create
  6. In the column Running On, click Not deployed and choose the node(s) to deploy the endpoint to. In a simple deployment this is probably the default node. Click Submit and then Done.
  7. Commit the changes.

The GraphQL API requires an Authorization Manager. If one is already configured, assign it to the User Management Profile.

  1. Profiles → um → General
  2. From the Authorization Manager drop-down menu, select the Authorization Manager
  3. Commit the changes.

If an Authorization Manager needs to be created, the User Management with GraphQL article describes the process to create a Groups Authorization Manager. There are also custom Authorization Manager examples available in the Code Examples section.

Testing the New API

If the configuration was accepted and committed, it should be ready to use. The endpoint(s) will be available on the node(s) that the configuration was deployed to. Each of those have a protocol (e.g., https), a listening address or interface (e.g., 0.0.0.0 for all interfaces on that server host), and a port (e.g., 8443). These together forms the Base URL:

url
1
https://localhost:8443

To call either the SCIM or GraphQL endpoints, a token is needed that authorizes the caller. This can be obtained in various ways. When the caller is a customer support portal, for instance, this token will usually be obtained using the client credentials flow. The client ID and secret that the portal uses will be some configured values that it keeps safe. With this, it can make a request such as this to obtain an access token:

http
1234567
POST /oauth/v2/oauth-token HTTP/1.1
Host: localhost:8443
Authorization: Basic Y2xpZW50LW9uZTowbmUhU2VjcmV0
Content-Type: application/x-www-form-urlencoded
 
grant_type=client_credentials&
response_type=token

In this request, the client ID and secret may also be provided in body using the form elements client_id and client_secret. The result will be a JSON document that includes an access token:

json
123456
{
"access_token": "_0XBPWQQ_c25227b2-5e00-4324-934d-ba5ddccd54aa",
"scope": "",
"token_type": "bearer",
"expires_in": 300
}

With this access token in hand, the client can now call the SCIM API to get a list of users. The SCIM endpoint is comprised of the base URL, user management path with a SCIM standard defined part added at the end. For a User Management profile with the configured path /um, the complete SCIM endpoint would be something like:

url
1
https://localhost:8443/um/Users

To call the SCIM API, a request similar to this would be used:

http
1234
GET /um/Users HTTP/1.1
Host: localhost:8443
Authorization: Bearer _0XBPWQQ_c25227b2-5e00-4324-934d-ba5ddccd54aa
Accept: application/scim+json

The result could be a number of users with various attributes as shown in the following elided listing:

json
123456789101112131415
{
"totalResults": 1,
"startIndex": 1,
"itemsPerPage": 50,
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
],
"Resources": [
{
"id": "VVNFUjoyOWZmODMzYWQ2NGM0MzY1YmY3Y2QzYjQ1YjM1NWI0Mw",
"userName": "teddie",
"active": true,
// ...
}]
}

To restrict this to a particular one, a query string can be added to filter the results by email addresses. To do this, append a query string such as filter=emails eq "teddie@example.com". The quotes in the filter are required by the SCIM specification. The result should be a single user if the filter is precise enough. Another noteworthy part of the request is the Accept header; this should always be application/scim+json or */*.

In any event, the API should be clearly working by performing such requests. If it is not, check that the endpoint was assigned to a server node.

Adding Entitlements to an Existing User

To add entitlements to an existing user, the PATCH method can be used. This will update certain attributes of the user without affecting others. Using the ID of the user, a PATCH request can be made to the SCIM users endpoint like this:

http
123456789101112131415161718
PATCH /um/Users/VVNFUjoyOWZmODMzYWQ2NGM0MzY1YmY3Y2QzYjQ1YjM1NWI0Mw HTTP/1.1
Host: localhost:8443
Authorization: Bearer 75398416-3958-4828-a738-6c27465204eb
Accept: */*
Content-Type: application/scim+json
 
{
"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations":[{
"op":"add",
"path":"entitlements",
"value":[
{"value": "a"},
{"value": "b"},
{"value": "c"}
]
}]
}

In this request, the user with an ID of VVNFUjoyOWZmODMzYWQ2NGM0MzY1YmY3Y2QzYjQ1YjM1NWI0Mw is being patched to have three new entitlements added: a, b, and c. The string values can be anything that makes sense for the application in question. SCIM also allows certain metadata to be added to the entitlement attribute as well.

With these new rights in place, it is now possible to include them in the introspection results, so that the API or its gateway can make an informed access control decision.

Including Entitlements in Introspection Results

To include these new entitlements in the introspection results, we have to configure the Curity Identity Server to retrieve the attributes. This can be achieved in several different ways and will be done as an Authentication Action configured on the HTML Form Authenticator. The Attribute Query used by the Data Source will be modified, and a token issuance procedure configured that returns the user's rights when the token is introspected.

Custom Attribute Query

To include the entitlements attributes in the ones that are available for a user when that user's token is introspected, a custom attribute query is configured. This should be done on the Data Source that will be used by the Authentication Action assigned to the authenticator used. A custom credential query could be used instead; however, this will not be described in this walk-through. To configure the custom Attribute Query on the Data Source, do the following:

  1. In the FacilitiesData Sources menu, find the Data Source that is used (should be the same as used by the User Management Service) and click Edit.
  2. In the Attribute Query field, enter the following query:
sql
1
SELECT * FROM accounts WHERE accounts.username = :subject

Here, :subject will be replaced with the username of the user that logged in (e.g., teddie).

Data Source Authentication Action

With the Attribute Query modified, a Data Source action can be added to the HTML Form Authenticator.

  1. Go to ProfilesAuthentication ServiceAuthenticators and click the HTML Form Authenticator to use.
  2. In the Actions column and the Login lane, click Add and then click New Action.
  3. Enter a name, e.g., getAttributes. Choose the Data Source type and then click Next.
  4. Select the Data Source that was previously modified from the Attribute Data Source drop-down and click close.
  5. From the Changes menu, click Commit.

The Authenticator configuration should now look like this:

Authenticator with Action

Claims Value Providers

It is also possible to leverage Claims Value Providers to map entitlements in a token issued for a user. This is not covered in this guide but is covered in the Implementing Custom Claims tutorials.

Introspection Procedure

A custom introspection procedure is needed that can add the entitlements to the result. To do this in the UI, perform the following:

  1. Open the Token Service profile that is being used.
  2. Click Endpoints on the left-hand side.
  3. Search for the introspection endpoint that is being used.
  4. In the Flows dropdown associated with that endpoint, click the introspect procedure drop-down. Choose New Procedure and enter a name, e.g., entitlements-in-introspection. Click the Save button.
  5. An editor will open. In this, replace the existing script with the following:
javascript
1234567891011121314151617181920212223242526272829303132
function result(context) {
var responseData = {
active: context.presentedToken.active
};
if (context.presentedToken.active) {
appendObjectTo(context.presentedToken.data, responseData);
responseData.entitlements = getEntitlements(context);
responseData.token_type = context.presentedToken.type;
responseData.client_id = context.presentedToken.delegation.clientId;
responseData.expired_scope = context.presentedToken.expiredScopes;
}
return responseData;
}
function getEntitlements(context) {
var subjectAttributes = context.subjectAttributes();
var jsonAttributes = subjectAttributes["attributes"];
if (jsonAttributes) {
var parsedJsonAttributes = JSON.parse(jsonAttributes);
return parsedJsonAttributes["entitlements"].map(function (x) {
return x.value;
});
}
else {
return null;
}
}
  1. Click Update, and then close the editor
  2. From the Changes menu, click Commit.

Checking the Introspection Results

To test the procedure, a token that represents the user is required. Tokens that represent a client only, like those issued when the client credential flow is used, will not work. A user-specific access token that runs the custom credential query and gets the entitlements will be issued after the user logs in with an HTML Form Authenticator that uses that Data Source. (The Data Source will not be used directly but through the Credential Manager that the HTML Form Authenticator uses.) With this token in hand, the API or the API gateway can introspect it. A sample request is shown in the following listing:

http
123456
POST /oauth/v2/oauth-introspect HTTP/1.1
Host: localhost:8443
Authorization: Basic Y2xpZW50LW9uZTowbmUhU2VjcmV0
Content-Type: application/x-www-form-urlencoded
 
token=_0XBPWQQ_a7409965-43fb-4fef-9620-0ff9ae4fdc53

Here, the client ID and secret are supplied in the HTTP Authorization header using the basic authentication scheme. This credential is that of the API gateway (or if no such gateway is used, then the API itself). This client must have the introspection capability, and if it is only an API gateway, it should not have any other capabilities. The result will be a JSON document such as the following where the entitlements are returned as an array of values:

javascript
12345678910
{
"sub": "teddie",
// ...
"scope": "read openid write",
"entitlements": [
"a",
"b",
"c"
]
}

These will be the same ones set by the SCIM client at the beginning of the walk-through. With this information in hand, the gateway has not only the scopes but other permissions necessary to make a course-grained access control decision.

Conclusion

This writeup shows how to setup the Curity Identity Server as a SCIM 2 server and optionally enable the User Management GraphQL API by defining a user management profile. With that it's possible to add information to a user, such as entitlements. How that information can be used to enrich introspection results of tokens that are issued through the Curity Identity Server was also demonstrated. With this, an API gateway can enforce access control. In all of this, the APIs exposed through the Curity Identity Server are standards-based, making it fast and easy to integrate into existing customer portals, mobile apps, and web sites. This entire use case was implemented as configuration and required no custom coding and can be setup in just a few minutes.

Join our Newsletter

Get the latest on identity management, API Security and authentication straight to your inbox.

Start Free Trial

Try the Curity Identity Server for Free. Get up and running in 10 minutes.

Start Free Trial