-1

I am trying to set up authentication and authorization in my ASP.NET Core 3.0 Web Application. Users must be able to authenticate using their user principal name and password. I need to then retrieve their group memberships in order to determine their roles.

I have found some articles in various sources, suggesting the use of the Microsoft.Windows.Compatibility NuGet package. However, I am missing how to 'glue' all this together.

My main point of reference is this SO question: ASP.NET Core 2.0 LDAP Active Directory Authentication

My three main questions are:

  • Where does the authentication code sit in the code structure? Separate namespace, class, etc?
  • How do I configure this in my ConfigureServices method?
  • How does this all fit into the ASP authentication/authorization structure?

I was expecting to see some way of extending the .AddAuthentication() method to cover then authentication, but I cannot work out how.

Could anyone please point out what I am missing?

Thanks

Trolley01
  • 19
  • 1
  • 5
  • Any reason you're not using [Windows Authentication](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0&tabs=visual-studio) (which will do automatic login for intranet users - no typing in credentials) – Gabriel Luci Oct 21 '19 at 13:19
  • 1
    Yes, there is no guarantee that the client is using Windows, and there is a need to not rely on a popup but have forms based authentication. – Trolley01 Oct 21 '19 at 13:46
  • Fair enough. Unfortunately I can't help much since I haven't done this in ASP.NET Core yet. (I've done it in old-school ASP.NET) – Gabriel Luci Oct 21 '19 at 16:43

2 Answers2

1

For AD Authenticaiton, you could try Novell.Directory.Ldap.NETStandard.

For using AD Authentication with Asp.Net Core, you could combine CookieAuthentication and Novell.Directory.Ldap.NETStandard.

You could follow steps below:

  1. install package Novell.Directory.Ldap.NETStandard with Version="3.0.0-beta5"
  2. IAuthenticationServiceand LdapAuthenticationService

    public class LdapAuthenticationService : IAuthenticationService
    {
        public bool ValidateUser(string domainName, string username, string password)
        {
            string userDn = $"{username}@{domainName}";
            try
            {
                using (var connection = new LdapConnection { SecureSocketLayer = false })
                {
                    connection.Connect(domainName, LdapConnection.DefaultPort);
                    connection.Bind(userDn, password);
    
                    if (connection.Bound)
                        return true;
                }
            }
            catch (LdapException ex)
            {
                // Log exception
            }
            return false;
        }        
    }
    public interface IAuthenticationService
    {
        bool ValidateUser(string domainName, string username, string password);
    }
    
  3. Configure in Startup.cs

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddScoped<IAuthenticationService, LdapAuthenticationService>();
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                    .AddCookie();
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/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.UseAuthentication();
            app.UseAuthorization();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    
  4. AccountController

    public class AccountController : Controller
    {
        private readonly IAuthenticationService _authenticationService;
    
        public AccountController(IAuthenticationService authenticationService)
        {
            _authenticationService = authenticationService;
        }
        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        public async Task<IActionResult> Login(LoginModel model)
        {
            var result = _authenticationService.ValidateUser("xx.com",model.UserName, model.Password);
            if (result)
            {
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, model.UserName),
                    new Claim(ClaimTypes.Role, "Administrator"),
                };
    
                var claimsIdentity = new ClaimsIdentity(
                    claims, CookieAuthenticationDefaults.AuthenticationScheme);
    
                var authProperties = new AuthenticationProperties
                {
                    //AllowRefresh = <bool>,
                    // Refreshing the authentication session should be allowed.
    
                    //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
                    // The time at which the authentication ticket expires. A 
                    // value set here overrides the ExpireTimeSpan option of 
                    // CookieAuthenticationOptions set with AddCookie.
    
                    //IsPersistent = true,
                    // Whether the authentication session is persisted across 
                    // multiple requests. When used with cookies, controls
                    // whether the cookie's lifetime is absolute (matching the
                    // lifetime of the authentication ticket) or session-based.
    
                    //IssuedUtc = <DateTimeOffset>,
                    // The time at which the authentication ticket was issued.
    
                    //RedirectUri = <string>
                    // The full path or absolute URI to be used as an http 
                    // redirect response value.
                };
                await HttpContext.SignInAsync(
                        CookieAuthenticationDefaults.AuthenticationScheme,
                        new ClaimsPrincipal(claimsIdentity),
                        authProperties);
            }
            return Ok();
        }
        public IActionResult Index()
        {
            var user = HttpContext.User.Identity.Name;
            return View();
        }
    }
    public class LoginModel
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }
    
Edward
  • 28,296
  • 11
  • 76
  • 121
  • I'm rather new to .Net. So while I can follow your examples, I'm not quite sure where the code from step #2 goes. It doesn't seem to be a part of the Controller no the Startup. Appreciate your help and guidance. – TheSeeker Nov 22 '19 at 22:25
1

Tao option is working. I also found another option, here https://www.brechtbaekelandt.net/blog/post/authenticating-against-active-directory-with-aspnet-core-2-and-managing-users with code https://github.com/brechtb86/dotnet/tree/master/brechtbaekelandt.ldap. This however was for Asp.Net Core 2.0.

I have updated this to run on Asp.Net Core 3.0, and published my code on GitHub https://github.com/CraigTolley/AspNetCore-LdapAuth. It currently also uses the Novell library. I'm not promising that it is perfect, but will hopefully help someone else too.

The code example shows functioning LDAP authentication, which then extracts the group memberships for the authenticated user to construct a set of roles and claims which can be used for authentication.

Trolley01
  • 19
  • 1
  • 5