PolicyServer.Local Error In PolicyServerClaimsMiddleware When Using Windows Authentication - asp.net-core

I've set up a Asp.Net Core 2.2 MVC web application with Windows Authentication, and added the PolicyServer.Local code to assist in adding Authorization to the web app.
Whenever it performs an authorization check on a controller action, I get an error...Anyone seen this type of thing before?
[Authorize(Roles = "MailAdministrator")]
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
Win32Exception: The trust relationship between the primary domain and the
trusted domain failed
System.Security.Principal.NTAccount.TranslateToSids(IdentityReferenceCollection sourceAccounts, out bool someFailed)

Related

.Net 6: Enable Windows and Anonymous authentication for one

I work on a .Net core application and I need to mix windows and anonymous authentication within the same endpoint(s). So the goal is to be able to determine the windows user but the endpoint should also work when no windows user is present (aka windows authentication fails).
My problem is that when I use the Authorize attribe (as shown in the example below), the endpoint will only be called when windows authentication succeded. If I additionaly add the [AllowAnonymous] attribute, the User is never authenticated.
Example: (
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public IActionResult Index()
{
_log.LogDebug("IsAuthenticated = " + this.User.Identity.IsAuthenticated.ToString());
_log.LogDebug("Authenticated Name: " + this.User.Identity.IsAuthenticated.Name);
return View();
}
How can this be done in .Net 6.0? It should be really simple as authentication and authorization should be separated but it seems they are quite intertwined. I haven't found a solution after extensive googling, checking the .net core source code and trying out myself.
Is there a good way to solve this?
Remark 1: there are solutions for .Net core 3.1 but then don't work in .Net 6 Enable both Windows authentication and Anonymous authentication in an ASP.NET Core app
Remark 2: we have endpoint that have to work with Windows Authentication only and other with anonyomous authentication. These both work fine within the same application. It is really about being able to detect the windows user in an endpoint that otherwise supports anymous authentication.
I (or better we) have found a solution that works even when Windows authentication is disabled on IIS. It is not very elegant but this is what we came up with. The idea is basically to trigger another call to an endpoint to determine if the user is actually a windows loging or not. If this call is successful, then we know we have a windows user and can act accordingly, for example do a redirect to an endpoint that requires windows authentication.
Remark: If you can control the IIS settings - which probably is often the case - , then I suggest you go with the solution proposed here:
enable-both-windows-authentication-and-anonymous-authentication-in-an-asp-net-co )
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> TestWindowsAuthAsync(CancellationToken cancellationToken)
{
using var client = new HttpClient(new HttpClientHandler()
{
UseDefaultCredentials = true
});
var response = await client.GetAsync($"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.PathBase}{HttpContext.Request.Path}/HasUserWindowsAuth");
if (response.IsSuccessStatusCode)
{
// Yes, now we know that user indeed has windows authentication and we can act upon it
return RedirectToAction("QuickLogin", input);
}
// No windows credentials have been passed at this point
return View();
}
[HttpGet("HasUserWindowsAuth")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public IActionResult HasUserWindowsAuth() => Ok();
[HttpGet("QuickLogin")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public async Task<IActionResult> QuickLoginAsync(LoginModel input, CancellationToken cancellationToken)
{
var user = this.User.Identities.FirstOrDefault(i => i System.Security.Principal.WindowsIdentity && i.IsAuthenticatd);
// do something with that user
}

HubConnectionContext.User property not populated for SignalR / Blazor WASM / IdentityServer hubs

I followed the tutorial Use ASP.NET Core SignalR with a hosted Blazor WebAssembly app and Authentication and authorization in ASP.NET Core SignalR to create an authenticated SignalR hub.
However I can't get the logged in user from the hub Context.User.Identity.Name object.
Upon user creation I have added a claim:
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Email, Input.Email));
In Startup.cs I have registered this IUserIdProvider and setting a breakpoint confirms it gets called during runtime but it returns null.
public class EmailBasedUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
In the hub, I have set the [Authorize] attribute but the Context.User.Identity object is null for the Name property and if I look in the Claims collection, the SQLServer user Id is present but nothing else.
I suspect the problem is in the EmailBasedUserIdProvider which is unable to find the Email claim because it is not there on the connection.User object. What needs to be done to make this available?
Thanks

Blazor Server / Asp.Net Core: Http Request doesn't pass user identity to MVC Controller when published on IIS

