Getting started with JWT authorization - .NET Core edition
Tags: netcore, jwt, authorization, api
Published: 2020-09-11 07:57:04

This blog post will teach you how to issue JSON Web Tokens (JWT) from a .NET Core 3.1 Web API – the guide should also be somewhat applicable to .NET Core 2.2. 

JWTs makes it possible to securely transmit data between parties – such as a client and a server. A JWT consists of three things:

1. A header that usually defines the signing algorithm and the token type,
2. A payload containing claims such as expiration time and custom data such as user UID and user type,
3. A signature consisting of Base 64 Url encoded header and Base 64 Url encoded payload and a secret key.

A JWT will look something like this: 

`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`

Which basically translates to the following:

`base64urlEncoding(header) + '.' + base64urlEncoding(payload) + '.' + base64urlEncoding(HMAC-SHA256(secret,base64urlEncoding(header) + '.' +base64urlEncoding(payload)))`

This JWT is taken from https://jwt.io/, where you can decode, verify and generate tokens.

For this guide I assume you have already created a basic .NET Core Web API. If you haven't, then use `dotnet new webapi` in an empty folder.

The code used in the example can be found [on GitHub](https://github.com/Drsela/JSON-Web-Token-NET-Core-Example)

# Installing required NuGet Packages

For this demo you have to add the following NuGet packages to your solution:
* Microsoft.AspNetCore.Authentication.JwtBearer
* Microsoft.EntityFrameworkCore.SqlServer

# Basic setup of models and Authorize Attribute

I’ll be using EF Core to save information to a database for this demonstration. The relevant information regarding the User model can be seen here:

```csharp
    public class User
    {
        [Key]
        public Guid Id { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public UserType Type { get; set; }
    }

    public enum UserType
    {
        Regular = 1,
        Admin = 2,
        SuperUser = 3
    }
```

In this instance a user can have one of three types. These types will be used to restrict some endpoints - and therefore implement authorization. 

To implement these roles in your controllers, you must create a custom **AuthorizeAttribute** as such:

```csharp
    public class AuthorizeRolesAttribute : AuthorizeAttribute
    {
        public AuthorizeRolesAttribute(params UserType[] roles) : base()
        {
            Roles = string.Join(",", roles);
        }
    }
```

Doing this allows you to restrict endpoint to specific user types, simply by using a **AuthorizeRoles**-tag such as `[AuthorizeRoles(UserType.Regular)]`

# Using EF Core
To use EF Core add the following to **appsettings.development.json**. 

```json
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Integrated Security=true;Database=JwtExample"
  }
```
This defines which database to connect to. Afterwards, implement a DbContext class as such:

```csharp
    public class JwtExampleContext : DbContext
    {
        public DbSet<User> Users { get; set; }

        public JwtExampleContext(DbContextOptions options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<User>()
                .HasIndex(x => x.UserName)
                .IsUnique();

            var regularUser = new User { Id = Guid.Parse("54D39F1A-EF2D-4816-B2E8-90991F548BF0"), UserName = "Regular", Password = "RegularPassword", Type = UserType.Regular };
            var adminUser = new User { Id = Guid.Parse("36F72591-1D95-4E4F-877C-261D3386DF94"), UserName = "Admin", Password = "AdminPassword", Type = UserType.Admin };
            var superUser = new User { Id = Guid.Parse("FB270C9A-9479-4BD0-9C86-589F3FA84527"), UserName = "Super", Password = "SuperPassword", Type = UserType.SuperUser };
            builder.Entity<User>().HasData(regularUser, adminUser, superUser);
        }
    }

```

To connect to the database write the following lines in **Startup.cs**.

```csharp
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<JwtExampleContext>(options => options.UseSqlServer(connectionString));

```
Running `dotnet ef migrations add Initial` will create a migration file. To create and seed the database use `dotnet ef database update` which will run the migration script. Please note, passwords should never be stored as plain text in a database. This is only for demonstration purposes.

To see how the context is being used, look at **UserRepository.cs**


# Token related setup
First, you must configure your `IServiceCollection` to add Authentication and JwtBearer. This can be done by adding the following lines to `ConfigureServices` in **Startup.cs** file in your .NET Core project:

```csharp
            var key = Configuration.GetValue<string>("jwt-signing-key");
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = "http://localhost:5000",
                    ValidAudience = "http://localhost:5000",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))
                };
            });
```
These settings might not necessarily fit your needs and should possibly be adjusted. The **key** comes from **appsettings.development.json**. This key is used to sign the token and should always be kept secret. 

You also must also call `UseAuthentication()`and `UseAuthorization () `in `Configure`. These methods should be called after `UseRouting()` but before `UseEndpoints`, which should look similar to this:

```csharp
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseRouting();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
```

# Issuing the tokens
I’ve made a LoginController with a single POST endpoint (api/login). This endpoint takes an UserDTO containing username and password. The LoginController calls an AuthenticationService which is responsible for generating tokens. The methods in the AuthenticationService can be seen below:

```csharp
        public async Task<string> Login(UserDTO user)
        {
            var dbUser = await _repository.GetUser(user.UserName, user.Password);
            var jwt = GenerateJwt(dbUser);
            return jwt;
        }

        private string GenerateJwt(User user)
        {
            var key = _configuration.GetValue<string>("jwt-signing-key");
            var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
            var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
            var tokenOptions = new JwtSecurityToken(
                issuer: "http://localhost:5000",
                audience: "http://localhost:5000",
                claims: new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier,user.Id.ToString()),
                    new Claim(ClaimTypes.Role,user.Type.ToString()),
                },
                expires: DateTime.Now.AddHours(24),
                signingCredentials: signinCredentials
            );

            return new JwtSecurityTokenHandler().WriteToken(tokenOptions);
        }

```
It’s very important that the key, issuer and audience matches the options in **Startup.cs**. Otherwise your backend will return forbidden for all requests requiring authorization, since it’s validating the signature, issuer and audience.

Calling the endpoint will return something similar to this:

`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjM2ZjcyNTkxLTFkOTUtNGU0Zi04NzdjLTI2MWQzMzg2ZGY5NCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluIiwiZXhwIjoxNTk4MTk2NzY2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAifQ.x1vksHo5O-Fd8IXCUvEXfxcjToJXc7V7jMS7jqp0h5A` 

And that’s it! You now know how to issue tokens. However, you might ask: how do I use them?

# Using the token
It’s actually fairly simple! First you make a POST call to api/login with your login credentials. This’ll return a token as seen in the previous section. This token needs to be set as a HTTP Header as ‘Authorization: Bearer `token`’. This header should be present for all calls to the API. 
Afterwards, you simply create a new endpoint in a controller. I’ve created a new controller named DataController for this purpose. This controller has four endpoints:

1. GET api/data - Doesn’t require authorization
2. GET api/data/user - Available to all user types
3. GET api/data/admin - Available to admins and superusers
4. GET api/data/superuser - Available to superusers

All endpoints will return a small greeting and the usertype of the user. The code snippet below shows the implementation for api/data/admin.

```csharp
        [AuthorizeRoles(UserType.Admin, UserType.SuperUser)]
        [HttpGet]
        [Route("admin")]
        public IActionResult GetAdminData()
        {
            try
            {
                var role = User.Claims.First(x => x.Type == ClaimTypes.Role).Value;
                var text = $"Hello from DataController. Only users above admin can access this endpoint. Your role is {role}.";
                return Ok(text);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

```

Calling an endpoint that requires authorization without a valid token will result in HTTP 401 - also known as unauthorized. Calling an endpoint that doesn't match your authorization level will result in HTTP 403 - also known as forbidden. 

And that's it! You know now how to implement authorization with JWTs.

# But what now?
If you’re interested in the full code for this example, feel free to check out the repository [here](https://github.com/Drsela/JSON-Web-Token-NET-Core-Example).

You might need to restore NuGet packages before you can build the project.

If you’re further interested in how JWT works, I highly recommend visiting https://jwt.io/. 

Thanks for reading this blog post. I hoped you enjoyed it – feel free to ask any questions.