KEMBAR78
Implementing JWT Authentication in ASP | PDF | Representational State Transfer | World Wide Web
0% found this document useful (0 votes)
310 views34 pages

Implementing JWT Authentication in ASP

1) RESTful web services are web services that are designed based on the constraints of REST (REpresentational State Transfer). 2) The six constraints of REST include having a client-server architecture, being stateless, having responses that can be cached, and having a uniform interface. 3) Following the REST constraints leads to desirable properties like scalability and simplicity that are important for large web systems.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
310 views34 pages

Implementing JWT Authentication in ASP

1) RESTful web services are web services that are designed based on the constraints of REST (REpresentational State Transfer). 2) The six constraints of REST include having a client-server architecture, being stateless, having responses that can be cached, and having a uniform interface. 3) Following the REST constraints leads to desirable properties like scalability and simplicity that are important for large web systems.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 34

What are RESTful Web Services?

To put it mildly, the World Wide Web was an unexpected success.

What had started out as a convenient way for research labs to connect with each other suddenly exploded in size. Jakob
Nielsen estimated that between 1991 and 1997 the number of web sites grew with a staggering 850% per year each year!

This incredible growth worried some of the early web pioneers, because they knew that the underlying software was never designed
with such massive amount of users in mind.

So they set out to define the web standards more clearly, and enhance them so that the web would continue to flourish in this new
reality where it was suddenly the world’s most popular network.

One of these web pioneers was Roy Fielding, who set out to look at what made the internet software so successful in the first place
and where it was lacking, and in his fascinating PhD dissertation he formalized his findings into six constraints, which he collectively
called REpresentional State Transfer (REST).
Fielding’s observation was that if your architecture satisfies these six constraints then it will exhibit a number of desirable
properties (like scalability, decoupling, simplicity), which are absolutely essential in an Internet-sized system.

His idea was that the constraints should be used as a checklist to evaluate new potential web standards, so that poor design could be
spotted early, and way before it was suddenly deployed to millions of web servers.

He successfully used the constraints to evaluate new web standards, such as HTTP 1.1 (where he was one of the principal authors)
and URI (where he was also one of the authors). These standards have both stood the test of time, despite the immense pressure of
being essential protocols on the web and used by billions of people each day.

So a natural question to ask is that if following these REST constraints lead to such great systems, why only used them for browsers
and web sites? Why not also create web services that conform to them, so we can enjoy the desirable properties that they lead to?

This thinking led to the idea of RESTful Web Services, which are basically web services that satisfy the REST constraints, and are
therefore well-suited for Internet-scale systems.

So what are these 6 REST constraints?

1. Client-Server

The first constraint is that the system must be made up of clients and servers.

Servers have resources that clients want to use. For example, a server has a list of stock prices (i.e. a resource) and the client would
like to display these prices in some nice graphs.

There is a clear separation of concerns between the two. The server takes care of the back-end stuff (data storage, business rules,
etc.) and the client handles the front-end stuff (user interfaces).

The separation means that there can be many different types of clients (web portals, mobile apps, BPM engines, etc.) that access the
same server, and each of these can evolve independently of the other clients and the server (assuming that the interface between
the clients and server is stable).

The separation also seriously reduces the complexity of the server, as it doesn’t need to deal with UI stuff, which improves
scalability.

This is probably the least controversial constraint of REST as client-server is so ubiquitous today that we almost forget that there are
other styles to consider (like event-based protocols).
It important to note that while HTTP is almost always used when people develop RESTful Web Services, there is no constraint that
forces us to use it. We could use FTP as the underlying protocol, if we really wanted. Even though intellectual curiosity is probably
the only good reason for trying that.

2. Stateless

To further simplify interactions between clients and servers, the second constraint is that the communication between them must
be stateless.

This means that all information about the client’s session is kept on the client, and the server knows nothing of it (so no cookies,
session variables, or other naughty stuff!) The consequence is that each request must contain all information necessary to perform
the request (i.e. it cannot rely on any context information).

The stateless constraint simplifies the server as it no longer needs to keep track of client sessions, resources between requests, and
it does wonders for scalability because the server can quickly free resources after requests have been finished.

It also makes the system easier to reason about as you can easily see all the input data for a request and what output data it resulted
in. You no longer need to lookup session variables and other stuff that makes the system harder to understand.

In addition, it will also be easier for the client to recover from failures, as the session context on the server has not suddenly gotten
corrupted or out of sync with the client. Roy Fielding even goes as far as writing in an old newsgroup post that reliance on server-
side sessions is one of the primary reasons behind failed web applications and on top of that it also ruins scalability.
So far nothing too controversial in the constraints. Many RPC implementations could probably satisfy both the Client-Server and
Stateless constraints.

3. Cache

The last constraint on the client-server communication is that responses from servers must be marked as cacheable or non-
cacheable.

An effective cache can reduce the number of client-server interactions, which contributes positively to the performance of the
system. At least, from a user’s point of view.

Protocols, like SOAP, that only uses HTTP as a convenient way to get through firewalls (by using POST for all requests) miss out on
the improved performance from HTTP caching, which reduces their performance (and also slightly undermines the basic purpose of
a firewall.)

4. Uniform Interface

What really separate REST from other architectural styles is the Uniform Interface enforced by the fourth constraint.

We don’t usually think about it, but it’s pretty amazing that you can use the same Internet browser to read the news, and to do your
online banking. Despite these being fundamentally different applications. You don’t even need an extension to the browser to do
any of this!

We can do this because the Uniform Interface decouples the interface from the implementation, which makes interactions so simple
that it’s easy for somebody familiar with the style to understand it, even automatically (like Googlebot).

The Uniform Interface constraint is made up of 4 sub-constraints:

4.1. Identification of Resources

The REST style is centered around resources. This is unlike SOAP and other RPC styles that are modeled around procedures (or
methods).

So what is a resource? A resource is basically anything that can be named. From static picture to a feed with real-time stock prices.
But in enterprise software the resources are usually the entities from the business domain (i.e. customers, orders, products, etc.) On
an implementation level, it is often the database tables (with business logic on top) that are exposed as resources. But you can also
model a business process or workflow as resource.