currently I'm having trouble getting identity information in my MVC controllers. It's no problem when debugging the blazor application locally but when I publish the application on IIS, I only get the identity of the executing service account, for instance "MachineName\ApplicationPoolIdentity" or "MachineName\LocalService" (depending on what was selected as identity in the application pool settings) in my MVC controllers. On the Blazor pages, however, authentication and authorization seems to work fine.
I got "Windows Authentication" enabled and "Anonymous Authentication" disabled in IIS site authentication settings.
I need the users identity for our audit trail implementation, which creates an entry for each crud operation.
In the MVC Controller I tried using "this.User.Identity" or "HttpContext.User.Identity", which is the same object. When debugging locally it shows the corect identity (of myself as caller). When deployed on IIS I get "MachineName\ApplicationPoolIdentity"
the MVC controller looks as such:
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class DatabaseSystemsController : CustomControllerBase
{
// GET: api/DatabaseSystems
[HttpGet("/api/AllDatabaseSystems")]
public async Task<ActionResult<IEnumerable<DatabaseSystem>>> GetAllDatabaseSystems()
{
try
{
var identity = HttpContext.User.Identity
...
return await _context.DatabaseSystems.ToListAsync();
}
catch (Exception)
{
...
}
}
}
I hope someone can help.
Thanks in advance
L.
To get the user to impersonate use the AuthenticationStateProvider and get the user from this and cast to a WindowsIdentity to retrieve the AccessToken.
This works in both a controller and a razor component.
Inject the AuthenticationStateProvider and then in your method use the following code:
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
var userToImpersonate = (WindowsIdentity)user.Identity;
await WindowsIdentity.RunImpersonatedAsync(userToImpersonate.AccessToken, async () =>
{
// Your Code in here to call your api
}

How to handle cancel button of windows authentication pop-up in asp.net core mvc deployed on Kestrel?

I'm trying to implement mixed authentication windows + cookie based in asp.net core mvc application. When windows authentication is canceled I want to redirect user to fallback page where can choose windows or cookie based authentication. The app will be deployed on Kestrel not IIS. I'm using .net core 3.1.
Basically I need to redirect user on fallback page when http status code is 401 and substatus code is 1 or 2.
So far I tried to use status code pages as fallows:
In Startup.cs I added
app.UseStatusCodePagesWithReExecute("/Error/Status", "?statusCode={0}");
In ErrorController.cs
public IActionResult Status(int statusCode)
{
var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCode == 401)
{
return RedirectToAction("Index", "Fallback");
}
var originalURL =
statusCodeReExecuteFeature.OriginalPathBase
+ statusCodeReExecuteFeature.OriginalPath
+ statusCodeReExecuteFeature.OriginalQueryString;
return Redirect(originalURL);
}
This not works because it breaks windows authentication handshake.
I also tried to implement custom error handler and check HttpResponse.StatusCode == 401, but the error controller method is not hit probably because windows authentication is handled by another layer ..
It took me a few days before I almost thought it can't be done. But finally I figured it out.
My problem was when executing External Authentication on Identity Server 4. I couldn't catch the event when the user clicks "Cancel". I got 401 status code for entering Windows Authentication, for clicking "Cancel" and also when clicking "Signin". The solution is the following:
Place this line in Startup.cs in public void Configure(IApplicationBuilder app):
app.UseStatusCodePagesWithReExecute("/external/ErrorPage", "?statusCode={0}");
After that just create a IActionResult function in controller like this:
[HttpGet]
public IActionResult ErrorPage(int? statusCode = null)
{
if (statusCode.HasValue)
{
return View(new SomeViewModel() { ErrorMessage = statusCode.Value.ToString()});
}
return View();
}

Hosting MVC 4 site with Google Drive Client API on AppHarbor hang on oAuth authentication

I am playing with Google Drive Client API with MVC 4 web project. The code works great locally with IIS express. However, when I deploy the site to AppHarbor, the oAuth authentication hang. I tried both web client credentials and installed app client credentials. What do I need to do to get it working?
Here is the code snippet for Authentication:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = { Client_ID set in Google developer console},
ClientSecret = { Client secret in Google developer console},
},
new[] { DriveService.Scope.Drive },
"user",
CancellationToken.None).Result;
//Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Google Drive Reader",
});
//More code goes here
return View();
}
}
Update:
I figured this out and put an answer to this question in case others may what to know.
I figured this out.
First of all, the method I used in the question works only for standalone applications, does not work for MVC applications. MVC application should follow the method documented here:
https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web_applications
One important thing to notice is that web application client ID should be used, and the Redirect URL needs to be the URL to your AuthCallbackController.
Second, there is a problem in the Sample code: in HomeController
public async Task IndexAsync(CancellationToken cancellationToken)
Should be:
public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
Third: make sure adding the following appSetting to web.config so that AppHarbor sends correct redirect url.
<appSettings>
<add key="aspnet:UseHostHeaderForRequestUrl" value="true" />
</appSettings>
After that, it worked for me both locally with IIS Express and on AppHarbor.