From 5b37dbf8547486b3659ecab4b11f32e3df12616a Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 13 Mar 2026 10:36:54 +0100 Subject: [PATCH] Add POST /auth/login for Windows credential auth Introduced a new endpoint to AuthController that allows authentication using Windows username, password, and optional domain via the Win32 LogonUser API. This enables credential validation without NTLM/Negotiate middleware or IIS. The endpoint parses both "DOMAIN\user" and "user@domain" formats and returns user info and claims on success, or Unauthorized on failure. Added necessary using directives for implementation. --- Controllers/AuthController.cs | 52 +++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/Controllers/AuthController.cs b/Controllers/AuthController.cs index a2fc353..1727589 100644 --- a/Controllers/AuthController.cs +++ b/Controllers/AuthController.cs @@ -1,5 +1,8 @@ -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using System.Security.Principal; +using FakeNTLMServer.Model; +using FakeNTLMServer.Common; namespace FakeNTLMServer.Controllers; @@ -23,7 +26,7 @@ public class AuthController : ControllerBase /// /// NTLM/Negotiate login endpoint. - /// Triggers the NTLM handshake (401 → challenge → response) and returns authenticated user info. + /// Triggers the NTLM handshake and returns authenticated user info. /// [Authorize] [HttpGet("login")] @@ -44,6 +47,51 @@ public class AuthController : ControllerBase }); } + /// + /// Validates Windows credentials (username/password) using the Win32 LogonUser API. + /// Works on local Kestrel without IIS or Negotiate middleware. + /// + [AllowAnonymous] + [HttpPost("login")] + public IActionResult LoginWithCredentials([FromBody] Login request) + { + var username = request.Username; + var domain = request.Domain ?? "."; + + if (username.Contains('\\')) + { + var parts = username.Split('\\', 2); + domain = parts[0]; + username = parts[1]; + } + else if (username.Contains('@')) + { + var parts = username.Split('@', 2); + username = parts[0]; + domain = parts[1]; + } + + if (!NtlmHelper.ValidateCredentials(username, domain, request.Password, out var token)) + { + return Unauthorized(new { Message = "Invalid username or password." }); + } + + using (token) + { + var windowsIdentity = new WindowsIdentity(token.DangerousGetHandle()); + var claims = windowsIdentity.Claims.Select(c => new { c.Type, c.Value }).ToList(); + + return Ok(new + { + Message = "Authentication successful.", + Name = windowsIdentity.Name, + AuthenticationType = windowsIdentity.AuthenticationType, + IsAuthenticated = windowsIdentity.IsAuthenticated, + Claims = claims + }); + } + } + [Authorize] [HttpGet("status")] public IActionResult Status()