Each resource in a RESTful design must be uniquely identifiable via an URI (Uniform Resource Identifier) and the identifier must be
stable even when the underlying resource is updated (i.e. “Cool URIs don’t change”).

This means that each resource you want to expose through a RESTful web service must have its own URI. Normally, you would use
the first URI below to access a collection of resources (i.e. several customers) and the second URI to access a specific resource inside
that collection (i.e. a specific customer):

1) https://api.example.com/customers
2) https://api.example.com/customers/932612
Some well-known APIs that claim to be RESTful fail this sub-constraint. For example, Twitter’s REST APIs uses RPC-like URIs
like statuses/destroy/:id and it’s the same with Flickr.

The problem is that they break the Uniform Interface requirement, which adds unnecessary complexity to their APIs.

4.2 Manipulation of Resources through Representations

The second sub-constraint in the Uniform Interface is that resources are manipulated through representations.

This means that the client does not interact directly with the server’s resource. For example, we don’t allow the client to run SQL
statements against our database tables.

Instead, the server exposes a representation of the resource’s state. It can sound complicated, but it’s not.

It just means that we show the resource’s data (i.e. state) in a neutral format. This is similar to how the data for a web page can be
stored in a database, but is always send to the browser in HTML.

The most common format for RESTful web services is JSON, which is used in the body of the HTTP requests and responses:
{

"id": 12,

"firstname": "Han",

"lastname":"Solo"

When a client wants to update the resource, it gets a representation of that resource from the server, updates the representation
with the new data, send the updated representation to the server, and ask the server to update its resource so it corresponds with
the new representation.

The benefit is that you avoid a strong coupling between the client and server (like with RMI in Java), so you can change the
underlying implementation without affecting the clients. It also makes it easier for clients as they don’t need to understand the
underlying technology used by each server that they interact with.

4.3 Self-Descriptive Messages

The third constraint in the Uniform Interface is that each message (i.e. request/response) must include enough information for the
receiver to understand it in isolation.
Each message must have a media type (for instance, application/json or application/xml) that tells the receiver how the message
should be parsed.
HTTP is not formally required for RESTful web services, but if you use the HTTP methods you should follow their formal meaning, so
the user won’t rely on out of band information to understand them (i.e. don’t use POST to retrieve data, or GET to save data).

So for the Customer URIs, which we defined earlier, we can expose the following methods for the client to use:

TASK METHOD PATH

Create a new customer POST /customers

Delete an existing customer DELETE /customers/{id}

Get a specific customer GET /customers/{id}

Search for customers GET /customers

Update an existing customer PUT /customers/{id}

The benefit is that the four HTTP methods are clearly defined, so an API user who knows HTTP, but doesn’t know our system can
quickly guess what the service is doing by only looking at the HTTP method and URI path (i.e. if you hide the first column, a person
who knows HTTP can guess what it says based on the two new columns).

Another cool thing about self-descriptive message is that (similar to statelessness) you can understand and reason about the
message in isolation. You don’t need some out-of-band information to decipher it, which again simplifies things.

4.4 Hypermedia as the Engine of Application State

The fourth and final sub-constraint in the Uniform Interface is called Hypermedia as the Engine of Application State (HATEOAS). It
sounds a bit overwhelming, but in reality it’s a simple concept.

A web page is an instance of application state, hypermedia is text with hyperlinks. The hypermedia drives (i.e. engine) the
application state. In other words, we click on links to move to new pages (i.e. application states).

So when you are surfing the web, you are using hypermedia as the engine of application state!

So it basically means that we should use links (i.e. hypermedia) to navigate through the application. The opposite would be to take
an Customer ID from one service call, and then use it as an input parameter to another service call.

It should work like a good web site where you just enter the URI and then you just follow the links that are provided on the web
pages. You don’t need to know more than the initial URI.

For example, inside a customer representation there could be a links section with links to the customer’s orders:

"id":12,
"firstname":"Han",

"lastname":"Solo",

"_links": {

"self": {

"href":"https://api.example.com/customers/12"

},

"orders": {

"href":"https://api.example.com/customers/12/orders"

The service can also provide the links in the Link HTTP header, and W3C is working on a standard definition for the relation types, so
we can use standardized meanings which further helps the user.

An enormous benefit is that the API user doesn’t need to look in the API documentation to see how to find the customer’s orders, so
he or she can easily explore while developing without having to refer to out-of-band API documentation.

It also means that the API user doesn’t need to hardcode (and manually construct) the URIs that he or she wants to call. It might
sound like a trivial thing, but Craig McClanahan (co-designer of The Sun Cloud API) wrote in an informative blog post that in his
experience 90% of client defects were caused by badly constructed URIs.
Roy Fielding didn’t write that much about the hypermedia sub-constraint in his PhD dissertation (due to lack of time), but he later
wrote a blog post where he clarified some of the details.

5. Layered System

The fifth constraint is another constraint on top of the Uniform Interface, which says that the client should only know the immediate
layer it is communicating with, and not be aware of any layers behind it.

This means that the client doesn’t know if it’s talking with an intermediate, or the actual server. So if we place a proxy or load
balancer between the client and server, it wouldn’t affect their communications and we wouldn’t need to update the client or server
code.

It also means that we can add security as a layer on top of the web services, and then clearly separate business logic from security
logic.

6. Code-On-Demand (optional)

The sixth and final constraint is the only optional constraint in the REST style.

Code-On-Demand means that a server can extend the functionality of a client on runtime, by sending code to it that it should
execute (like Java Applets, or JavaScript).
I have not heard of any RESTful web services that actually send code from the server to the client (after deployment) and gets it
executed on the client, but could be a powerful way to beef up the client.

A really nice feature of the simplicity that is enforced by these six constraints (especially, uniform interface and stateless
interactions) is that the client code becomes really easy to write.

Most modern web framework can figure out what to do, if we follow the conventions above and they can take care of most of the
boilerplate code for us.

For example, in the new Oracle JET toolkit, we simply need the JavaScript below to create a customer (and it would be just as easy
in AngularJS):
// Only code needed to configure the RESTful Web Service

var Customer = oj.Model.extend({

urlRoot: "http://api.example.com/customers",

idAttribute: "id"

});

// Create a new customer representation

var customer = new Customer();

customer.attributes.firstName = "Han";

customer.attributes.lastName = "Solo";

// Ask the server to save it

customer.save().then(function() {

console.log("Saved!");

});

And it’s just as easy to call the other HTTP methods.

So the front-end engineer just need to add a few more lines to add an HTML form where the user can enter the values, and voila we
have a basic web app!

And if we use one of the many gorgeous UI frameworks (like Twitter’s Bootstrap or Google’s Materialize), we can quickly develop
something really nice looking in a really short time.

That’s it for now. Thank you for reading and take good care until next time!

Implementing JWT Authentication in ASP.NET Core 5

With the surge in APIs and their consumption globally, API security is extremely important these days. JWT
authentication is a standard way for protecting APIs - it's adept at verifying the data that's transmitted over the wire
between APIs and the clients that consume the APIs. You can even safely pass claims between the communicating
parties as well.

JSON Web Tokens (commonly known as JWT) is an open standard to pass data between client and server, and enables
you to transmit data back and forth between the server and the consumers in a secure manner. This article talks about
how you can take advantage of JWTs to protect APIs.

You can download the source code for this article posted in GitHub: https://github.com/joydipkanjilal/jwt-aspnetcore

Prerequisites

If you're to work with the code examples discussed in this article, you should have the following installed in
your system:

 Visual Studio 2019 (an earlier version will also work but Visual Studio 2019 is preferred)
 .NET 5.0
 ASP.NET 5.0 Runtime
You can download Visual Studio 2019 from here: https://visualstudio.microsoft.com/downloads/.
You can download .NET 5.0 and ASP.NET 5.0 runtime from here: https://dotnet.microsoft.com/download/dotnet/5.0.
What Are JSON Web Tokens (JWT)?

JSON Web Token is an open standard (RFC 7519) that defines a safe, compact, and self-contained, secured way for
transmission of information between a sender and a receiver through a URL, a POST parameter, or inside the HTTP
Header. It should be noted that the information to be transmitted securely between two parties is represented in JSON
format and it is cryptographically signed to verify its authenticity. JWT is typically used for implementing authentication
and authorization in Web applications. Because JWT is a standard, all JWTs are tokens but the reverse is not true. You
can work with JSON Web Tokens in .NET, Python, Node.js, Java, PHP, Ruby, Go, JavaScript, etc.

Figure 1 illustrates how a typical JWT authentication works.

Figure 1: JWT authentication at work

JWT is represented as a combination of three base64url encoded parts concatenated with period ('.')
characters and comprises the following three sections:

 Header
 Payload
 Signature
Header Section

This section provides metadata about the type of data and the algorithm to be used to encrypt the data that is to be
transferred. The JWT header comprises three sections - these include: the metadata for the token, the type of signature,
and the encryption algorithm. It comprises two properties, i.e., “alg” and “typ”. Although the former relates to the
cryptography algorithm used, i.e., HS256 in this case, the latter is used to specify the type of the token, i.e., JWT in this
case.

{
"typ": "JWT",
"alg": "HS256"
}
Payload

The payload represents the actual information in JSON format that is to be transmitted over the wire. The code snippet
given below illustrates a simple payload.

{
"sub": "1234567890",
"name": "Joydip Kanjilal",
"admin": true,
"jti": "cdafc246-109d-4ac9-9aa1-eb689fad9357",
"iat": 1611497332,
"exp": 1611500932
}
The payload typically contains claims, the identity information of the user, the allowed permissions, etc. You can use
claims to transmit additional information. These are also called JWT claims and are of two types: Reserved and Custom.
Here's a list of the reserved claims:

 iss: This represents the issuer of the token.


 sub: This is the subject of the token.
 aud: This represents the audience of the token.
 exp: This is used to define token expiration.
 nbf: This is used to specify the time before which the token must not be processed.
 iat: This represents the time when the token was issued.
 jti: This represents a unique identifier for the token.
You can also use custom claims, which can be added to the token using a rule.

Signature

The signature adheres to the JSON Web Signature (JWS) specification and is used to verify the integrity of the data
transferred over the wire. It comprises a hash of the header, the payload, and the secret, and is used to ensure that the
message was not changed while being transmitted. The final signed token is created by adhering to the JSON Web
Signature (JWS) specification. The encoded JWT header and as well as the encoded JWT payload is combined and then
it's signed using a strong encryption algorithm such as HMAC SHA 256.

Getting Started

First, create a new ASP.NET Core MVC 5 project in Visual Studio 2019. You can create a project in Visual Studio 2019 in
several ways. When you launch Visual Studio 2019, you'll see the Start window. You can choose “Continue without
code” to launch the main screen of the Visual Studio 2019 IDE. In the menu of the main screen, you can select File >
New > Project to launch the screen shown in Figure 2.

Figure 2: Select the project template and specify authentication and the target framework.
Next, follow the sequence of steps in Visual Studio 2019 to create a new ASP.NET Core MVC 5 project. I'll use this project
throughout this article.

Install NuGet Package

So far so good. The next step is to install the necessary NuGet Package(s). A NuGet package is represented as a file that
has a .nupkg extension and is comprised of compiled code (also called DLLs), other  related  files, and a manifest that
provides information related to the package such as version number, etc.
To install the required packages into your project, execute the following commands at the NuGet Package Manager
Console.

 dotnet add package Microsoft.AspNetCore.Authentication


 dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Implementing JWT in ASP.NET Core 5 MVC

In this section, I'll examine how you can implement JWT authentication in ASP.NET Core MVC 5 application. In this
example, you'll be using the following classes and interfaces:
 HomeController: This is the controller class that contains all the action methods.
 ITokenService: This is an interface that contains the declaration of two methods, i.e.,  BuildToken  and  IsTokenValid .
The former is used to build the token and the latter is used to check whether a given token is valid.
 TokenService: This class extends the  ITokenService  interface and implements its methods.
 IUserRepository: This interface contains the declaration of the  GetUser  method that's used to get
an  UserDTO  instance based on the username from an instance of  UserModel  class.
 UserRepository: The  UserRepository  class extends the  IUserRepository  interface and implements
the  GetUser  method. It also contains the sample data used by the application as a list of the  UserDTO  class.

Creating the Model Classes

There are two entities you can use in this application: the  UserModel  and the  UserDTO  classes. The following code
snippet shows how the  UserModel  class looks:
public class UserModel
{
[Required]
public string UserName { get; set; }

[Required]
public string Password { get; set; }
}
Next, create a class named UserDTO with the following content:

public class UserDTO


{
public string UserName { get; set; }
public string Password { get; set; }
public string Role { get; set; }
}
The UserDTO represents the user data transfer object and contains three string properties:  UserName ,  Password ,
and  Role . You'll use this class at several places in your application.

Configuring JWT in the AppSettings File

Create a section in the  appsettings.cs  file called Jwt with the following content inside:
"Jwt": {
"Key": "This is where you should specify your secret key, which is used to sign and verify Jwt tokens.",
"Issuer": "www.joydipkanjilal.net"
}

Replace the text mentioned in “Key” above with the actual key you would like to use as a secret. After you've added the
new section your  appsettings.cs  file would look like this:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Jwt": {
"Key": "ThisismySecretKey",
"Issuer": www.joydipkanjilal.net
"Audience": "http://localhost:36145/"
},
"AllowedHosts": "*"
}
Configure Authentication with Bearer and JWT

In the  ConfigureServices  method of the  Startup  class, I should mention that you'll use the  AddAuthentication  feature
as well as  JwtBearer  using the  AddJwtBearer  method, as shown in the code snippet below.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new
SymmetricSecurityKey
(Encoding.UTF8.GetBytes
(Configuration["Jwt:Key"]))
};
});
The following code can be used in the  ConfigureServices  method of the  Startup  class to add a transient service of
type  IUserRepository  and  IITokenService  respectively.

