Claims-Based Authorization and Authentication

In this lesson, we will learn the basics of claims-based authorization and how it is implemented in ASP.NET Core.

Like all modern frameworks, ASP.NET Core authorizes access to resources using claims. Claims are assertions about the subject that needs to access the resources. They are obtained through a process called authentication, which is defined in manifests called authentication schemes.

Authentication and authentication schemes

Each authentication scheme specifies the kind of action needed for authenticating a user and to compute its claims. Authentication must not be confused with login. In fact, login is the process of obtaining credentials for being authenticated in subsequent requests, while authentication is the process of validating these credentials on each request, and of extracting claims from them. The application that issues your credentials can be different from the application where we use these credentials to authenticate.

Typical credentials used by web applications are cookies and JWT (JSON Web Token) tokens.

Cookies must be necessarily emitted by the same web application where we need to authenticate as cookies cannot be used in cross-site calls. However, this doesn’t mean the user must log in to the same website where we need to authenticate. Protocols like OpenId and OAuth2 enable users to log in to an application that plays the role of identifying the provider to get a cookie for another application. These protocols work by redirecting the user browser between the two websites with information and security tokens passed in the query string URLs during these redirects. In other words, the two websites communicate through the user browser.

JWTs, on the other hand, do not have the same limitations as cookies. They are strings received from the identity provider application that we must include in all request headers, as shown below:

Authorization: Bearer <bearer token string>

The advantage of cookies is that they are automatically sent by the browser in each request to their target URLs, while JWT must be manually included each time in the request headers. That’s why usual browser requests to web servers must necessarily use cookies as browsers do not automatically include the headers needed by JWT credentials.

JWT tokens are preferred for authenticating requests to web APIs from browsers through AJAX or from native applications through HTTP clients. Web APIs prefer JWTs to cookies as they are not vulnerable to XSRF attacks (see the previous chapter on validation). In fact, XSRF attacks rely on cookies being automatically sent in all requests to their target URLs, while JWTs are not automatically sent.

Both authentication cookies and JWTs store all claims serialized and encrypted/signed so they can’t be modified, together with other information, such as their expiration date and their issuer. Therefore, authenticating them just requires some cryptographic and deserialization jobs together with expiration and issuer verifications.

Challenge and forbid actions

Authentication schemes specify what happens when an unauthenticated user tries to access a protected resource (challenge action), and when an authenticated user fails to access a protected resource for lack of privileges (forbid action).

The challenge action of all cookie-based authentication schemes is to redirect the unauthenticated user to the login page. The challenge action of all JWT-based authentication schemes is to return a 401 code with a www-authenticate: bearer header to inform the client that it needs to authenticate with a JWT.

The forbid action of cookie-based authentication schemes is to redirect to a “Forbidden” page that informs the user about the lack of the needed privileges. The forbid action of all JWT-based authentication schemes is to simply return a 403 error code.

Anatomy of claims, identities, and principals

Claims basically are value-name pairs. The name part of the pair is called claim type, and it can be public or private. Public claim types can’t create ambiguities and consequently can be used across different websites without limitations since they have a URI format that ensures their unicity. In other words, since they contain a domain part that is specific for the organization that issues the claim they can’t collide with the claims of other organizations. Private claims are generic strings with no format constraints.

In .NET, claims are represented by the Claims class that is contained in the System.Security.Claims namespace.

The main properties of the Claim class are:

Property name Description
Type A string containing the claim type
Value A string with the claim value
ValueType An optional string that specifies a target type for deserializing the claim value
Issuer A string representing the organization that issued the claim

The ClaimTypes static class in the same System.Security.Claims namespace contains several standard claim types in its static properties, including:

Property Description
ClaimTypes.Name Represents the username
ClaimTypes.Role Represents a role assigned to the user
ClaimTypes.GivenName Represents the user first name
ClaimTypes.Surname Represents the user surname

A subject may be assigned several occurrences of the same claim type with different values. This is typical of the role claim type since the same user can cover several roles, such as DB administrator, website administrator, etc.

When a user authenticates with a given authentication scheme, an object that represents the identity of the subject according to that authentication scheme is created. This object contains the list of all assigned claims, together with the authentication scheme name.

ASP.NET Core applications use the ClaimsIdentity class to represent identities. This class contains a Claims property with all claims and an AuthenticationType string property that contains the name of the authentication scheme used for authenticating the request.

ClaimsIdentity also contains read-only computed properties, such as IsAuthenticated that returns true whenever an authorization scheme authorizes that identity. This property is useful because unauthenticated requests do not contain a null HttpContext.User but an unauthorized user with an unauthorized ClaimsIdentity. ClaimsIdentity also contains a Name read-only property that reflects the value of the ClaimTypes.Name claim that is the username.

HttpContext.User, that is the user assigned to the current request, is not a ClaimsIdentity but a ClaimsPrincipal, a more complex object that may potentially contain several ClaimsIdentity instances. In fact, the same request may be simultaneously authenticated by several authentication schemes, and so the request HttpContext.User must be a type that can contain several ClaimsIdentity instances, as summarized in the picture below:

Get hands-on with 1400+ tech skills courses.