IClaimsTransformation claims not sticking - claims-based-identity

I've followed the tutorial from Microsoft on .NET Core claims (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/claims?view=aspnetcore-6.0#extend-or-add-custom-claims-using-iclaimstransformation)
The line if (!principal.HasClaim(claim => claim.Type == claimType)) is always true - i.e. the claim never exists in the ClaimsPrinciple after refreshing the page.
Any reason why it wouldn't stick? I have the class inherited from IClaimsTransformation and have hooked it up in program.cs

I have checked into this using .NET 6, Microsoft.Identity.Web, and Blazor Server.
The documentation says "This method might get called multiple times."
For me, the IClaimsTransformation is called each time. The might never happens.
After debugging, it seems the Microsoft.Identity.Web is providing the initial claims. The initial claims are passed to IClaimsTransformation and then my custom claims are added as a second Identity Principal which is this section of code:
principal.AddIdentity(claimsIdentity);
My custom claims are never added in duplicate and the IClaimsTransformation method fires multiple times for each page/component load.
Long story short, I think the custom claims are cleared and re-added each time IClaimsTransformation is run.
At first I was worried about performance because I was loading data from a database to generate the custom claims. So...I timed it and reading the database only required 44ms to 54ms - in other words negligible even at scale. Another benefit, is that as soon as the database is updated the claims are almost immediately reflected to the end user.
To verify yourself, you can add your custom claims using IClaimsTransformation and then setup a page to iterate through all the claims. Your custom claims should be there so long as you added the service during start-up:
services.AddTransient<IClaimsTransformation, UserClaimsTransformation>();

Related

Why is Context.User.Identity suddenly null with .NET 5.0.102?

I was using authorization fine previously with .NET 5, but suddenly after .NET 5.0.102 Context.User.Identity was not populated at all in some methods. Nothing else changed, but some places where identity was supposed to be used just didn't get the information.
This happened in controller methods as well as in Signalr hub methods. Why?
It seems .NET 5.0.102 changes the way authorization is handled and I couldn't find this from the documentation. My Signalr hub didn't have the Authorize attribute set so OnConnected etc methods didn't get any identity information. The same with the controller methods.
So every single place where you need identity needs to have Authorize attribute set!
This doesn't work in hub methods, the whole hub has to have the attribute.
I assume this is to reduce the places where the identity information is available unless you specifically ask for it, but is a breaking change that I for one did not see coming.
Note that this is documented behavior: the attribute should always be there, but for some reason it worked without before also and may cause cases where it’s left out.

Getting a token for Web API

