Implementing Claims Best Practices

Implementing Claims Best Practices

architect

Intro

In this tutorial and video we will describe how to issue custom claims from the Claims Best Practices article, using the Curity Identity Server. A toolbox is provided, consisting of a number of separate behaviors that can be composed together to construct your solution.

Custom Claims Pattern

Provide Custom Claims API Endpoints

To provide custom claims via APIs you would implement one or more REST endpoints, which the Authorization Server will call during token issuing. For our scenario we will provide a single endpoint, which receives the subject claim in a GET request:

  • https://localhost:3000/api/users/556c8ae33f8c0ffdd94a/id

The endpoint would then look up the user details and return a payload containing custom claims. These claims will then be issued, then included later in access tokens sent to APIs, where they will become part of the Claims Principal object.

{
  "user_id": 123,
  "user_role": "customer",
  "user_subscription_level": "gold",
  "user_company_id": 0
}

The above claims are simple strings and integers, but the claim returned can also be a JSON array or object. The Curity Identity Server will then write the same claims format to the tokens it issues.

Handle Claims Requests for New Users

If a subject claim is received that is not recognized in the Users API, you can implement any logic you want, though the following two options are perhaps the most common:

  • Assume this is an online customer who has just signed up, create a user record with default permissions, and return its details
  • Return a default payload with empty / null claims, so that the token issued can be rejected when sent to APIs later

Design Scopes Composed of Claims

The Curity Identity Server allows us to compose claims into scopes, which provides a way to manage authorization data by area. The below order scope contains claims that allow us to restrict customers to their own orders, while granting administrators access to all orders:

Custom Scope

Similarly the inventory scope has claims we can use to grant access to different items by business partner, and to allow customers with higher subscription levels to purchase additional items:

Custom Scope

It is common to include certain claims, such as user_role above, in multiple scopes, in a similar way to how the OpenID Connect built in email claim may be included in both email and profile scopes.

Configure Claims

For each claim, a Claims Mapper is used to decide where to issue the claim. For our scenario we only want the claims to be available to APIs, so we have selected the access token option:

Claims Mapper

Visualize Claims

To view claims being issued to clients, it can be useful to temporarily activate the consent screen for the client. When a scope is composed of claims, the Curity Identity Server does not show the scope itself in the consent screen, and instead only shows the individual claims.

Consent to Claims

Create a Claims Value Provider

At runtime, claims need to be assigned values based on which user is signing in, and this is done by a claims value provider. There are a number of options, including a scriptable version where you can implement arbitrary logic, but the simplest fit for our scenario is a JSON Data Source.

Claims Provider

Create a Claims Data Source

A JSON data source is configured to point to REST URL within the API that provides custom claims. In the below example we are configuring the Curity Identity Server to include the OAuth subject claim in a URL path segment.

Claims Provider

Create an HTTP Client

The JSON data source uses an HTTP Client to deal with the connectivity and security details of the outgoing connection. The below example uses Basic Authentication within the back end environment, though more advanced options such as Mutual TLS are also supported:

Claims Provider

Use Custom Claims in APIs

The end result is usually that an opaque reference token is issued to the client. Later the token will be introspected by the API Gateway, which will forward full claim details to the API, as in the last 4 fields below:

{
  "sub": "556c8ae33f8c0ffdd94a57b7eab37e9833445143cf6847357c65fcb8570413c4",
  "purpose": "access_token",
  "iss": "https://localhost:8443/oauth/v2/oauth-anonymous",
  "active": true,
  "token_type": "bearer",
  "client_id": "web-client",
  "aud": "api.yourcompany.com",
  "nbf": 1611673855,
  "scope": "openid profile userid transactions",
  "exp": 1611674155,
  "delegationId": "93f036b6-7cdc-4f9e-89a6-2ab5ec635fbc",
  "iat": 1611673855,
  "user_id": 123,
  "user_role": "customer",
  "user_subscription_level": "gold",
  "user_company_id": 0
}

Each API that receives the JWT will validate it via a library, enabling the custom claims to be included in the Claims Principal. It is then straightforward to implement authorization rules using the available values.

Video

This video provides a step by step guide to issuing claims, starting with an API claims endpoint that is called by the Curity Identity Server at the time of token issuance.

Conclusion

By separating concerns, the Curity Identity Server provides a powerful toolbox that provides complete flexibility when dealing with claims. Each of the above behaviors can be customized, and each implementation does a single job well.

Keep up with our latest articles and how-tos RSS feeds.