services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ITokenService, TokenService>();
Once these instances have been added, you can take advantage of dependency injection in the constructor of
the  HomeController  class to retrieve these instances from the container.
The complete code of the  ConfigureServices  method is given in Listing 1.

Listing 1: The ConfigureServices Method


public void ConfigureServices(IServiceCollection services)

services.AddControllers();
services.AddTransient<IUserRepository, UserRepository>();

services.AddTransient<ITokenService, TokenService>();

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>

options.TokenValidationParameters = new TokenValidationParameters

ValidateIssuer = true,

ValidateAudience = true,

ValidateLifetime = true,

ValidateIssuerSigningKey = true,

ValidIssuer = Configuration["Jwt:Issuer"],

ValidAudience = Configuration["Jwt:Issuer"],

IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))

};

});

}
You'll take advantage of session state in this example to store the generated token. You should make a call to
the  UseSession  extension method in the  Configure  method of the  Startup  class to enable session state for your
application. The code snippet below shows how you can retrieve the generated token from the session and then add it
as a bearer token in the request header.

app.Use(async (context, next) =>


{
var token = context.Session.GetString("Token");
if (!string.IsNullOrEmpty(token))
{
context.Request.Headers.Add("Authorization", "Bearer " + token);
}
await next();
});
Listing 2 illustrates the complete source code of the  Configure  method - note how you can specify that session state,
authentication, and routing that will be used.
Listing 2: The Configure Method
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