Once upon a time I used to install Thinktecture.IdentityModel, call it using basic auth to get a token and then pass this in the headers of ajax calls to Web API.
I just tried to do this, and it barfed.
Up periscope!
Method not found: no match for ctor signature (hurrah for Fiddler).
Comparison with a project in which it does work reveals that the required signature exists in version 2.0.0.0 of System.IdentityModel.Tokens.Jwt but is no longer present in version 4.0.20622.1351
Quick, Robin - to the Bingpole!
There's a github support query on this very topic and in the comments we find
leastprivilege commented on Jan 27
The AuthenticationHandler is not the recommended approach anymore for
Web API v2 since everything is now built-in - use middleware instead.
Unask the question, Grasshopper
This is a definitive opinion. He's one of the library's authors. It is also incredibly unhelpful to anyone who needs to ask the question.
Could someone could point me at appropriate introductory and tutorial links so I can join the ranks of those who nod sagely and marvel at the zen-like brevity of this comment?
I was using AuthenticationHandler to validate out of SQL Server table containing a username and password. Purists please don't lecture me, there are countless businesses that don't or won't use third party OAUTH. I need to convert a username/password pair into a session token that I can use on my Web API methods. That's all. I happen to agree about OAUTH but the people paying for this are not interested in grand unified authentication, they like silos.
Supplementary info
I found a publically accessible PluralSight course on the whole authentication thing. It's by the fellow who wrote the ThinkTecture.AuthenticationHandler and it's quite good as a backgrounder on recent change.
In the course of the material he refers to another PluralSight course on MVC, which has more information on OWIN (which is pure API ie an interface) and Katana (which is Microsoft's implementation of OWIN).
At this point it is clear to me that
There has been a fundamental architecture change for assorted good reasons.
A MessageHandler is not the place to do authentication.
There is a NuGet package called Thinktecture.IdentityModel.Owin.BasicAuthentication which is described as OWIN middleware for HTTP Basic Authentication in OWIN/Katana applications.
My still fuzzy understanding of the new landscape suggests that the middleware package is what I need. NuGet did its thing and now I have to provide some code to insert the middleware into the OWIN middleware chain and more code to actually validate the credentials. As near as I can tell I need to do this in Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseBasicAuthentication("some_realm", (id, secret) =>
{
if (id == secret) //should check database, but never mind right now
{
var claims = new List<Claim> {
new Claim(ClaimTypes.NameIdentifier, id),
new Claim(ClaimTypes.Role, "Foo") //this can come from db also
};
return Task.FromResult<IEnumerable<Claim>>(claims);
}
return Task.FromResult<IEnumerable<Claim>>(null);
});
Assuming I've got this right so far, how do I use it? Do I continue to decorate Web API methods with [Authorize]?
Success! Nearly.
At the moment a request from my test client causes a hit on the breakpoint I've set on the line if (id == secret) and the condition is satisfied because id and secret contain the values sent by my test code. Claims objects are created as per the code above and duly returned, but the test client receives 401 Unauthorized.
This is almost certainly due to a failure to create a principal object. There's some business with a UserManager and an IdentityModel, and it may be possible to customise that to use our own schema.

Calling Web API action from within an actionfilter to take advantage of outputcache for repeated authorization

I wanted to take advantage of the OutputCache attribute for a authorization check to be made on an Action in a Controller. Based on a related thread and what I could tell the following design made sense, I was looking for feedback or corrections.
In other words:
1. a client calls an action on a controller which has my custom authorization filter attribute
2. The filter makes an HTTPClient call to another action on a web API controller (in the same site)
3. This action has an outputcache attribute to ensure I don't repeat an access check for the same parameters
Questions I had:
1. Is my use of OutputCache appropriate? I'm assuming a 5 minute cache lifetime.
2. In step#2 is a HttpClient call from my authorization filter the only way to make sure the pipeline for caching is built and used?
There are several related threads but I couldn't quite find one that tried to use this pattern for authorization.
FYI I did build out the solution I'd designed.
Answers for the questions I had:
Q1: OutputCache attribute on the authorization check call seems to work fine, I had to vary it using the cookie parameter, I'm a little concerned about this given cookies come from the client, but I still have the forms authorization filter higher and that should reject completely un-authenticated requests from coming in, would be happy to change to a better solution.
Q2: If i didn't make an HTTP call from my filter, the cache pipeline was not being built, so this is necessary as far as i can tell.

Calling ConfigureAwait from an ASP.NET MVC Action

I was working on a presentation and thought the following should fail since the ActionResult isn't being returned on the right context. I've load tested it with VS and got no errors. I've debugged it and know that it is switching threads. So it seems like it is legit code.
Does ASP.NET not care what context or thread it is on like a client app? If so, what purpose does the AspNetSynchronizationContext provide? I don't feel right putting a ConfigureAwait in the action itself. Something seems wrong about it. Can anyone explain?
public async Task<ActionResult> AsyncWithBackendTest()
{
var result = await BackendCall().ConfigureAwait(false);
var server = HttpContext.Server;
HttpContext.Cache["hello"] = "world";
return Content(result);
}
ASP.NET doesn't have the 'UI thread' need that many clients apps do (due to the UI framework below it). That context isn't about thread affinity, but for tracking the page progress (and other things, like carrying around the security context for the request)
Stephen Toub mentions this in an MSDN article:
Windows Forms isn't the only environment that provides a
SynchronizationContext-derived class. ASP.NET also provides one,
AspNetSynchronizationContext, though it's not public and is not meant
for external consumption. Rather, it is used under the covers by
ASP.NET to facilitate the asynchronous pages functionality in ASP.NET
2.0 (for more information, see msdn.microsoft.com/msdnmag/issues/05/10/WickedCode). This
implementation allows ASP.NET to prevent page processing completion
until all outstanding asynchronous invocations have been completed.
A little more detail about the synchronization context is given in Stephen Cleary's article from last year.
Figure 4 in particular shows that it doesn't have the 'specific thread' behavior of WinForms/WPF, but the whole thing is a great read.
If multiple operations complete at once for the same application,
AspNetSynchronizationContext will ensure that they execute one at a
time. They may execute on any thread, but that thread will have the
identity and culture of the original page.
In your code, HttpContext is a member of your AsyncController base class. It is not the current context for the executing thread.
Also, in your case, HttpContext is still valid, since the request has not yet completed.
I'm unable to test this at the moment, but I would expect it to fail if you used System.Web.HttpContext.Current instead of HttpContext.
P.S. Security is always propagated, regardless of ConfigureAwait - this makes sense if you think about it. I'm not sure about culture, but I wouldn't be surprised if it was always propagated too.
It appears because the Controller captures the Context whereas using System.Web.HttpContext is live access to what is part of the synchronization context.
If we look at the ASP.NET MVC5 sources we can see that the ControllerBase class that all controllers inherit from has its own ControllerContext which is built from the RequestContext.
I would assume this means that while the synchronization context is lost after a ConfigureAwait(false); the state of the Controller in which the continuation is happening still has access to the state of the control from before the continuation via the closure.
Outside of the Controller we don't have access to this ControllerContext so we have to use the live System.Web.HttpContext which has all the caveats with ConfigureAwait(false);.

MVVM on top of claims aware web services

I'm looking for some input for a challenge that I'm currently facing.
I have built a custom WIF STS which I use to identify users who want to call some WCF services that my system offers. The WCF services use a custom authorization manager that determines whether or not the caller has the required claims to invoke a given service.
Now, I'm building a WPF app. on top of those WCF services. I'm using the MVVM pattern, such that the View Model invokes the protected WCF services (which implement the Model). The challenge that I'm facing is that I do not know whether or not the current user can succesfully invoke the web service methods without actually invoking them. Basically, what I want to achieve is to enable/disable certain parts of the UI based on the ability to succesfully invoke a method.
The best solution that I have come up with thus far is to create a service, which based on the same business logic as the custom authorization policy manager will be able to determine whether or not a user can invoke a given method. Now, the method would have to passed to this service as a string, or actually two strings, ServiceAddress and Method (Action), and based on that input, the service would be able to determine if the current user has the required claims to access the method. Obviously, for this to work, this service would itself have to require a issued token from the same STS, and with the same claims, in order to do its job.
Have any of you done something similar in the past, or do you have any good ideas on how to do this?
Thanks in advance,
Klaus
This depends a bit on what claims you're requiring in your services.
If your services require the same set of claims, I would recommend making a service that does nothing but checks the claims, and call that in advance. This would let you "pre-authorize" the user, in turn enabling/disabling the appropriate portions of the UI. When it comes time to call your actual services, the user can just call them at will, and you've already checked that it's safe.
If the services all require different sets of claims, and there is no easy way to verify that they will work in advance, I would just let the user call them, and handle this via normal exception handling. This is going to make life a bit trickier, though, since you'll have to let the user try (and fail) then disable.
Otherwise, you can do something like what you suggested - put in some form of catalog you can query for a specific user. In addition to just passing a address/method, it might be nicer to allow you to just pass an address, and retrieve the entire set of allowed (or disallowed, whichever is smaller) methods. This way you could reduce the round trips just for authentication.
An approach that I have taken is a class that does the inspection of a ClaimSet to guard the methods behind the service. I use attributes to decorate the methods with type, resource and right property values. Then the inspection class has a Demand method that throws an exception if the caller's ClaimSet does not contain a Claim with those property values. So before any method code executes, the claim inspection demand is called first. If the method is still executing after the demand, then the caller is good. There is also a bool function in the inspection class to answer the same question (does the caller have the appropriate claims) without throwing an exception.
I then package the inspection class so that it is deployed with clients and, as long as the client can also get the caller's ClaimSet (which I provide via a GetClaimSet method on the service) then it has everything it needs to make the same evaluations that the domain model is doing. I then use the bool method of the claim inspection class in the CanExecute method of ICommand properties in my view models to enable/disable controls and basically keep the user from getting authorization exceptions by not letting them do things that they don't have the claims for.
As far as how the client knows what claims are required for what methods, I guess I leave that up to the client developer to just know. In general on my projects this isn't a big problem because the methods have been very classic crud. So if the method is to add an Apple, then the claim required is intuitively going to be Type = Apple, Right = Add.
Not sure if this helps your situation but it has worked pretty well on some projects I have done.