Scopes, Claims and the Client

Scopes, Claims and the Client

develop

Thinking about claims from a client perspective

Overview

This article breaks down the different aspects of claims and scopes from a client perspective.

If you're new to claims we recommend to start with the introduction and claims explained first.

The client requests tokens, and by using the OpenID Connect scope and claims request parameters it's possible to request certain claims to be part of the resulting tokens.

Client Request


Web or Mobile

When working with claims, there is no need to make a distinction between mobile or web clients. This article therefore assumes any client when discussing the workflow needed.

Scopes and Claims

The Client needs to understand two things. The Scope is what gives access to API's (with a valid token). But Scopes are also what gives access to claims. The ID Token is populated with (some of) the claims corresponding to the scopes authorized for the request. So if a client needs certain data in the ID Token it has two options:

  1. Request the scope that is mapped to the claim or set of claims needed
  2. Request the claim specifically

Client Configuration

A client is only allowed to ask for claims that are mapped to scopes that are pre-configured on the client. It is not possible to ask for arbitrary claims and expect them to show up in the token.

Tokens and Claims

All claims don't end up in all tokens. A client usually receives two types of tokens where claims are present: the ID Token and the Access Token.

The claims are mapped to the Tokens using the Curity Claims Mapper, which depending on configuration will map each claim to the appropriate token.

ID Token

The claims in the ID Token are intended for the Client to decode and use. These are issued during an interactive flow where the user authenticates.

Access Token

The claims in the Access Token are not for the Client but for the API when the Client passes the Access Token in the call to the API. More about this in the claims for APIs overview.

Scope to Claim Mapping

Claims Mapping

In the image above the account-nbr claim is only present in the Access Token. This means that the client never will be able to read that claim, instead it's intended for the API to use. But the client can (and should) still request it as part of the invoice scope.

The tokens aren't the only place where claims are used. The OpenID Connect Userinfo endpoint also provides resolved claims. This is useful when the ID Token needs to be kept small.

Claims mapper

For more details about the mapping of claims see the Claims Explained article.

Requesting Claims

Using the scope parameter

The most straightforward way to request claims is to use the scope parameter, and simply request the scope that contains the claims needed.

Example OpenID Connect Code Flow

    GET https://login.example.com/authorize \
        ?client_id=app1 \
        &scope=invoice  \
        &state=<STATE>  \
        &nonce=<NONCE>  \
        &redirect_uri=<REDIRECT_URI>

The server will map the scope to the appropriate claims and map the claims to tokens and the userinfo endpoint.

Using the claims parameter

For a more fine grained approach it's possible to request specific claims directly and in what token they should end up. However, the claims will only end up in the requested token if the following conditions apply:

  1. The claim is part of a scope that the client is allowed to request
  2. The claim is allowed to be mapped to the token the client requests (via the claims mapper)

If either of the above conditions fail to match, the claim will not be part of the resulting token. It will be silently dropped.

Example of the claims request parameter

{
  "id_token":
    {
     "account-nbr": null,
     "customer-id": {"essential": true}
    }
}

As the example shows, the client requests to get both the account-nbr and the customer-id claim in the id_token, but the customer-id claim is marked essential, which means that if it's not possible to issue, the request should fail and return with an error.

If we assume the mapping as presented in the Mapping section, only customer-id is allowed to be mapped to the ID Token, so the account-nbr will be silently dropped.

By default the following mappings are possible to request:

  • id_token
  • userinfo
  • access_token

For more details on how to use the claims parameter see the OpenID Connect Core Specification on Claims.

However, Curity allows custom tokens to be defined and mapped, and if such exist they can also be controlled with the claims parameter. An example would be a custom mapping called my_token which would then allow the my_token object to be present in the claims request parameter.

Receiving Claims

Depending on the mapping the claims will either end up in tokens for the client, such as the the ID Token or it will end up in the userinfo endpoint. In either case claims are only transmitted to the client in a way where the client is sure to be able to verify who issued the claims. This is important so that the client always only use data from trusted sources.

An example of a decoded ID Token with the above claims would be:

{
  "at_hash" : "OjYn67xN95k3gJp3YEcHbg",
  "delegation_id" : "a4430aa3-bd20-4d12-a05c-de3e9afef79d",
  "acr" : "urn:se:curity:authentication:html-form:html-primary",
  "s_hash" : "_6mDB4mxYlNV5y1o-8chWw",
  "azp" : "app1",
  "auth_time" : 1555840126,
  "exp" : 1555843739,
  "nbf" : 1555840139,
  "jti" : "c6a7baeb-8840-49f8-8742-4c6f1d7e125d",
  "iss" : "https://login.example.com/~",
  "aud" : "app1",
  "sub" : "johndoe",
  "iat" : 1555840139,
  "purpose" : "id",
  "customer-id": "5003-2213"
}

Conclusion

Claims are grouped by scopes and a claim always belongs to a scope. It's possible to request claims by using the scope parameter and get all mapped claims to that scope, or directly with the claims parameter to get a more fine grained set of claims in the token.

Depending on how the mappers are configured in Curity, the claims will either end up in the ID Token, Userinfo endpoint or in the Access Token. They may also end up in custom tokens when such mappings are available.

Was this page helpful?