if (env.IsDevelopment())

app.UseDeveloperExceptionPage();

else

app.UseExceptionHandler("/Home/Error");

app.UseSession();

app.Use(async (context, next) =>

var token = context.Session.GetString("Token");

if (!string.IsNullOrEmpty(token))

context.Request.Headers.Add("Authorization", "Bearer " + token);

await next();

});

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();

app.UseEndpoints(endpoints =>

endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");


});

Create the Views

Replace the source code of the Index.cshtml with the code in Listing 3.
Listing 3: The Login View
@model CodeMagazineMVCDemo.Models.UserModel

@{ ViewData["Title"] = "Index"; }

<hr />

<div class="row">

<div class="col-md-4">

<form asp-action="Login">

<div asp-validation-summary="ModelOnly" class="text-danger"></div>

<div class="form-group">

<label asp-for="UserName" class="control- label"></label>

<input asp-for="UserName" class="form-control" />

<span asp-validation-for= "UserName" class="text-danger"></span>

</div>

<div class="form-group">

<label asp-for="Password" class="control-label"></label>

<input asp-for="Password" type="password" class="form-control"/>

<span asp-validation-for="Password" class="text-danger"></span>

</div>

<div class="form-group">
<input type="submit" value="Login" class="btn btn-primary" />

</div>

</form>

</div>

</div>

@section Scripts {@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}}

Next, create a view named MainWindow.cshtml and write the following code in there

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>CodeMag JWT Demo</title>
</head>

<body>
@{
ViewBag.Title = "Demonstrating JWTs in ASP.NET Core MVC 5";
}

<p>You're logged in as:&nbsp;@User.Identity.Name</p>


<p>@ViewBag.Message</p>
</body>
</html>
Lastly, create a view named Error.cshtml and write the following code in there

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error...</title>
</head>

<body>
<p>@ViewBag.Message</p>
</body>
</html>
Create the UserRepository Class

A repository class is an implementation of the Repository design pattern and is one that manages data access. The
application takes advantage of the repository instance to perform CRUD operations against the database. In this
example, the HomeController interacts with the  UserRepository  to retrieve a user based on the username and
password.
Create a file named IUserRepository.cs with the following content inside:
public interface IUserRepository
{
UserDTO GetUser(UserModel userMode);
}
The  UserRepository  class extends the  IUserRepository  interface and implements the  GetUser  method, as can be seen
in Listing 4. It also builds a list of  UserDTO  objects. Note that the password here has been hardcoded for simplicity.

Listing 4: The UserRepository Class


