Why doesn't HttpContext.SignOutAsync invalidate the authentication cookie? - c#

I am using ASP.NET Core 2.1, and I am building a website which uses the pretty basic kind of authentication, with cookies. I am using the builtin functionality for this, i.e. HTTPContext.SignInAsync and HTTPContext.SignOutAsync. It works well in the browser, I sign in, sign out, the ControllerBase.User property works as expected.
But I am also going to use that authentication for a REST API, and I am encountering a strange behavior. I am not sure whether it is a bug, or a feature, or just a misunderstanding, but after I sign out, if I keep the cookie in the headers, the User property still remains set, and access is allowed, even with [Authorize].
Is that supposed to happen? It does not seem logical. Signing out means closing the session, which means you have to sign in again to perform actions which require authentication.
But if this behavior is normal, what do I have to do to manually invalidate the authentication cookie on sign-out?
I can provide a minimal code to replicate this behavior on request.

Related

ASP.NET Core get partially authenticated user (i.e. correct username + password, but not yet 2FA)

In ASP.NET Core (v5.0), is there a way to determine inside of a controller/pagemodel/middleware component, whether a specific HTTP request is made by a user who has successfully authenticated using their username+password, but has yet to go through their 2FA stage (assuming it's enabled)? So basically at the stage of the authentication process, where they've been redirected to the 2FA token input page after successfully entering their credentials.
The value of HttpContext.User.Identity.IsAuthenticated is only true if the user has authenticated all the way including with 2FA, which makes sense of course, since it's a boolean.
I've been trying to get any sort of status indicator of this login state using some combination of SignInManager, UserManager and IdentityUser objects, with no luck. These objects seem to only be available after a user has already successfully authenticated, which of course makes sense too.
My use case is that I'm writing a middleware component which filters all requests and redirect the user to input their 2FA token when trying to perform certain actions as an additional security measure. So I thought it'd be neater to also filter the initial login 2FA (if enabled by the user) through this middleware. If this isn't reasonable, then I'll just have to handle these two different applications of 2FA verification separately in my code, which will likely add a bit of redundancy (but not hugely so).

Generate a token based on the windows user who makes the request

I have searched all over for an answer to this, and not found anything that seems to answer my question. Which feels like it should be an easy one (but clearly isn't).
I have an API which authenticates using a token. This token I generate from my application - more specifically, I have a new Token Generation web call that will return a token. Currently, I pass in a cookie with the user and password information, and it uses this to identify who I am and what I should be allowed to do. This is all working absolutely fine and hunky-dory.
I am achieving this process by making the Token Generation use OWIN Cookie Authentication, which means that the cookie is read and the Identity is set. I am then able to use this identity to confirm whether the user is allowed to access the system.
What I now want to do is replace this Cookie Authentication process by authenticating against a Windows User (everything is windows based, and this will be an option so non-windows users can still use the cookie authentication route). But I cannot discover how to straightforwardly do this.
Note that I don't actually need to validate that the user is genuine or refer back to the AD at all. If you provide a windows user that matches a user in the system, you can log in.
So how can I - easily - get the requesting user into the Identity Name? Or is this not possible?
If you are looking for information on the current user accessing your program, assuming the program is running on the user's machine and is windows based, you can simply query windows for the user's username or any other publicly available information about the user.
Refer to https://learn.microsoft.com/en-us/dotnet/api/system.environment?view=netframework-4.8 for information on the Enviroment class and what it's features are.
If you could provide some code or further clarity I could help you further.

How to do a complicated 2FA authentication in .net core

Ok so this request is a little convoluted. Apologies ahead of time.
Firstly this is what I need:
Google signin (using: .AddGoogle() and this is working)
After signing in initially push user to another page where they must enter a PIN.
Verify that PIN against the database on the back-end. If it does not match return user to enter PIN again.
Once PIN is verified allow full access to the site (excluding enter PIN page).
After X minutes of inactivity (or if the browser is closed) log the user out immediately.
Next, this is what I think I need to do:
Use services.AddAuthentication(...).AddCookie(...).AddGoogle().
So I know that the cookie will have the expiration time embedded into it's contents, but I need some way of also making it expire when the session expires, and keep track of whether or not the PIN has been verified, hence:
Create my own middleware that creates a session cookie (if one does not exist) with a random string or number in it.
Modify whatever is happening in AddAuthentication with the following behaviour:
Have ASP.NET write out it's auth cookie with the session cookie's random value encrypted into it.
Only accept an ASP.NET auth cookie if the cookie's embedded expiration time has not passed AND the auth cookie's embedded session random string matches the session cookie.
Have ASP.NET auth redirect to the PIN page if no PIN is recorded in the cookie and then forward the request to the appropriate page once the PIN is accepted and recorded back into the auth cookie.
My question is am I right about needing to write my own middleware? Will I need to entirely replace AddAuthentication with my own middleware or can I get what I need by making my own middleware to supplement what AddAuthentication is doing already?
In experimenting one of the things I've had trouble with is if I logout a user in the middleware before authentication (when I detect the session cookie is missing) I can't signal to the authentication mechanism to redirect back to google sign-in, and the user just gets an error and has to refresh.
Additionally, if I am on the right track: How can I create a middleware that will inject a value into the auth ticket before it's created; verify that value; and remove the auth ticket cookie and/or signal to the auth middleware that it should not process the cookie stored in the context's request, and should instead redirect to the google sign-in?
EDIT: So I tried to do some reading on my own. I'm looking at OAuthHandlers and claims, maybe there is some way to utilize this? I'm still investigating but any direction is welcome.
As it is generally said: you should not be writing your own authentication.
Since you didn't mention Identity, I'll assume you are not using it. I would recommend using .Net's Identity as it easily covers the problems you have. As of Identity 2.0, external login(Facebook, Twitter, Google) and 2FA functionality are included as a part of this library. This is the defacto authentication system for .Net, and adding it is not too difficult.
The actual implementation of Identity goes beyond the scope of the question so here's a link to help you get started: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.1&tabs=visual-studio%2Caspnetcore2x

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.

Set up STS but keep formsauthentication in webapp

I'm enabling an windows identity foundation on an existing webapp.
I want to mess as little as possile with the existing code so I would like to the login page which uses formsauthentication left in the application and I just connect with the STS if the user enters the application via a specific page e.g "im_comming_from_some_other_site.aspx".
in the "im_comming_from_some_other_site.aspx" the code would be like:
Page_Load(...)
{
if(verifyAgainstSTS()
{
FormsAuthentication.SetAuthCookie(<some_STS_Userid), ...)
Response.Redirect("default.aspx")
}
else
{
Response.Redirect("http://<STS_server_name/<STS_service...etc>")
}
}
Is there someone who knows if this may be done and how? Any links to example code (if available) deeply appreciated.
(Of course some code would be needed when to determine what to do when the authentication is timed out; either go to local login page or goto STS-login page)
I know this may seem like a bad design, not going all the way with STS, but I need to implement this ASAP and I want to keep the original site as untouched as possible.
It is not a bad design, it's your requirement and you try to fulfill it. We have working system built like that and it's not a rocket science. The only difference is that we switch it to forms/sam statically (via global settings), not dynamically.
Anyway, you keep your forms authentication in web.config so that when there's no authorization for current user, forms redirects the request to the login page.
In the login page you have two options. One creates the forms cookie somehow.
The other option involves WIF's FederatedPassiveSignIn control.
If a user follows forms authentication, the cookie is set and you are done.
If a user follows the STS login control, sooner or later he/she will come back with valid SAML token. The FederatedPassiveSignIn will pick it up automatically and you just handle the redirect in the SignedIn event.
You will even not need the if you mention in your question.
There's one caveat from what I remember. When a user is authenticated by STS, the WS-Federation cookie is created, you can read claims etc. Everything works.
However, if a user is authenticated by forms, the SAM (SessionAuthenticationModule) will REPLACE forms cookie by the WS-Federation cookie in ASP.NET pipeline upon EACH request (I guess it's because the SAM is later in the pipeline that forms authentication module).
This will NOT blow up your context.User.Identity.IsInRole(...) also authorization works correctly because SAM will copy user roles to corresponding claims.
However, if at any place in your code you try to extract information directly from the forms cookie (instead of using general APIs), you could find out that the forms cookie is not present even if the user was authenticated by forms in first place (and the cookie is not present because it will be replaced by the WS-Federation cookie).

Categories