Web API Extended Security - c#

I have a web application that has implemented the latest Web API as a restful server for a mostly javascript/ajax based web application built on .NET 4.5. I'm using forms authentication and its working as it should to secure the web api as well as the rest of the application.
I'm wanting to add another layer of security to the Web API to prevent users from using another user guid in a manual request to the API. They would have to view the source in another logged in user's browser to get that user's guid from a hidden input. They could then potentially use that guid to access the other user's data through their own authenticated session. Of course this bad person would have to be authenticated under their own account for this to work.
What I'm thinking of doing is passing the user guid with every api request and if that guid matches HttpContext.Current.User.Identity.Name then allow the request...otherwise throw an unauthorized exception and handle it on the client accordingly. But then all of my handler methods become a bit dirty as shown below...this would have to be in each and every GET, POST, PUT, etc... along with other objects in most cases.
public Community Get(string userGuid)
{
if (HttpContext.Current.User.Identity.Name == userGuid)
return myDataHandler.getUserData();
else
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
I can make this work but it seems like a bit too much overhead. I've looked at numerous posts and articles regarding web api security but nothing regarding this potential situation. What is the best way to verify every request is actually requesting information regarding the user logged in without having to do what I'm doing above? Possibly use this idea but do it globally for each api request instead of it being tested in every method? Another way around this is to user HttpContext.Current.User.Identity.Name for every request instead of relying on what is sent from the client...but I'm not sure this will work in all of my situations...as my application is still young.
Thank you for your time!

I'm currently putting together an API which uses bearer authentication (See Microsoft documentation here as there is too much detail to post here).
One thing I am using are Claims, and one of the claims I am setting is a User ID which forms part of a User Identity. When someone authenticates with the API the claims are also set (read from the token) and can be accessed from the User Context as you are currently doing to access the name.
By doing it that way you don't need to store the Guid in a hidden field.

Related

Login via API (outside app) and create a cookie for access

Ok so I think I've settled on choosing BLAZOR for my upcoming project. But first I need to do something that is seemingly very basic.
For context, Blazor Server side will be how I interface with my SQL Server dB, I want "individual accounts" mode to be the way users authenticate. I'm using net 6.0,almost all tutorials out there seem to be net 5 since they all still have startup.cs files. Ok, but I also am creating a parallel app that is NOT a website, but I want it to grab data from the same database via routes after authenticating.
For example, website.com/api/data?variablestograb as a GET would send me some json data.
OK that being said, how do I login programmatically from an outside app? If you must know, the outside app is part of Unity C#. But that doesn't matter so much, what itll do is use a post call to login in via the api routes. Something like
Website.com/api/login?un=blah&pw=haha
This will generate a cookie and I can grab it with the session data and I'll use this with every get call hence.
Just using the basic templates, Blazor server net 6.0 with individual auth, how do I set up such a route? Looking at the files, I'm at a complete loss on how the login pages are actually passing data around.
Thanks!
Update: the specific ask is exactly how do I modify the Blazor Server Net 6 Individual Accounts template to allow me to authenticate a user via an external access api? My thought would be to reference the route above for /login/ but that might not even be the best practice. But even if it is, how exactly and where would I update the template to make this happen?
(I have a feeling it's pretty basic to do, but I've been reading tutorials for weeks now and they're all just talking about internal authentication and verification within each Blazor component. I basically want an external program to also be able to "drive the car" remotely, but first it must authenticate.)
If you're building an API for this from scratch, then it seems like you have the right idea, no matter what happens, you're going to send the cookie to be website every request or at least a session id which contains all the information provided. From a database perspective maybe create a session table which then contains all the info you want and also can be easily found. That's a way for you to create a cookie for client server communication, however this is from my limited knowledge and may not be the best way.
I'd recommend hiding information like keys in the header to prevent exposure, looking at other APIs like the Spotify API you can see they set the authorisation bearer.
Exposing all the information for the credentials in the URL for what could be sensitive database behaviour may not be the best. You can hide the information in the header for every request you make.
Maybe have a default controller that obtains the user information before handling any specific requests and making it accessible to your other methods/requests?
The basic process for any external authentication is:
Redirect to the external log in page.
External Provider does it business.
External provider posts to a page on your site with the authentication information included - normally security info in the header or a cookie.
The Blazor app reads the authentication information with the AuthenticationStateProvider.
Normally you just need to write a customer AuthenticationStateProvider and code to redirect if the user is not authorized. This may be a manual button in the top bar, a you aren't logged in page with a button to log in, or an automatic redirect to the provider. The return for the provider is either your landing page or some other page to tell them they logged in successfully.
The custom AuthenticationStateProvider replaces the standard one in DI services and provides the security information to the Authorization components.
Search for "blazor custom authentication provider" will get you lots of good resources.

Asp Net Core authentication with JWT

I'm currently planning on making an online scheduler in Blazor Webassembly (NET Core 3.1). As I've written intranet applications most of the time, I'm kind of concerned about the security aspects of the web api that the client will consume.
Currently, we're issuing JWT Tokens from the Backend with the username and the validity of the token in hours to the client and store the said token in the local storage of the browser. Since the token can be accessed by the user and the claims can be extracted from it, is there anything I have to be aware of? The token then is set as the DefaultRequestHeader of the HttpClient after the user has logged in. A cusotm Middleware then validates the token and sets the username in a scoped service if the user is authenticated.
The users (customers and employees) are stored in a database which is not publicly accessible. There is no option to register a user via the website. Users can create appointments in multiple locations of the company (not at the same time/day) but how do you restrict a user from consuming an api endpoint for one location but not for the other? Since claims can be manipulated I'm really not that confident in writing the accessible locations into the jwt.
Some actions also required to be executed in the four eyes principal, e.g. a second user needs to login (30 seconds validity with a refresh if possoible) in order to confirm the action. Are there existing mechanisms which are capable of handling such a thing?
Any advice, sources or thoughts are welcome. Feel free to ask for more details if necessary.
You could look at these posts, they helped me: https://chrissainty.com/securing-your-blazor-apps-authentication-with-clientside-blazor-using-webapi-aspnet-core-identity/

identityserver3 - can an access token issued by one id server be used with another id server?

I've been trying out IdentityServer3 and have watched the videos, followed through the online docs, downloaded the samples and I have got the samples all working fine.
I have set up two separate identity servers, one for securing a web api and one for securing an MVC app. I have done this rather than have one id server for both because the two applications are essentially for two different customers and only a few of the users will be in common.
However, I do want to be able to call the web api (api_A, idsvr_A) from the mvc app (mvc_B, idsvr_B) for some users (Single Sign On).
I'm finding that this works fine if I use the same idsvr at both ends but I thought that I should be able to have mvc_B get an access token from idsvr_B, add it to a request calling api_A and have idsvr_A act on the claims in the token?
I'm finding that idsvr_A does not understand(?) the token from idsvr_B and when I try to inspect the claims list on context.Principal in a breakpoint in api_A it is empty & the principal is not set to the identity from idsvr_B:
e.g. in a class deriving from ResourceAuthorizationManager, the context in the
CheckAccessAsync(ResourceAuthorizationContext context)
method ends up with an empty claims list on the context.Principal.
Is there something I need to do to translate claims from one idsvr to another? Or have I not set this up correctly? I thought that since the access token format is standard the tokens would be interchangeable between id servers?
I'm really asking for confirmation that this should work, before I do further debugging to make sure the token is being passed and received correctly.
Presumably the two identity servers will have different issuer names and different signing certificates - so no - the web api will not accept both unless you add one validation middleware for each identityserver.
It is uncommon for an API to trust multiple issuers but technically possible.

REST session workflow for creating users

I am working on a C# WebApi/MVC project that has a rather large workflow process for creating a user and placing in their required information.
There is about 10 major steps involved, in which it could technically take a user hours to fill out.
The first step takes standard basic information such as username, password, email, name, address etc.
What I would like to do is after this first step is successful, send a rest call that will create the basic user in the user table, and then prepare a session for the further steps in which when any field is filled out in the next steps, it will automatically send an ajax call and update the field in the database.
While this all sounds easy and simple in theory with the use of sessions, which I could do in MVC, I want to do this in WebApi with REST in which REST is supposed to be STATELESS.
Has anyone come across similar issues, and if so what do they recommend as an approach? The options I can currently think of are:
-Ditch the REST for standard MVC for this process and leave WebAPI for only Reads instead of Writes as the only Write process is the inital creation of users/accounts.
-Using Authentication tokens? But can this handle this process successfully?
-Once the user is created, take the username/password for every REST call as the auth to the WebAPI? Store the User/Password in MVC session and directly call the API from MVC, mobile applications would just store the username/password in the application and call the WebAPI (I think this is the most appropriate)
Can anyone tell me if any of those options are the best practice, or does anyone have a better best practice/process for these things? I would prefer to write things once to cover Web and Mobile as much as possible rather than having to duplicate processes.
Thanks in advance!!!
I would consider to modify regular WebAPI OWIN register flow.
Collect basic user info and post to Web API via Ajax. If succeeded -
send OWIN token back to the caller in HTTP header.
Proceed to extra
steps for user info updates (via HTTP PUT for example) and put the
token in authenticate header. Mark WebAPI update procedure with
Authorize attribute.
This blog post could help to setup WebAPI to issue and accept bearer tokens.

ADFS Active Authentication .NET 4.5 (Post-WIF)

I have an ASP.NET web application (running on .NET 4.5). It's currently doing forms authentication. We've set up an ADFS 3 server with multiple federations, some internal, some external (customer STSs), and we'd like to configure the web application to use an active authentication model. If I understand it correctly, that means that we will continue to use the login form in my web application, and it will gather credentials from the users, then send a security token request to our ADFS server. The token request would presumably tell ADFS which federation to send the request to. If everything is successful, then I get a token back from ADFS, validate it, and construct a ClaimsPrinciple and go from there.
Now, with that background, the problem I'm running into is how to send the token request to ADFS in .NET 4.5. Every example I've seen, despite being labeled as applicable to .NET 4.5 uses the old UserNameWSTrustBinding class from WIF. This is deprecated and not present in 4.5. Web searches for "UsernameWSTrustBinding 4.5 equivalent" have been fruitless. I've seen one guy construct his own class to duplicate the functionality, but I can't believe this is necessary. I've got a hunch that there is a class here somewhere that I'm supposed to be using for the binding in the WSTrustChannelFactory, but I can't find it. Or, perhaps the entire WSTrustChannelFactory pattern is outdated as well (but then why would it have been included in .NET 4.5)?
Can anyone provide a snippet of code or even shed some light on how you're supposed to go about active authentication in .NET 4.5?
So far my best idea has been to check username in the users cookie (if it exists) or from regular login form when the cookie didn't exist. With that info, I can determine whether it needs to be sent to the IdP or not. In the case that it needs to be sent to the IdP, I can just build a request URL and redirect.
WSFederationAuthenticationModule instance = FederatedAuthentication.WSFederationAuthenticationModule;
SignInRequestMessage request = instance.CreateSignInRequest(Guid.NewGuid().ToString(), instance.Realm, true);
request.AuthenticationType = "urn:federation:authentication:windows";
Response.Redirect(request.WriteQueryString());
Of course, I can tweak that request with the appropriate .HomeRealm value or .AuthenticationType in order to skip the HRD process, and then after that, they'll be sent back to the app authenticated and with a proper claimsidentity.
One reason that this isn't the perfect answer for me is that if the user has never logged in before, or has cookies disabled, and depending on the federation, there's potential for them to have to login twice. That is, once to the app's login form, and once to the ADFS form. That's why I was hoping to be able to send a request programmatically somehow instead of redirecting. That way I could presumably send the username & password that were already collected by the app without having to collect them again at ADFS.
For that reason, I won't mark this as the answer. I'd like to hold out for better.

Categories