I'm using .NET Core 3.0 Preview6.
We have an Intranet application with enabled Windows authentication which means that only valid AD users are allowed to use the application.
However, we like to run our own authentication backend with ASP.NET Identity, because it works "out-of-the-box". I just added a column to AspNetUsers table with the user's Windows login.
What I'd like to accomplish is that Windows users are automatically signed-in to the application with their Windows login.
I already created a custom Authentication middleware, please see code below:
public class AutoLoginMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public AutoLoginMiddleware(RequestDelegate next, ILogger<AutoLoginMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, UserService userService, UserManager<IntranetUser> userManager,
SignInManager<IntranetUser> signInManager)
{
if (signInManager.IsSignedIn(context.User))
{
_logger.LogInformation("User already signed in");
}
else
{
if (context.User.Identity as WindowsIdentity != null)
{
_logger.LogInformation($"User with Windows Login {context.User.Identity.Name} needs to sign in");
var windowsLogin = context.User.Identity.Name;
var user = await userManager.Users.FirstOrDefaultAsync(u => u.NormalizedWindowsLogin == windowsLogin.ToUpperInvariant());
if (user != null)
{
await signInManager.SignInAsync(user, true, "automatic");
_logger.LogInformation($"User with id {user.Id}, name {user.UserName} successfully signed in");
// Workaround
context.Items["IntranetUser"] = user;
}
else
{
_logger.LogInformation($"User cannot be found in identity store.");
throw new System.InvalidOperationException($"user not found.");
}
}
}
// Pass the request to the next middleware
await _next(context);
}
}
The doc says that SignInManager.SignInAsync
creates a new ClaimsIdentity
- but it seems that never happens - HttpContext.User
always stays a WindowsIdentity
. On every request the user is signed in again, the call to signInManager.IsSignedIn()
always returns false.
My question now: is it generally a good idea to have automatic authentication in this way? What other ways do exists?
My next requirement is to have a custom AuthorizationHandler
.
The problem here is that sometimes in the HandleRequirementAsync
method the AuthorizationHandlerContext.User.Identity
is a WindowsIdentity
and then the call to context.User.Identity.Name
raises the following Exception:
System.ObjectDisposedException: Safe handle has been closed.
Object name: 'SafeHandle'.
at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
at Interop.Advapi32.GetTokenInformation(SafeAccessTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, UInt32& ReturnLength)
at System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeAccessTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, Boolean nullOnInvalidParam)
at System.Security.Principal.WindowsIdentity.get_User()
at System.Security.Principal.WindowsIdentity.<GetName>b__51_0()
at System.Security.Principal.WindowsIdentity.<>c__DisplayClass67_0.<RunImpersonatedInternal>b__0(Object <p0>)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
My assumption now is that these both parts don't work well together. Sometimes it seems there is a timing issue - my custom AuthorizationHandler
is called in between the call to AutoLoginMiddleware
The ASP.NET Development Web Server also supports NTLM authentication. You can enable NTLM authentication by right-clicking the name of your project in the Solution Explorer window and selecting Properties.
Authentication. Negotiate NuGet package can be used with Kestrel to support Windows Authentication using Negotiate and Kerberos on Windows, Linux, and macOS. Credentials can be persisted across requests on a connection.
On the taskbar, click Start, and then click Control Panel. In Control Panel, click Programs and Features, and then click Turn Windows Features on or off. Expand Internet Information Services, then World Wide Web Services, then Security. Select Windows Authentication, and then click OK.
This is fixed now. It's been a bug in the preview releases. Now it's working like intended. Good luck!
Update: I'd like to post my working code for .NET Core 3.1 Final.
Configure
: app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<AutoLoginMiddleware>();
In the custom middleware, after signing in the user you must call CreateUserPrincipalAsync
and save this principal to the HttpContext.User
property.
await signInManager.SignInAsync(user, true);
context.User = await signInManager.CreateUserPrincipalAsync(user);
For Blazor, we must use AuthenticationStateProvider
. It has a property User
which contains the ClaimsPrincipal
from HttpContext
. That's it.
You are now able to get the Identity user like follows:
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
var intranetUser = await UserManager.GetUserAsync(authState.User);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With