Web API Authentication .NET 5

In the last article we implemented the basic operations for our .NET 5 Web API. If you have missed the last article, you can read it here. In this article we are going to implement the authentication to secure our API. So we are adding JWT token based authentication to the API. Token-based authentications are very popular methods to secure web APIs and the JWT approach is one of the widely used methods for Web API Authentication. 

JWT (JSON Web Token)  is an open standard method for representing claims securely between two parties. In .NET 5 Web APIs, JWT support is available by default. The package required for the implementation is Microsoft.AspNetCore.Authentication.JwtTokenBearer

jwt nuget Web API Authentication

Implementation

As we tested last time, our web API is open to anyone, So lets start restricting the access. Go to Movies controller and add [Authorize] attribute at the top. So all the methods in the controller should be authenticated. 

Authorize - Web API Authentication

Now run the project and try to access any of our methods. Great! we cant access anymore. Oh wait! then how can we access our methods? Yes, we have to authenticate our requests to access the methods. But before doing that we need to add something to our swagger page. We must tell the clients we are secured and authentication required.

We need to add some codes in Startup.cs file to indicate that we have authenticated access. Add below codes inside services.AddSwaggerGen method.

 c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
 Description =  "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter    'Bearer' [space] and then your token in the text input below.\r\n\r\nExample:      "Bearer abcdef1234\"",
  Name = "Authorization",
  In = ParameterLocation.Header,
  Type = SecuritySchemeType.ApiKey,
  Scheme = "Bearer"
});

c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
 {
  new OpenApiSecurityScheme
  {
   Reference = new OpenApiReference
   {
    Type = ReferenceType.SecurityScheme,
    Id = "Bearer"
   },
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header, },


Add swagger authentication

Once we run the project, We can see the icon for authentication and when click on it we can see the text box to enter JWT token for authorization. For now we don’t have the JWT token.

jwt token required

Maintain Users

Lets implement our Web API Authentication part internally. So we need to maintain our valid users in our database. First add the User class inside Models folder.

public class User
{
 [Key]
 public int Id { get; set; }
 [Required]
 public string UserName { get; set; }
 [Required]
 public string Password { get; set; }
 public string Role { get; set; }
}

Then register user class in DB Context same as we did for Movie class.

public DbSet<User> User { get; set; }

We need to modify our database to handle users. So Add a migration and update the DB. We can use Add-Migration addUserTable and Update-Database commands for this. It will update the database.

Next step is to add service to handle user login and issuing JWT tokens for the valid users. If the user credentials are correct, then we authenticate the user and return a token in the response. So the client can use that token for the next API calls.

Add a new interface and a class for UserService.

public interface IUserService
{
  string Login(string userName, string password);
}

public class UserService : IUserService
{
public IConfiguration Configuration { get; }
private readonly MovieAPIContext dbContext;
public UserService(IConfiguration configuration, MovieAPIContext _dbContext)
{
Configuration = configuration;
dbContext = _dbContext;
}
public string Login(string userName, string password)
{
var user = dbContext.User.SingleOrDefault(x => x.UserName == userName && x.Password == password);
// return null if user not found
if (user == null)
{
return string.Empty;
}
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(Configuration["SECRETKEY"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, user.Role)
}),
Expires = DateTime.UtcNow.AddMinutes(5),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);

return tokenHandler.WriteToken(token);
}
}

Now we need to register the service in Startup.cs like below.

services.AddScoped<IUserService, UserService>();

Declare Authentication 

Another thing is we need to add our Secret Key in appsettings.json file. This key will be using in the user service to generate the JWT token. So I added the Secret key which is in base64 encoded and minimum 32 characters in length. 

secretkey

Add below codes to register the authentication in Startup class ConfigureService method. This will read the secret key from appsettings and use in authentication for signing the token.

var key = Encoding.ASCII.GetBytes(Configuration["SECRETKEY"]);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = true;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});

OK now we have the user service to authenticate users, but we need to expose the login method as an API method for clients. For this we need a UsersController.

[Route("api/[controller]/[action]")]
[ApiController]
public class UsersController : ControllerBase
{
  private IUserService _userService;
  public UsersController(IUserService userService)
{
  _userService = userService;
}
// POST api/user/login
[AllowAnonymous]
[HttpPost]
public IActionResult Login([FromBody] User user)
{
  var token = _userService.Login(user.UserName, user.Password);
  if (token == null || token == String.Empty)
  return BadRequest(new { message = "User name or password is incorrect" });
  return Ok(token);
}

As next step, we must specify our API to use Authentication. So add below line in Startup.cs inside the Configure method.

app.UseAuthentication();

We must not add [Authorize] attribute for this controller since clients have to access this method anonymously before the authorization happens. Now this method also will be available in swagger page. So to allow access, we need some users in our database. Just add some test users with passwords from database and run the project. 

Testing the Web API Authentication

We can execute the Login method in swagger page with our test user.

swagger json for user

Once we execute, our method will return the JWT token in response body.

jwt token

Now copy the token and click on Authorize icon on top right. Then it will open up the dialog to enter token. Then enter the token like below

Bearer [your copied JWT token]

Once authorize, we can access the methods again without a problem. 

success response

This is only a one way to authorize but there are some other ways. For me I prefer this method using JWT token because this is very straight forward and simple.

 

 

 

 

1 thought on “Web API Authentication .NET 5

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.