asp.net-core.net-corerazor-pagesasp.net-core-7.0

I have an error in my code for IAuthenticationHandler - how to fix this?


I created a .NET Core 7 project on Windows 10 with Razor pages, but I get this error:

Error CS0311
The type 'WebAppSecurity.Authorization.HRManagerProbationRequirmentHandle' cannot be used as type parameter 'TImplementation' in the generic type or method 'ServiceCollectionServiceExtensions.AddSingleton<TService, TImplementation>(IServiceCollection)'. There is no implicit reference conversion from 'WebAppSecurity.Authorization.HRManagerProbationRequirmentHandle' to 'Microsoft.AspNetCore.Authentication.IAuthenticationHandler'. WebAppSecurity F:\WebProj\WebAppSecurity\WebAppSecurity\Program.cs

program.cs:

using Microsoft.AspNetCore.Authentication;
using WebAppSecurity.Authorization;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddAuthentication().AddCookie("MyCookieAuth", options => 
{
    options.Cookie.Name = "MyCookieAuth";
    options.LoginPath = "/Account/Login";
    
});
builder.Services.AddAuthorization(options => 
{
    options.AddPolicy("MustBelongToHRDeparteman",policy => policy.RequireClaim("Department","HR"));
    options.AddPolicy("AdminOnly",policy => policy.RequireClaim("Admin"));
    options.AddPolicy("HRAdmin", policy => policy.RequireClaim("Department", "HR").RequireClaim("Manager").Requirements.Add(new HRManagerProbationRequirment(3)));
});

builder.Services.AddSingleton<IAuthenticationHandler, HRManagerProbationRequirmentHandle>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

HRManager:

using Microsoft.AspNetCore.Authorization;

namespace WebAppSecurity.Authorization
{
    public class HRManagerProbationRequirment : IAuthorizationRequirement
    {
        public HRManagerProbationRequirment(int probationMonth)
        {
            ProbationMonth = probationMonth;
        }

        public int ProbationMonth { get; }
    }

    public class HRManagerProbationRequirmentHandle : AuthorizationHandler<HRManagerProbationRequirment>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HRManagerProbationRequirment requirement)
        {
            if (!context.User.HasClaim(x => x.Type == "EmploymentDate"))
            {
                return Task.CompletedTask;
            };

            if (DateTime.TryParse(context.User.FindFirst(x => x.Type == "EmploymentDate")?.Value, out DateTime employmentDate)) 
            {
                var period = DateTime.Now - employmentDate;

                if (period.Days > 30 * requirement.ProbationMonth)
                {
                    context.Succeed(requirement);
                }
            };

            return Task.CompletedTask;
        }
    }
}

Login :

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;

namespace WebAppSecurity.Pages.Account
{
    public class LoginModel : PageModel
    {
        [BindProperty]
        public Credential Credential { get; set; } = new Credential();

        public void OnGet()
        {
        }

        public async Task<ActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            if (Credential.UserName == "admin" && Credential.Password == "admin") 
            {
                var claims = new List<Claim> 
                {
                    new Claim(ClaimTypes.Name,"admin"),
                    new Claim(ClaimTypes.Email,"admin@gmail.com"),
                    new Claim("Department" ,"HR"),
                    new Claim("Admin","true"),
                    new Claim("Manager","true"),
                    new Claim("EmploymentDate","2023-03-03"),
                };

                var identity = new ClaimsIdentity(claims, "MyCookieAuth");

                ClaimsPrincipal principal = new ClaimsPrincipal(identity);

                await HttpContext.SignInAsync("MyCookieAuth", principal);

                return RedirectToPage("/Index");
            }

            return Page();
        }
    }

    public class Credential
    {
        [Required]
        [Display(Description ="User Name")]
        public string UserName { get; set; } = string.Empty;
        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; } = string.Empty;
    }
}

Solution

  • Try to use IAuthorizationHandler:

    Classes implementing this interface are able to make a decision if authorization is allowed.

    Change below :

    builder.Services.AddSingleton<IAuthenticationHandler, HRManagerProbationRequirmentHandle>();
    

    into:

     builder.Services.AddSingleton<IAuthorizationHandler, HRManagerProbationRequirmentHandle>();