2

Authentication And Authorization In .NET 5: How It Works

 3 years ago
source link: https://hackernoon.com/authentication-and-authorization-in-net-5-how-it-works-nun33v4
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Authentication And Authorization In .NET 5: How It Works

@arjavdaveArjav Dave

Technical Director & Co-Founder - @Royale Cheese

In my 11 years as a developer, I have seen so many API's that have major security flaws. They either lack proper authentication or authorisation or both.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Developers might feel like everything's ok, since those endpoints are usually not public. But it is a huge security loop hole which anyone can easily target.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

To better understand API security, let's create a demo project for the FBI. There will be an Admin who can enroll FBI Agents and change their clearance levels.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

FBI Agents with Clearance Level 1 will be able to access public files, and agents with Clearance Level 2 will be able to access pubic and classified files.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

But before we get started, here's some theory.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How Authentication Works

Our Agent has successfully cleared all their exams; time to enroll them. In order to do that they will provide their documents and in return will get their badge.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

In the above scenario, providing documents is like logging in – where once verified, the agent will be provided with a token (badge). This process is called Authentication. It determines whether agents are who they claim to be.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

We are going to use JSON Web Tokens (JWT) Bearer tokens for authentication. Bearer tokens are a type of token that's generated by servers, and which contain details of the claims/roles of a user trying to login. Bearer tokens are mostly structured tokens like JWT. You can read more about JWT here if you want to learn more.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How Authorisation Works

Now since the FBI Agent has gotten their badge, they can enter the FBI building. They are also able to access public files, but when trying to access classified files they get a 401 error.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

This is because FBI Agent is not authorised to access classified files. Authorisation determines what agents can and cannot access.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

As mentioned above, the JWT Bearer token contains claims/roles. Based on it, our server decides whether to give access to a private resource or not.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Access Flow

As you can see in the above diagram, on successful login the server returns a Bearer token. The client uses the bearer token in subsequent calls to access a private resource.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

These are the two main concepts that we are going to implement in our article.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Enough with the theory, show me some code!

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How to Set Up our Project

Create a new project by executing the command dotnet new webapi --name FBI from your CLI. It will create a project with a sample WeatherForecast API.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Why work on WeatherForecast when we can work on FBI? Go ahead and delete the WeatherForecast.cs file.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Add the necessary dependencies by executing these commands:

0 reactions
heart.png
light.png
money.png
thumbs-down.png
dotnet add package Microsoft.IdentityModel.Tokens --version 6.9.0
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 5.0.4

In the ConfigureServices function in your Startup.cs file, add the below code:

0 reactions
heart.png
light.png
money.png
thumbs-down.png
var TokenValidationParameters = new TokenValidationParameters
{
    ValidIssuer = "https://fbi-demo.com",
    ValidAudience = "https://fbi-demo.com",
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ")),
    ClockSkew = TimeSpan.Zero // remove delay of token when expire
};

We are defining the parameters for validating a token. Make sure that the length of the string for generating SymmetricSecurityKey is 32.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Next, setup the services to add authentication for API's like this:

0 reactions
heart.png
light.png
money.png
thumbs-down.png
services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(cfg =>
    {
        cfg.TokenValidationParameters = TokenValidationParameters;
    });

The AddAuthentication method registers services required by authentication services. It also configures the JWT Bearer Authentication as the default scheme.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

The AddJwtBearer enables JWT-bearer authentication and setting the TokenValidationParameters defined above.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Now let's add some Authorisation claims for our Agent and Admin.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
services.AddAuthorization(cfg =>
    {
        cfg.AddPolicy("Admin", policy => policy.RequireClaim("type", "Admin"));
        cfg.AddPolicy("Agent", policy => policy.RequireClaim("type", "Agent"));
        cfg.AddPolicy("ClearanceLevel1", policy => policy.RequireClaim("ClearanceLevel", "1", "2"));
        cfg.AddPolicy("ClearanceLevel2", policy => policy.RequireClaim("ClearanceLevel", "2"));
    });

The AddAuthorization method registers services required for authorisation. We are also adding claims for Admin, Agent, ClearanceLevel1 and ClearanceLevel2 by calling AddPolicy.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

A claim is a name value pair that represents what the subject is. Since clearance level 2 can also access clearance level 1, we have put "1", "2" in ClearanceLevel1. You can read more about claims here.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Lastly, in the Configure method, add the below line just above app.UseAuthorization();

0 reactions
heart.png
light.png
money.png
thumbs-down.png
app.UseAuthentication();

The Admin Controller File

Rename your file WeatherForecastController.cs to AdminController.cs. Do change the class name and constructor names as well. Finally, remove everything except the constructor.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
using Microsoft.AspNetCore.Mvc;

namespace FBI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class AdminController : ControllerBase
    {
        public AdminController() { }
    }
}

Let's create a login API for Admin so that they can get a token to perform other tasks.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
[HttpPost]
[Route("[action]")]
public IActionResult Login([FromBody] User User)
{
    // TODO: Authenticate Admin with Database
    // If not authenticate return 401 Unauthorized
    // Else continue with below flow

    var Claims = new List<Claim>
            {
                new Claim("type", "Admin"),
            };

    var Key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ"));

    var Token = new JwtSecurityToken(
        "https://fbi-demo.com",
        "https://fbi-demo.com",
        Claims,
        expires: DateTime.Now.AddDays(30.0),
        signingCredentials: new SigningCredentials(Key, SecurityAlgorithms.HmacSha256)
    );

    return new OkObjectResult(new JwtSecurityTokenHandler().WriteToken(Token));
}