public class UserRepository : IUserRepository {

private readonly List<UserDTO> users = new List<UserDTO>();

public UserRepository(){

users.Add(new UserDTO {

UserName = "joydipkanjilal",

Password = "joydip123",

Role = "manager" });

users.Add(new UserDTO {

UserName = "michaelsanders",

Password = "michael321",

Role = "developer" });

users.Add(new UserDTO {

UserName = "stephensmith",

Password = "stephen123",

Role = "tester" });

users.Add(new UserDTO {

UserName = "rodpaddock",

Password = "rod123",

Role = "admin" });


users.Add(new UserDTO {

UserName = "rexwills",

Password = "rex321",

Role = "admin" });

public UserDTO GetUser(UserModel userModel) {

return users.Where(x => x.UserName.ToLower() == userModel.UserName.ToLower()

&& x.Password == userModel.Password).FirstOrDefault();

Create the TokenService Class

Create an interface called  ITokenService  with the following content: c


public interface ITokenService
{
string BuildToken(string key, string issuer, UserDTO user);
bool ValidateToken(string key, string issuer, string audience, string token);
}
The  TokenService  class extends the  ITokenService  interface and implements its methods as shown in Listing 5.
Listing 5: The TokenService Class
public class TokenService : ITokenService{

private const double EXPIRY_DURATION_MINUTES = 30;

public string BuildToken(string key, string issuer, UserDTO user) {

var claims = new[] {

new Claim(ClaimTypes.Name, user.UserName),

new Claim(ClaimTypes.Role, user.Role),

new Claim(ClaimTypes.NameIdentifier,

Guid.NewGuid().ToString())

};

var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));

var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);

var tokenDescriptor = new JwtSecurityToken(issuer, issuer, claims,


expires: DateTime.Now.AddMinutes(EXPIRY_DURATION_MINUTES), signingCredentials: credentials);

return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);

public bool IsTokenValid(string key, string issuer, string token){

var mySecret = Encoding.UTF8.GetBytes(key);

var mySecurityKey = new SymmetricSecurityKey(mySecret);

var tokenHandler = new JwtSecurityTokenHandler();

try {

tokenHandler.ValidateToken(token, new TokenValidationParameters {

ValidateIssuerSigningKey = true, ValidateIssuer = true, ValidateAudience = true, ValidIssuer = issuer, ValidAudience = issuer,

IssuerSigningKey = mySecurityKey, }, out SecurityToken validatedToken);

catch {

return false;

return true;

Create the HomeController Class

Finally, we come to the controller class. In the  HomeController  class, you'll take advantage of dependency injection to
be able to use instances of the  Configuration ,  TokenService , and  UserRepository  classes. Create the following read-
only instances for each of the three interfaces as shown below:
private readonly IConfiguration_config;
private readonly ITokenService_tokenService;
private readonly IUserRepository_userRepository;
The  ITokenService  and  IUserRepository  instances are created in the  ConfigureServices  method of the  Startup  class, as
shown in the code snippet given below:
services.AddScoped<ITokenService, TokenService>();
services.AddScoped<IUserRepository, UserRepository>();
The  IConfiguration  interface is declared in the  Startup  class as a read-only property, as shown below:
public IConfiguration Configuration { get; }
Here's how constructor injection is used in the  HomeController  class for each of the instances discussed earlier.
public HomeController (IConfiguration config, ITokenService tokenService, IUserRepository userRepository)
{
_tokenService = tokenService;
_userRepository = userRepository;
_config = config;
}
The complete source code of the  HomeController  class is given in Listing 6.
Listing 6: The HomeController Class
public class HomeController : Controller {

private readonly IConfiguration _config;

private readonly IUserRepository _userRepository;

private readonly ITokenService _tokenService;

private string generatedToken = null;

public HomeController (IConfiguration config, ITokenService tokenService, IUserRepository userRepository) {

_config = config;

_tokenService = tokenService;

_userRepository = userRepository;

public IActionResult Index() {

return View();

[AllowAnonymous]

[Route("login")]

[HttpPost]

public IActionResult Login(UserModel userModel) {

if (string.IsNullOrEmpty(userModel.UserName) || string.IsNullOrEmpty(userModel.Password))

return (RedirectToAction("Error"));

IActionResult response = Unauthorized();

var validUser = GetUser(userModel);

if (validUser != null) {

generatedToken = _tokenService.BuildToken(_config["Jwt:Key"].ToString(), _config["Jwt:Issuer"].ToString(), validUser);

if (generatedToken != null) {

HttpContext.Session.SetString("Token", generatedToken);

return RedirectToAction("MainWindow");
}

else

return (RedirectToAction("Error"));

else {

return (RedirectToAction("Error"));

private UserDTO GetUser(UserModel userModel) {

// Write your code here to authenticate the user

return _userRepository.GetUser(userModel);

[Authorize]

[Route("mainwindow")]

[HttpGet]

public IActionResult MainWindow() {

string token = HttpContext.Session.GetString("Token");

if (token == null)

return (RedirectToAction("Index"));

if (!_tokenService.IsTokenValid(_config["Jwt:Key"].ToString(), _config["Jwt:Issuer"].ToString(), token))

return (RedirectToAction("Index"));

ViewBag.Message = BuildMessage(token, 50);

return View();

}
public IActionResult Error() {

ViewBag.Message = "An error occured...";

return View();

private string BuildMessage(string stringToSplit, int chunkSize) {

var data = Enumerable.Range(0, stringToSplit.Length / chunkSize).Select(i => stringToSplit.Substring (i * chunkSize, chunkSize));

string result = "The generated token is:";

foreach (string str in data) {

result += Environment.NewLine + str;

return result;

Refer to the code listing of the  HomeController  class in Listing 6. The  BuildMessage  method is used to split the
generated token in multiple lines. The  GetUser  method of the  HomeController  class calls the  GetUser  method of
the  UserRepository  class to retrieve an instance of the  UserDTO  class based on the user credentials entered in the
Login screen shown in Figure 3.

Figure 3: The Login Screen as seen in the Web Browser

Run the Application

Now run the application by pressing on Ctrl + F5 or just F5. Figure 3 shows how the output looks in the Web browser.
Once you specify the user's credentials and click on Login, you'll be redirected to another Web page that shows the
name of the logged in user and the generated token, as shown in Figure 4.
Figure 4: Displaying the Generated Token

Summary

JSON Web Token (JWT) is an open standard (RFC 7519) that defines how you can securely transfer information between
two parties. You must use SSL/TLS together with JSON Web Tokens (JWT) to combat man-in-the-middle attacks. In most
cases, this should be sufficient to encrypt the payload before it is transferred over the wire. You can take advantage of
JWT as an additional layer of security as well.

Source Code

I've placed the source code in GitHub here: https://github.com/joydipkanjilal/jwt-aspnetcore. There is also a Readme file
that outlines the steps to run the same application.

ASP.NET Core 6: Autenticación JWT y Identity Core

Introducción
En este artículo exploraremos a fondo las características de los JSON Web Tokens, su composición y su implementación
utilizando Minimal APIs y ASP.NET Identity.
El código de ejemplo lo podrás encontrar en este repositorio en mi github. Espero les sea de utilidad.
Autenticación JWT Bearer

¿Qué es un Json Web Token?

Un JSON Web Token (JWT) es un estándar (RFC 7519) que define una forma segura y compacta de transmitir
información entre dos entidades en forma de un objeto JSON.
Esta información puede ser verificada y es confiable ya que está firmada digitalmente. Los JWTs pueden ser firmados
utilizando una llave privada (con un algoritmo HMAC) o con llaves públicas y privadas utilizando RSA o ECDSA.

¿Cuando deberías utilizar Json Web Tokens?

Aquí veremos un par de escenarios donde es útil y recomendable utilizar los JWTs:

 Autorización: Este es el caso de uso más común de los JWTs. Una vez que un usuario ha iniciado sesión, cada
llamada subsecuente al servicio incluirá el JWT, permitiendo al usuario acceder a rutas, servicios o recursos que
solo están permitidos con su debido token. SSO (Single Sign On) es una funcionalidad que hoy en día usa los
JWTs ampliamente, por que son de tamaño reducido y por su habilidad de ser usado entre diferentes dominios.
 Intercambio de Información: Los JWTs son útiles también para transmitir información entre dos entidades.
Debido a que los JWTs pueden estar firmados — por ejemplo, utilizando una llave pública/privada — podemos
estar seguros que quien manda la información es verdaderamente él quien lo manda. Adicionalmente, la firma
es calculada utilizando el encabezado del JWT y el contenido (payload) por lo que también estamos seguros que
el contenido del JWT no fue alterado.
¿Qué estructura tiene un JWT?
Un JWT está separado por puntos ( . ) en tres partes, las cuales son:
 Encabezado (header)
 Contenido (payload)
 Firma (signature)
Un JWT comúnmente tiene la siguiente forma.
xxxxx.yyyyy.zzzzz
Veamos que significa cada una de estas partes.

Header

El encabezado típicamente consiste de dos partes: el tipo de token (que será JWT) y el algoritmo que se está usando en
la firma, que puede ser HMAC SHA256 o RSA.

Por ejemplo:

{
"alg": "HS256",
"typ": "JWT"
}

Después, este JSON se codifica en Base64URL para formar parte del primer segmento del JWT.

Payload

La segunda parte del JWT es el contenido que se transmite o certifica (payload), el cual contiene la serie de claims.
Claims son afirmaciones sobre una entidad (usualmente, el usuario) e información adicional. Hay tres tipos de claims:
registrados, públicos y privados.

 Claims registrados: Son un conjunto de claims predefinidos que no son obligatorios pero sí recomendados, para
proveer un conjunto de claims interoperables. Algunos de ellos son: iss (issuer), exp (tiempo de
expiración), sub (subject), aud (audience), entre otros.

💡 Nótese que los nombres de los claims son de tres letras por la misma intención de mantener el JWT de tamaño
reducido.

 Claims públicos: Estos pueden ser definidos como cada quien desee, pero para evitar colisiones de nombres y
mantener un estándar (ya que puede usarse en distintos servicios), se utiliza la siguiente lista llamada IANA JSON
Web Token Registry.
 Claims privados: Estos claims son personalizados por cada quien que implemente los JWTs y al igual que los
públicos, para evitar colisiones es recomendable utilizar un formato URL con algún namespace y así asegurar
que son únicos
 Por ejemplo, un claim que guarda los roles de ASP.NET Core tendría el siguiente
nombre: http://schemas.microsoft.com/ws/2008/06/identity/claims/role.

Un ejemplo de un payload sería el siguiente:

{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

Y al igual que el header, este segmento se codifica en Base64Url.

💡Nota: Aunque los JWT estén firmados, solo están protegidos para evitar falsificaciones (editar el payload) pero de
igual forma, toda la información en el payload es visible para cualquiera. NO INCLUYAS información sensible en el
payload al menos que esté encriptada*.*

Signature

Para crear la firma debemos de tomar el header codificado, el payload codificado, una llave secreta, el algoritmo
especificado en el header y firmar todo eso.

Por ejemplo, si vamos a utilizar el algoritmo de encripción HMAC SHA256, la firma será creada de la siguiente forma:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

La firma se usará para verificar que el mensaje no ha cambiado mientras viaja por la red, y en caso de ser tokens
firmados por una llave privada de un certificado, también se puede verificar el emisor.

Juntando todo

Al final, tendremos tres cadenas de texto codificadas en Base64-URL separadas por puntos y se podrán incluir en
solicitudes HTTP o contenido HTML sin ningún problema. Esto es una forma mucho más compacta comparado a otros
estándares como SAML que utiliza XML.

Al final, tendríamos un JWT de la siguiente forma:

Si quieres jugar y generar tus propios JWT de prueba, puedes visitar jwt.io.

¿Cómo funcionan los JWT?

Cuando un usuario ha sido autenticado, el servicio deberá regresar un JSON Web Token para ser usado como sus
credenciales. Dado que esto es usado para autorizar el usuario, debes de considerar cuidar muy bien donde guardas el
token, y eliminarlo lo más pronto posible si ya no se requiere.

Cuando un usuario quiere acceder a contenido restringido en una ruta protegida, se debe de incluir el token en el HTTP
Header Authorization y utilizando el esquema Bearer.

Ejemplo:
Authorization: Bearer <token>

Generalmente en Web APIs (y como lo haremos más adelante) que son aplicaciones stateless, siempre requerirá que el
token vaya incluido en el encabezado Authorization. El servicio verificará lo necesario para determinar si es un token
válido o no, y si este es válido. leerá su información (los claims) y lo usará en la solicitud de ser necesario.

Esto también reduce las consultas a bases de datos para leer información del usuario, ya que el token puede contener
información común para poder operar (como username, email, roles, etc).

Dado que el token va incluido en el header, no habrá problemas con el Cross-Origin Resource Sharing (CORS) ya que no
se utilizan cookies (las cookies son por dominio).

El siguiente diagrama muestra como se podría utilizar una autorización y autenticación por medio de JWT:

1. La aplicación cliente solicita autorización al Identity Server (como Auth0 o Azure AD B2C). Esto se puede hacer
por medio de distintos flujos de autorización definidos en el estándar OpenID Connect (pero no estamos
obligados a seguirlos). De igual forma, si seguimos OpenID, típicamente se utilizaría el
endpoint /oauth/authorize utilizando el flujo de code flow.
2. Cuando se autoriza el acceso, el servidor de autorización regresa el access token a la aplicación cliente
3. La aplicación cliente usa el access token para acceder a recursos protegidos (como una API)

¿Y el código? Probemos con ASP.NET y Minimal APIs

En este ejemplo utilizaremos herramientas production-ready y trataré de mantenerlo simple, sin embargo, cada quien
podrá decidir como estructurarlo e implementarlo.

Anteriormente mencionamos el estándar OpenId, que especifica como realizar estos flujos de autenticación, pero para
fines prácticos y didácticos, realizaremos nuestro propio servidor de autorización (será el mismo que la API protegida)
pero es muy recomendable delegar este proceso a servicios (como Auth0) o frameworks (como IdentityServer)
certificados para una mayor seguridad y compliance.

En este proyecto utilizaremos:

 Entity Framework Core con SQLite para persistencia (para fines del ejemplo, en producción deberías de usar un
servicio como SQL Azure o similares)
 ASP.NET Identity para el manejo de credenciales.
 Minimal APIs por su sencilles, pero podrán usar Controllers, Carter, ApiEndpoints o cualquier endpoint que
deseen.

Para comenzar, crearemos un proyecto Web vacío:


dotnet new web -o WebApiJwt

Y necesitamos los siguientes paquetes registrados en el WebApiJwt.csproj:

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

Persistencia

Crearemos una carpeta llamada “Persistence” y aquí pondremos las migraciones y el DbContext con tablas
preestablecidas por Identity:

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApiJwt.Entities;

namespace WebApiJwt.Persistence;

public class MyDbContext : IdentityDbContext<User>


{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
}

Para lo cual, necesitaremos nuestra definición custom de la clase Usuario:

using Microsoft.AspNetCore.Identity;

namespace WebApiJwt.Entities;

public class User : IdentityUser


{
public string FirstName { get; set; } = default!;
public string LastName { get; set; } = default!;
}

Aquí estamos usando un DbContext con tablas preestablecidas y IdentityUser es parte de ellas, solo lo estamos
extendiendo para agregar campos personalizados (nombre y apellidos).

Configuración de Identity y JWT

Para configurar Identity y EntityFramework, registramos las siguientes dependencias en nuestro archivo Program.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using WebApiJwt.Entities;
using WebApiJwt.Models;
using WebApiJwt.Persistence;

var builder = WebApplication.CreateBuilder(args);

builder.Services
.AddSqlite<MyDbContext>(builder.Configuration.GetConnectionString("Default"))
.AddIdentityCore<User>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<MyDbContext>();
 AddSqlite: Registra el DbContext, es un atajo del método habitual AddDbContext
 AddIdentityCore: Registra las dependencias que necesita Identity, como generador de contraseñas, manejo de
usuarios, etc
 AddRoles: Registra todo lo necesario para poder usar roles (en este caso, con la implementación default de la
clase IdentityRole)
 AddEntityFrameworkStores: Vincula nuestro contexto de EntityFramework con todas sus dependencias que
Identity necesita respecto a persistencia

Después de esto, agregamos la configuración que necesitamos para poder autenticar por medio de JWTs:

builder.Services
.AddHttpContextAccessor()
.AddAuthorization()
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
 AddHttpContextAccessor: Registra el IHttpContextAccessor que nos permite acceder el HttpContextde cada solicitud
(la usaremos más adelante para acceder al usuario actual autenticado)
 AddAutorization: Dependencias necesarias para autorizar solicitudes (como autorización por roles)
 AddAuthentication: Agrega el esquema de autenticación que queramos usar, en este caso, queremos usar por
default la autenticación por Bearer Tokens
 AddJwtBearer: Configura la autenticación por tokens, especificando que debe de validar y que llave privada utilizar
 Por supuesto, esta configuración la va a leer del appsettings.json
Quedando el archivo de configuración de la siguiente manera:
{
"ConnectionStrings": {
"Default": "Data Source=Identity.db"
},
"Jwt": {
"Issuer": "WebApiJwt.com",
"Audience": "localhost",
"Key": "S3cr3t_K3y!.123_S3cr3t_K3y!.123"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

En este punto, deberíamos de poder crear las migraciones de la base de datos (en este caso, SQLite) y actualizar el
esquema con todo lo predefinido por Identity:

dotnet ef migrations add FirstMigration -o Persistence/Migrations

Y contaríamos con algo similar a lo siguiente:

Para finalizar la configuración y antes de implementar la autenticación, debemos de usar dos middlewares que nos
ayudarán a decodificar automáticamente el JWT y agregarlo (en caso de ser válido) a la solicitud HTTP.

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/", () => "Hello World!");

app.Run();

Endpoints
Implementaremos dos endpoints, uno para autenticación y uno para simular un acceso restringido

Authorization endpoint (/token):

app.MapPost("/token", async (AuthenticateRequest request, UserManager<User> userManager) =>


{
// Verificamos credenciales con Identity
var user = await userManager.FindByNameAsync(request.UserName);
if (user is null || !await userManager.CheckPasswordAsync(user, request.Password))
{
return Results.Forbid();
}
var roles = await userManager.GetRolesAsync(user);

// Generamos un token según los claims


var claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, $"{user.FirstName} {user.LastName}")
};

foreach (var role in roles)


{
claims.Add(new Claim(ClaimTypes.Role, role));
}
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var tokenDescriptor = new JwtSecurityToken(
issuer: builder.Configuration["Jwt:Issuer"],
audience: builder.Configuration["Jwt:Audience"],
claims: claims,
expires: DateTime.Now.AddMinutes(720),
signingCredentials: credentials);

var jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);

return Results.Ok(new
{
AccessToken = jwt
});
});

El código de arriba se divide en dos partes:

 Verificación de credenciales: Utilizamos Identity de ASP.NET para guardar usuarios (tiene más funcionalidad
pero por ahora solo usaremos esta parte) y roles. UserManager cuenta ya con muchos métodos para manejar
usuarios, sus contraseñas y sus roles.
 Generación del JWT: Según el listado de claims que se generaron según el usuario autenticado, generamos el
JWT. Esto es un boilerplate, siempre será el mismo código. Lo importante es ver que estamos utilizando la
configuración del appsettings, los mismos que se utilizarán para verificar el JWT al hacer solicitudes.

Por parámetro se recibe el usuario y contraseña, este es el siguiente record:

namespace WebApiJwt.Models;
public record AuthenticateRequest(string UserName, string Password);
Protected endpoint (/me)

Este endpoint lo único que hará es regresar la información del usuario (claims) según el JWT que se mandó:

app.MapGet("/me", (IHttpContextAccessor contextAccessor) =>


{
var user = contextAccessor.HttpContext.User;

return Results.Ok(new
{
Claims = user.Claims.Select(s => new
{
s.Type,
s.Value
}).ToList(),
user.Identity.Name,
user.Identity.IsAuthenticated,
user.Identity.AuthenticationType
});
})
.RequireAuthorization();

Utilizamos IHttpContextAccessor para acceder al usuario decodificado automáticamente por el middleware y


simplemente regresamos esa información como prueba.

Usamos la extensión RequireAuthorization para indicar al endpoint que se necesita un esquema de autorización y como


no se específica lo contrario, utilizará el esquema default, que es Bearer Tokens.

Probando la solución

Para poder probar esto, necesitamos usuarios de prueba, para eso crearemos un método SeedData dentro
del Program.cs

async Task SeedData()


{
var scopeFactory = app!.Services.GetRequiredService<IServiceScopeFactory>();
using var scope = scopeFactory.CreateScope();

var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();


var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();

context.Database.EnsureCreated();

if (!userManager.Users.Any())
{
logger.LogInformation("Creando usuario de prueba");

var newUser = new User


{
Email = "test@demo.com",
FirstName = "Test",
LastName = "User",
UserName = "test.demo"
};
await userManager.CreateAsync(newUser, "P@ss.W0rd");
await roleManager.CreateAsync(new IdentityRole
{
Name = "Admin"
});
await roleManager.CreateAsync(new IdentityRole
{
Name = "AnotherRole"
});

await userManager.AddToRoleAsync(newUser, "Admin");


await userManager.AddToRoleAsync(newUser, "AnotherRole");
}
}

Aquí simplemente nos aseguramos que la base de datos exista y si previamente no hay usuarios, se crearán los roles y
un usuario de prueba utilizando las clases de Identity.

Los roles se pueden utilizar para autorizar endpoints según el rol del usuario. En este ejemplo solo muestro como
incluirlos en el JWT pero asp.net lo entenderá sin problema.

// ...Más código

var app = builder.Build();

await SeedData();

app.UseAuthentication();
app.UseAuthorization();

// Más código...

Corremos la aplicación y hacemos nuestras primeras pruebas utilizando HTTP Rest de VS Code (o puedes usar Postman o cualquier
cliente http que gustes):

Solicitud:

POST {{host}}/token
Content-Type: application/json

{
"userName": "test.demo",
"password": "P@ss.W0rd"
}

Respuesta:

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json; charset=utf-8
Date: Sun, 02 Jan 2022 22:32:30 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
"accessToken":
"eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8v
c2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiJhMWNhODMxZC1iMTIzLTQ0ZDgtYjViOC1i
NjNlYWZiYzZlNDciLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdC5k
ZW1vIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiVGVzdCBVc
2VyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjpbIkFub3RoZXJSb2xlIiwi
QWRtaW4iXSwiZXhwIjoxNjQxMjA1OTUwLCJpc3MiOiJXZWJBcGlKd3QuY29tIiwiYXVkIjoibG9jYWxob3N0In0.CtTkO7JVmFl6ASRv1v7Ou
ZhCrOHUy-AiMfNUzQbYByc"
}

Puedes hacer pruebas con usuarios o contraseñas incorrectas.

Para verificar el endpoint protegido llamamos el endpoint /me:

GET {{host}}/me
Content-Type: application/json
Authorization: Bearer {{jwt}}

Respuesta:

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json; charset=utf-8
Date: Sun, 02 Jan 2022 22:33:56 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
"claims": [
{
"type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid",
"value": "a1ca831d-b123-44d8-b5b8-b63eafbc6e47"
},
{
"type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"value": "test.demo"
},
{
"type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
"value": "Test User"
},
{
"type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"value": "AnotherRole"
},
{
"type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"value": "Admin"
},
{
"type": "exp",
"value": "1641205950"
},
{
"type": "iss",
"value": "WebApiJwt.com"
},
{
"type": "aud",
"value": "localhost"
}
],
"name": "test.demo",
"isAuthenticated": true,
"authenticationType": "AuthenticationTypes.Federation"
}

Puedes hacer pruebas modificando el token manualmente desde JWT.io o modificando cualquier dato y explora como se
comporta.

Conclusión

Los JSON Web Tokens se han convertido en el esquema default de autenticación de las aplicaciones modernas. Saber
como se forman y como implementarlas es un must have al diseñar una aplicación web hoy en día.

El uso de asp.net Identity es la forma recomendada de emplear este mecanismo (o cualquier mecanismo de
autenticación) ya que el manejo de seguridad y contraseñas a nivel código ya no sería de nuestra preocupación y
utilizamos un framework enterprise ready en lugar de reinventar la rueda.

You might also like