According to US law, the term information security means protecting information and information systems from unauthorized access, use, disclosure, disruption, modification, or destruction in order to provide confidentiality, integrity, and availability, referred to as the CIA triad. Confidentiality is about preventing the disclosure of information to unauthorized entities. Integrity is about preventing modifications to the data by unauthorized entities. Availability is about the data and hence the information system that owns the data being available for legitimate users.
An entity, in this sense, refers to a user or an external system that uses an information system. To prevent the disclosure of information to unauthorized entities and to prevent modifications to the data by unauthorized entities, a system must be able to differentiate between authorized and unauthorized entities. In order to do that, a system must be able to identify an entity in the first place. Once the entity is identified, a system must be able to verify that identity by validating the credentials presented by the entity against an authority. Once a system is able to identify and authenticate an entity, it is in a position to control the access to the data it owns and hence to ensure confidentiality and integrity of data.
Availability, the third aspect of the CIA triad, from a security standpoint is mostly attributed to IT administration and operation activities involving specialized hardware and software, and is typically related to application programming in a limited sense. Though security is a team effort among the IT administration, operations, and development teams, this book is for programmers and hence the focus of this chapter is on the integrity and confidentiality aspects of the CIA triad from a programming perspective.
The topic of ASP.NET Web API security is very broad. I cover the security techniques related to ASP.NET Web API in-depth in my book Pro ASP.NET Web API Security: Securing ASP.NET Web API (Apress, 2013). This chapter covers the fundamentals and the key topics important for securing your ASP.NET Web API in order to provide a basic but solid understanding of the security concepts related to ASP.NET Web API.
10.1 Implementing Direct Authentication
In this exercise, you will implement the direct authentication pattern, in which a client application submits its credentials directly to your ASP.NET Web API. As an example, you will use the basic authentication scheme defined in Request for Comments (RFC) 2617, HTTP Authentication: Basic and Digest Access Authentication for this exercise.
Authentication is the process of an application such as your web API discovering the identity of a user through an identifier and verifying that identity by validating the credentials provided by the user against an authority (such as the membership store). Direct authentication is a common security pattern in which a client presents its credentials to a service directly. There is a trust relationship between the client application and your web API. Building a trust relationship is typically done out-of-band. For example, before using your web API, a user registers with the entity hosting your service and uses the credentials created in that process to authenticate from the client application.
Basic authentication is a simple scheme and works as follows:
Figure 10-1. HTTP basic authentication
Take the following steps to implement basic authentication:
Listing 10-1. The User Domain Class
using System.Linq;
using System.Security.Cryptography;
public class User : IIdentifiable
{
public int Id { get; set; }
public string UserName { get; set; }
public byte[] Password { get; set; }
public byte[] Salt { get; set; }
public bool IsAuthentic(string password)
{
byte[] storedPassword = this.Password;
byte[] storedSalt = this.Salt;
var pbkdf2 = new Rfc2898DeriveBytes(password, storedSalt);
pbkdf2.IterationCount = 1000;
byte[] computedPassword = pbkdf2.GetBytes(32);
return storedPassword.SequenceEqual(computedPassword);
}
}
Listing 10-2. The UserConfiguration Class
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using Robusta.TalentManager.Domain;
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasKey(k => k.Id);
Property(p => p.Id)
.HasColumnName("user_id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.UserName).HasColumnName("user_name");
Property(p => p.Password).HasColumnName("password");
Property(p => p.Salt).HasColumnName("salt");
}
}
Listing 10-3. The OnModelCreating Method of the Context Class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations
.Add(new EmployeeConfiguration())
.Add(new UserConfiguration());
}
Listing 10-4. The CREATE SQL Statement for the User Table
CREATE TABLE [dbo].[user](
[user_id] [int] IDENTITY(1,1) NOT NULL,
[user_name] [char](8) NOT NULL,
[password] [binary](32) NOT NULL,
[salt] [binary](32) NOT NULL,
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED
(
[user_id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Listing 10-5. The INSERT SQL Statement
INSERT INTO [talent_manager].[dbo].[user] (user_name, password, salt) values
('jqhuman',
0x012E7C7B70462B9AF3C109E7CF565189D82D5697DD8645AEF67CA87F2B5795A7, 0x18E086233B60F812B41916A2D7F2EC73E76D26E980CD56F83606687EC778E89A)
Listing 10-6. C# Code to Insert a User Record
string data = "p@ssw0rd!"; // User-entered password
byte[] salt = new Byte[32];
using (var provider = new System.Security.Cryptography.RNGCryptoServiceProvider())
{
provider.GetBytes(salt); // Generated salt
}
var pbkdf2 = new System.Security.Cryptography.Rfc2898DeriveBytes(data, salt);
pbkdf2.IterationCount = 1000;
byte[] hash = pbkdf2.GetBytes(32); // Hashed and salted password
var user = new User() { UserName = "jqhuman", Password = hash, Salt = salt };
var repository = new Repository<User>(uow);
repository.Insert(user);
uow.Save();
Listing 10-7. The Basic Authentication Message Handler (Incomplete)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Robusta.TalentManager.Data;
using Robusta.TalentManager.Domain;
public class AuthenticationHandler : DelegatingHandler
{
private const string SCHEME = "Basic";
private readonly IRepository<User> repository = null;
public AuthenticationHandler(IRepository<User> repository)
{
this.repository = repository;
}
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
try
{
// Perform request processing – Code from Listing 10-8 goes here
var response = await base.SendAsync(request, cancellationToken);
// Perform response processing – Code from Listing 10-9 goes here
return response;
}
catch (Exception)
{
// Perform error processing – Code from Listing 10-10 goes here
}
}
}
We will now add code to the three appropriate places in the listing to handle the request, response, and error.
Listing 10-8. Request Processing
var headers = request.Headers;
if (headers.Authorization != null && SCHEME.Equals(headers.Authorization.Scheme))
{
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string credentials = encoding.GetString(
Convert.FromBase64String(headers.Authorization.Parameter));
string[] parts = credentials.Split(':'),
string userName = parts[0].Trim();
string password = parts[1].Trim();
User user = repository.All.FirstOrDefault(u => u.UserName == userName);
if (user != null && user.IsAuthentic(password))
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userName)
};
var principal = new ClaimsPrincipal(new[] {
new ClaimsIdentity(claims, SCHEME) });
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
}
Listing 10-9. Response Processing
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue(SCHEME));
}
Listing 10-10. Error Processing
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue(SCHEME));
return response;
The preceding code is a great example for illustrating the power of message handlers. The HTTP status code can be set to 401 - Unauthorized by any component in the pipeline, including the Authorize filter. By registering AuthenticationHandler as the first handler to execute after HttpServer, we get the opportunity to inspect the response as late as possible and add the necessary WWW-Authenticate header(s).
Listing 10-11. The Configure Message Handler
using System.Web.Http;
using Robusta.TalentManager.Data;
using Robusta.TalentManager.Domain;
using Robusta.TalentManager.WebApi.Core.Handlers;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//config.MessageHandlers.Add(new AuthenticationHandler());
var repository = config.DependencyResolver
.GetService(typeof(IRepository<User>))
as IRepository<User>;
config.MessageHandlers.Add(new AuthenticationHandler(repository));
}
}
[ Authorize] public HttpResponseMessage Get(int id) { ... }
from the previous chapter. Just ensure that the Authorize attribute is still applied on the action method.
Listing 10-12. The Request and Response Messages (Failure Scenario)
Request
GET http://localhost/talentmanager/api/employees/1 HTTP/1.1
Host: localhost
Response
HTTP/1.1 401Unauthorized
Content-Type: application/json; charset=utf-8
WWW-Authenticate: Basic
Date: Mon, 22 Apr 2013 13:27:35 GMT
Content-Length: 61
{"Message":"Authorization has been denied for this request."}
Listing 10-13. The Request and Response Messages (Success Scenario)
Request
GET http://localhost/talentmanager/api/employees/1 HTTP/1.1
Authorization: Basic anFodW1hbjpwQHNzdzByZCE=
Host: localhost
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 93
{"Id":1,"FirstName":"Johnny","LastName":"Human","DepartmentId":1,"RowVersion":"AAAAAAAAF3E="}
You have just made a GET request. If you have Fiddler running, it will have captured a request and response exactly the same as in Listing 10-12.
Caution In a real-world application, do not use basic authentication without SSL/TLS. HTTPS is a must for this scheme, without which anyone can sniff the traffic and get the credentials. Also, if your client is browser-based and you let the browser pop up the dialog box and collect the credentials from the end user, the credentials will be cached by the browser until you close it. If you make a request to a URI for which you are already authenticated, the browser automatically sends the credentials in the Authorize header, making this mechanism susceptible to cross-site request forgery (CSRF) attacks. For this reason, try to avoid basic authentication with browser-based clients.
10.2 Implementing Brokered Authentication
In this exercise, you will implement the brokered authentication pattern, in which a client application submits the username-and-password credential to a broker, gets a JSON Web Token (JWT), and ultimately presents that token to your ASP.NET Web API as the credential for authentication.
The brokered authentication pattern introduces a broker or a centralized entity for authentication. Even if no trust is established between the client and the service, trust relationships are established between the client and the broker and between the service and the broker. In those cases where a client will be unable or unwilling to authenticate directly to a web API using the credentials, the client can authenticate against the central broker and receive a token and submit it to the web API as the credential for authentication.
For this exercise, the broker will be just an ASP.NET handler that implements IHttpHandler. The broker takes in a username and password from the request body, authenticates them, and returns a JWT. In practice, when you deal with an entity like a broker, which is external to your application, you will resort to some kind of standard protocol to communicate with the broker and get the token. In SOAP-based services, WS-Trust is one such specification used, and the broker in that case will be a Security Token Service (STS). In the case of REST-based services, the OAuth 2.0 protocol can be used. However, for the sake of brevity, we will not implement OAuth 2.0 or an STS based on WS-Trust in this exercise. We'll just use the ASP.NET handler that responds to HTTP POST. You can find more information related to OAuth 2.0 in my other book: Pro ASP.NET Web API Security: Securing ASP.NET Web API (Apress, 2013).
Note If you need a prebuilt implementation that issues tokens through OAuth 2.0, Thinktecture.IdentityServer v2 (https://github.com/thinktecture/Thinktecture.IdentityServer.v2) is a good open source option to evaluate. IdentityServer is a lightweight STS built with .NET 4.5, MVC 4, Web API, and WCF. It supports multiple protocols (both WS-Trust and OAuth 2.0). IdentityServer can mint tokens of different formats (SAML 1.1/2.0, JWT) and integrates with ASP.NET membership, roles, and profile out-of-the-box.
Listing 10-14. The BrokerHandler Class
using System;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.Web;
public class BrokerHandler : IHttpHandler
{
private const string ISSUER = "Robusta.Broker";
private const string AUDIENCE = " http://localhost/talentmanager/api ";
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
HttpRequest request = context.Request;
string userName = request["username"];
string password = request["password"];
bool isAuthentic = !String.IsNullOrEmpty(userName) && userName.Equals(password);
if (isAuthentic)
{
// I use a hard-coded key
byte[] key = Convert.FromBase64String(
"qqO5yXcbijtAdYmS2Otyzeze2XQedqy+Tp37wQ3sgTQ=");
var signingCredentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature,
SecurityAlgorithms.Sha256Digest);
var descriptor = new SecurityTokenDescriptor()
{
TokenIssuerName = ISSUER,
AppliesToAddress = AUDIENCE,
Lifetime = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddMinutes(5)),
SigningCredentials = signingCredentials,
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, userName)
})
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(descriptor);
context.Response.Write(tokenHandler.WriteToken(token));
}
else
context.Response.StatusCode = 401;
}
}
Listing 10-15. The Web.Config File
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<handlers>
<add name="BrokerHandler"
path="jwt" verb=" POST"
type="Robusta.Broker.BrokerHandler, Robusta.Broker" />
</handlers>
</system.webServer>
</configuration>
Listing 10-16. The JwtHandler Class
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.ServiceModel.Security.Tokens;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
public class JwtHandler : DelegatingHandler
{
private const string ISSUER = "Robusta.Broker";
private const string AUDIENCE = " http://localhost/talentmanager/api ";
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
byte[] key = Convert.FromBase64String(
"qqO5yXcbijtAdYmS2Otyzeze2XQedqy+Tp37wQ3sgTQ=");
try
{
var headers = request.Headers;
if (headers.Authorization != null)
{
if (headers.Authorization.Scheme.Equals("Bearer"))
{
string jwt = request.Headers.Authorization.Parameter;
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
TokenValidationParameters parms = new TokenValidationParameters()
{
AllowedAudience = AUDIENCE,
ValidIssuers = new List<string>() { ISSUER },
SigningToken = new BinarySecretSecurityToken(key)
};
var principal = tokenHandler.ValidateToken(jwt, parms);
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
}
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue("Bearer",
"error="invalid_token""));
}
return response;
}
catch (Exception)
{
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue("Bearer", "error="invalid_token""));
return response;
}
}
}
Listing 10-17. Registering JwtHandler
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//config.MessageHandlers.Add(new AuthenticationHandler());
var repository = config.DependencyResolver
.GetService(typeof(IRepository<User>))
as IRepository<User>;
config.MessageHandlers.Add(new AuthenticationHandler(repository));
config.MessageHandlers.Add(new JwtHandler());
}
}
Listing 10-18. Request and Response Messages (Broker)
Request
POST http://localhost:24890/handler/jwt HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:24890
Content-Length: 33
username=jqhuman&password=jqhuman
Response
HTTP/1.1 200 OK
Content-Length: 311
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0L3RhbGVudG1hbmFnZXIvYXBpIiwiaXNzIjoiUm9idXN0YS5Ccm9rZXIiLCJuYmYiOjEzNjY3MDc2MTQsImV4cCI6MTM2NjcwNzkxNCwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI6ImpxaHVtYW4ifQ.3jMam6VRXlDTspZfDtvwQMvdAopA4vqiqYOhdPhZgMI
Listing 10-19. Request and Response Messages (Web API)
Request
GET http://localhost/TalentManager/api/employees/1 HTTP/1.1
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0L3RhbGVudG1hbmFnZXIvYXBpIiwiaXNzIjoiUm9idXN0YS5Ccm9rZXIiLCJuYmYiOjEzNjY3MDc2MTQsImV4cCI6MTM2NjcwNzkxNCwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI6ImpxaHVtYW4ifQ.3jMam6VRXlDTspZfDtvwQMvdAopA4vqiqYOhdPhZgMI
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 23 Apr 2013 09:06:02 GMT
Content-Length: 93
{"Id":1,"FirstName":"Johnny","LastName":"Human","DepartmentId":1,"RowVersion":"AAAAAAAAF48="}
Note Since we have given a lifetime of 5 minutes for the token, you will need to make the preceding request to the web API within this time period from when the token was issued. Otherwise, the request will fail because of the expired token.
Thus, you were able to exchange your username and password for a JWT with the broker and submit the token to your web API as the credential, and you got the response back. You trust the broker, and your web API trusts the broker as well. The web API accepts the token you presented to it because it was issued by the broker the web API trusts.
Since we plugged in JwtHandler to the pipeline, in addition to the AuthenticationHandler, the web API at this point is capable of performing direct authentication based on the username and password you send through the basic scheme (shown in the previous exercise) as well as the authentication based on JWT.
HTTP MODULES VERSUS MESSAGE HANDLERS
By implementing your authentication code in an HTTP module, you can have that code execute even before your first all-route message handler runs. If you have a web API and other resources such as HTTP handlers, pages, or MVC controllers in the same application, and you want to establish an identity in one place and share it, an HTTP module is a great option. An HTTP module sees all requests that go through the ASP.NET pipeline. A message handler only sees requests that are routed to Web API.
The flip side of using an HTTP module is that your design is no longer host-agnostic, and you are creating a dependency on web hosting (IIS). I use message handlers throughout this chapter because they are host-agnostic, but you must be aware of the trade-offs involved in choosing between a message handler and an HTTP module.
10.3 Authorizing Requests
In this exercise, you will implement authorization for your web API. Authorization, sometimes abbreviated to AuthZ, is the process of an application determining whether an entity is allowed to perform a requested action. Once an authenticated identity is established for an entity, the application can control the entity’s access to the application resources based on this identity. An extremely simple and trivial application might authorize resource access purely based on the identity. But most practical applications authorize access based on attributes, such as roles, that are associated with the identity.
Listing 10-20. The ProcessRequest Method with an Additional Claim
public void ProcessRequest(HttpContext context)
{
...
var descriptor = new SecurityTokenDescriptor()
{
TokenIssuerName = ISSUER,
AppliesToAddress = AUDIENCE,
Lifetime = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddMinutes(5)),
SigningCredentials = signingCredentials,
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, "HRManager")
})
};
...
}
Listing 10-21. The EmployeesController GET Action Method
[Authorize(Roles="HRManager")]
public HttpResponseMessage Get(int id)
{
var employee = repository.Find(id);
if (employee == null)
{
var response = Request.CreateResponse(HttpStatusCode.NotFound, "Employee not found");
throw new HttpResponseException(response);
}
return Request.CreateResponse<EmployeeDto>(
HttpStatusCode.OK,
mapper.Map<Employee, EmployeeDto>(employee));
}
You will get the JSON response back, the same as the last time. What is different now is that we are authorizing the action method Get based on the role, using the Authorize attribute. We allow access only if the user is in the role of HRManager.
[Authorize(Roles="HRDirector")] public HttpResponseMessage Get(int id) { ... }
Listing 10-22. The AuthorizeByTimeSlot Filter
using System;
using System.Web.Http;
using System.Web.Http.Controllers;
public class AuthorizeByTimeSlot : AuthorizeAttribute
{
public int SlotStartHour { get; set; }
public int SlotEndHour { get; set; }
protected override bool IsAuthorized(HttpActionContext context)
{
if (DateTime.Now.Hour >= this.SlotStartHour &&
DateTime.Now.Hour <= this.SlotEndHour &&
base.IsAuthorized(context))
return true;
return false;
}
}
using Robusta.TalentManager.WebApi.Core.Filters;
to EmployeesController.
Listing 10-23. The AuthorizeByTimeSlot Filter Applied to GET
[AuthorizeByTimeSlot(Roles="HRManager", SlotStartHour=15, SlotEndHour = 17)]
public HttpResponseMessage Get(int id)
{
// Action method logic goes here
}
Summary
Information security means protecting information and information systems from unauthorized access, use, disclosure, disruption, modification, or destruction in order to provide confidentiality, integrity, and availability, referred to as the CIA triad. Confidentiality is about preventing the disclosure of information to unauthorized entities. Integrity is about preventing modifications to the data by unauthorized entities. Availability is about the data and hence the information system that owns the data being available for legitimate users.
To prevent the disclosure of information to unauthorized entities and to prevent modifications to the data by such entities, a system must be able to differentiate between authorized and unauthorized entities. In order to do that, a system must be able to identify an entity in the first place. Once identified, a system must be able to verify the identity of the entity by validating the credentials presented by the entity against an authority; this process is called authentication. Once a system is able to identify and authenticate an entity, it is in a position to control the access to the data it owns, and this process is called authorization. Authentication and authorization are fundamental to ensure confidentiality and integrity.
Direct authentication is a common security pattern in which a client presents its credentials to a service directly. There is a trust relationship between the client and the service. Brokered authentication, on the other hand, is a pattern that introduces a central authentication entity, called the broker. Even if no trust is established between the client and the service, trust relationships are established between the client and the broker and between the service and the broker.
In the area of direct authentication, HTTP authentication schemes are commonly used. Request for Comments (RFC) 2617, HTTP Authentication: Basic and Digest Access Authentication, provides the specification for the HTTP authentication framework. The basic authentication scheme is based on the model that the client must authenticate itself with a user ID and a password. In the case of the brokered authentication pattern, in which a broker issues a token to a client application based on a protocol such as OAuth 2.0 or WS-Trust, the token with the claims set is presented to the service as the credential. In this chapter, we looked at one token format, which is JSON Web Token (JWT).