Implementing Windows Authentication in .NET Core Web API (short guide with examples)

Aug 30, 2023ยท

3 min read

Implementing Windows Authentication in .NET Core Web API (short guide with examples)

Use case:

We need to implement simple Windows authentication for an intranet ASP.NET CORE WEB API application which should authorize users by verifying user details in a custom database.

What is Windows Authentication?

Windows Authentication, also known as Integrated Windows Authentication, is a secure authentication mechanism used in Microsoft Windows environments. It enables users to access network resources, applications, and services by authenticating with their Windows credentials, such as username and password, without the need for additional login prompts.

Windows Authentication is highly secure and well-suited for corporate environments. This mechanism is commonly used in Windows-based web applications, intranet sites, and services that require robust identity verification within Windows domains.

Configuring Windows Authentication in the program.cs (startup.cs).

To configure our application to use authentication and authorization, include AddAuthentication and AddAuthorization as shown below. Next, add UseAuthentication and UseAuthorization enables the same.

var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
   .AddNegotiate();
builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = options.DefaultPolicy;
});
var app = builder.Build();
...
app.UseAuthentication();
app.UseAuthorization();

Enabling Windows Authentication in the launchSettings.json file.

To instruct the IIS server, we need to configure launchSettings.json by adding windowsAuthentication as true and anonymousAuthentication as false.

"iisSettings": {
  "windowsAuthentication": true,
  "anonymousAuthentication": false,
  "iisExpress": {
    "applicationUrl": "http://localhost:57281",
    "sslPort": 0
  }
}

Create Custom Authorize attribute

To authorize logged-in users, we need to check the user's details in the database and then allow/restrict access to them.

The bellow code will do the following things (refer OnAuthorization block)

  • Skip the authorization check if AllowAnonymous is enabled

  • Get the logged-in user details using context.HttpContext.User.Identity?.Name

  • Fetch the user's role details from the database

  • Authorize only

    • If Role is not passed but the user exists in the database

    • If Role is passed and the user exists in the database with the expected role

  • Return Unauthorized - Either the user does not exist or does not have an expected role in the database

 public class CustomRoleAuthorizationAttribute : AuthorizeAttribute, IAuthorizationFilter
 {
     public string[] Role { get; }

     public CustomRoleAuthorizationAttribute(string role)
     {
         Role = string.IsNullOrWhiteSpace(role) ? Array.Empty<string>() : role.Trim().Split(",");
     }

     public CustomRoleAuthorizationAttribute()
     {
         Role = Array.Empty<string>();
     }

     public void OnAuthorization(AuthorizationFilterContext context)
     {
         // skip authorization if action is decorated with [AllowAnonymous] attribute
         var allowAnonymous = context.ActionDescriptor.EndpointMetadata.OfType<AllowAnonymousAttribute>().Any();
         if (allowAnonymous)
             return;

         // authorization
         var user = context.HttpContext.User.Identity?.Name;
         //TODO - get user and role from database (userDb)
         var userFromDb = new UserWithRole() { User = "Raja", Role = "Admin" };

         var authorized =  userFromDb != null && !string.IsNullOrWhiteSpace(userFromDb.User) && (Role == Array.Empty<string>() || Role.Contains(userFromDb.Role));
         if (string.IsNullOrWhiteSpace(user) || !authorized)
         {
             context.Result = new JsonResult(new { message = $"Unauthorized user {user}" }) { StatusCode = StatusCodes.Status401Unauthorized };
         }

     }
 }

Securing your API routes with [Authorize] attributes

From the example below, if we mention [CustomRoleAuthorization("Admin")] then it will check if the logged-in user exists in the database with the "Admin" role.

If we mention [CustomRoleAuthorization], it will check that only the user exists in the database and allow the login.

 [CustomRoleAuthorization("Admin")]
 [HttpGet(Name = "GetWeatherForecast")]
 public IEnumerable<WeatherForecast> Get()
 {
     return Enumerable.Range(1, 5).Select(index => new WeatherForecast
     {
         Date = DateTime.Now.AddDays(index),
         TemperatureC = Random.Shared.Next(-20, 55),
         Summary = Summaries[Random.Shared.Next(Summaries.Length)]
     })
     .ToArray();
 }

Test the authorization

Run the application and call GetWeatherForecast API from the browser. It will check whether the logged-in user exists in the database with the "Admin" role.

This is a simple solution to authenticate users by integrating Windows authentication and authorization by verifying user details in a custom database