In the above code, User is a model with properties Username and Password. We are also creating an object of JwtSecurityToken using configurations that we have used in our Startup.cs file. The token is then converted to a string and returned in an OkObjectResult.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

You can now open Swagger and execute the API to see a bearer token. A bearer token will be returned as you can see below.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Keep the token handy since we are going to use it in the next section. You can also visit https://jwt.io to analyse your token.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How to Generate the Badge API

Generating the badge for an Agent is a sensitive task and should only be authorised by an Admin. We are going to add an Authorize attribute for the GenerateBadge API.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
[HttpPost]
[Route("[action]")]
[Authorize(Policy = "Admin")]
public IActionResult GenerateBadge([FromBody] Agent Agent)
{
    // TODO: Add the agent to the database
    return new CreatedResult("Agent/1", Agent);
}

Here, Agent is a model with properties Name as string and ClearanceLevel as int.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Now when you go back to swagger and try to execute the GenerateBadge API it will give you a 401 Unauthorised response. We are getting this error because we have not passed the bearer token.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

To be able to add the Authorize header in Swagger, change the services.AddSwaggerGen as below:

0 reactions
heart.png
light.png
money.png
thumbs-down.png
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "FBI", Version = "v1" });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Description = "Please enter JWT with Bearer into field",
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement {
    { new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer"}
            },
        new string[] {}
    }
    });
});

When you refresh Swagger in your browser you will notice an Authorize button on the right side above the list of APIs.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Click on the newly added Authorize button in Swagger which will open up a dialog. We need to mention what type of token it is. So first enter Bearer in the field then a space and then the token generated from the /Admin/Login API from the previous section.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Click on the header to lock in the token. Now you are all set. When you execute the GenerateBadge API again you will get a token (analogous to badge). Keep this token handy, since we require it in the next section. Also make sure to pass ClearanceLevel as 1 for now.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How to Set Up the Agent Controller

Create a new file called AgentController.cs with the below content:

0 reactions
heart.png
light.png
money.png
thumbs-down.png
using Microsoft.AspNetCore.Mvc;

namespace FBI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    [Authorize(Policy = "Agent")]
    public class AgentController : ControllerBase
    {
        public AgentController() { }
    }
}

As you can see above, we are authorising the whole controller for Agent's access only. So even Admin won't be able to access the API's we are going to create.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How to Access the Record's APIs

Let's add the APIs to access both public and classified files.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
[HttpGet]
[Route("[action]")]
[Authorize(Policy = "ClearanceLevel1")]
public ActionResult<String> AccessPublicFiles()
{
    return new OkObjectResult("Public Files Accessed");
}

[HttpGet]
[Route("[action]")]
[Authorize(Policy = "ClearanceLevel2")]
public ActionResult<String> AccessClassifiedFiles()
{
    return new OkObjectResult("Classified Files Accessed");
}

We have added the Authorize attribute's for both API's such that public files can be accessed by ClearanceLevel1 and classified files can be accessed by ClearanceLevel2.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

If you try to access these API's with the Admin token you will get 403 Forbidden error. So go ahead and click on the Authorize button again and click on logout. Then, get the token from the above step and paste in the field with Bearer as a prefix.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Now when you access the /Agent/AccessPublicFiles API you will see response 200 with the message Public Files Accessed. But when you try the classified API you get the 403 Forbidden error.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How to Change the Clearance Level

Fast forward 3 years and our Agent's performance has been mind bogglingly good. Management has now decided to promote them to ClearanceLevel2.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

The Agent goes to the Admin and asks them to provide a token/badge with Clearance Level 2.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

The Admin calls the /Admin/Login API to generate their own token first. They then enter it in the Authorize dialog.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

The admin then calls the /Admin/GenerageBadge API with value 2 in the ClearanceLevel. This generates a new token/badge which they then hand over to Agent.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

The Agent enters this token/badge in the Authorize dialog and when they now call /Agent/AccessClassifiedFiles they are pleased to see the result Classified Files Accessed.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Conclusion

You can find the whole project here on GitHub.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

API security is extremely important and shouldn't be taken lightly, even if it's for internal use only. Setup Authentication and Authorisation and you are halfway there.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

There are other other security measures you can take against DDoS attacks, accepting API's from a particular IP or domain only, and so on.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

How did you like the article? What other security measures do you usually take? Any feedbacks or comments?

0 reactions
heart.png
light.png
money.png
thumbs-down.png

You can checkout out more tutorials here.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Previously published at https://arjavdave.com/2021/03/31/net-5-setup-authentication-and-authorisation/

0 reactions
heart.png
light.png
money.png
thumbs-down.png
8
heart.pngheart.pngheart.pngheart.png
light.pnglight.pnglight.pnglight.png
boat.pngboat.pngboat.pngboat.png
money.pngmoney.pngmoney.pngmoney.png
by Arjav Dave @arjavdave. Technical Director & Co-Founder - @Royale CheeseExplore. Learn. Connect.
Join Hacker Noon

Create your free account to unlock your custom reading experience.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK