Skip to content

Scope and claims design in IdentityServer

February 25, 2019

Very often I see developers that are confused about the relationship of scopes and claims in IdentityServer. Hopefully this blog post will help.

In OpenID Connect and OAuth 2.0 the definition of a scope is a resource that a client application is trying to get access to. This concept of a resource is deliberately vague and the confusion is exacerbated by the two different specs using the scope concept for two similar yet disparate uses. Also, designing scopes is up to the application developer which means you must impart semantics into your scopes, and this requires some amount of planning and/or design.

OpenID Connect and Identity Scopes

Given that OpenID Connect is all about an application authenticating a user, then the scope, as a resource, means that the application wants identity data about a user. For example, the user’s unique ID, name, email, employee ID, or something else along those lines. This scope is an identity resource and is an alias for some number of claims that the application requires about the user.

The OpenID Connect specification defines some scopes, for example openid which simply maps to the user’s unique ID (or sub claim), and profile which maps to about 10+ claims which include the user’s first name, last name, display name, website, location, etc. Custom identity scopes are allowed and the scope of the scope, so to speak, is defined by the application developer. So a custom scope called employee_info could be defined which could represent the employee ID, building number, and office number.

In IdentityServer, these identity scopes are modeled with the IdentityResource class. The constructor allows you to pass the name of the scope (e.g. employee_info) and a string array which is the list of claims that scope represents.

new IdentityResource("employee_info", new[] {
    "employee_id", "building_number", "office_number"})

The scopes (and corresponding claims) defined by the OpenID Connect specification are provided by the IdentityResources class and its nested classes such as OpenId, Profile, etc.

var identityResources = new[] { 
    new IdentityResources.OpenId(), 
    new IdentityResources.Profile(),
};

The nice aspect of this design is that the claims are only delivered to the application if needed as expressed by the scopes requested, and different applications can receive different claims by requesting different scopes.

OAuth 2.0 and API Scopes

Given that OAuth 2.0 is all about allowing a client application access to an API, then the scope is simply an abstract identifier for an API. A scope could be as coarse grained as “the calendar API” or “the document storage API”, or as fine grained as “read-only access to the calendar API” or “read-write access to the calendar API”. It’s possible that other semantics could be infused into your scope definitions as well. This scope is an API scope and models an application’s ability to use an API.

In IdentityServer, these API scopes are modeled with the ApiResource class. The constructor allows you to pass the name of the scope (e.g. calendar or documents).

var apiResources = new[] {
    new ApiResource("calendar"),
    new ApiResource("documents"),
};

The access token used to call these APIs will contain a minimal set of claims. Some of these claims are protocol claims (e.g. scope, issuer, expiration, etc), and there is one main user related claim which is the user’s unique ID (or sub claim). If other claims about the user are needed in one of the APIs, then the ApiResource constructor provides an additional constructor parameter as a string array which is the list of claims needed. This, in essence, allows the ApiResource class to model an API and the user claims needed by that API.

var apiResources = new[] {
    new ApiResource("calendar", new[] { "employee_id" }),
    new ApiResource("documents", new[] { "country" }),
};

The nice aspect of this design is that the claims are only present in the access token if the access token is meant to be used at those APIs.

Summary

We feel this is a nice balance for how to work with the abstract scope concepts in the OpenID Connect and OAuth 2.0 protocols, and at the same time allowing a concrete pattern for expressing what claims are needed by apps and APIs.

Hope this helps.

 

No comments yet

Leave a comment