Set up STS but keep formsauthentication in webapp - c#

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).

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.

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 can I get a list of logged in users with IdenityServer4?

I've an AspnetCore + Angular setup where the authentication part is handled by IdentityServer4. One of the requirements now is to get a list of all users who are currently logged in. Any ideas/suggestions how to proceed on this?
PS: Just in case it's needed, I'm using implicit flow .
Auth is persisted via a cookie, which lives client-side. The server doesn't know who all is logged in. It only know in the context of a particular request (where the client sends the auth cookie back) whether that particular client is authenticated or not.
If you really need this, you'll need to manually track it yourself somehow, like adding a record to a table for each login. However, this can get gnarly fast. You'll need to also manage sign outs and expirations, to keep your custom data fresh.
A generic answer could be: ASP.NET CORE keeps each user session in a cookie, separately located at each user's browser on each device. Once you need, you have to centralize that system yourself. Howerwer, since the question regards Identityserver, we can look into what is already done in that area by the IdentityServer authors. And they have provided at least two extensibility points.
The first one is to employ Reference Token (instead of the jwt by default), then look through the persisted grants database, fetch all the sessions grouped by userId. Not a standard way for OpenIdConnect, but it exists.
The other approach is to implement your custom session store based on a database (instead of the cookie based by default). That provides you access to all the clients logged in with the given user id. Here is my old (but still valid) example of a hybrid (cookie + IDistributedCache such as REDIS) extension for the DefaultUserSession. Usually after the requirement to list all the user sessions, appears the other one: to create a kill the session button next to each row. Here you have to be careful with access token lifetime (make it reasonably short), as a jwt once issued can not be invalidated before its normal expiration.
There is provision in the default cookie provider in ASP.Net Core to use a DB or distributed cache to store cookie payloads. Check out the Microsoft.AspNetCore.Authentication.Cookies.ITicketStore interface and the CookieAuthenticationOptions.SessionStore property.
We created our own implementation that stores auth cookies in the PersistedGrants table along with IDS4 stuff. It works well, keeps cookies small and facilitates a "sign out on all devices" option also. If you align the expiry of the entry with the cookie authentication properties then the housekeeping is done for you.

Implement both FormsAuthentication and BasicAuthentication ASP.Net

We currently have a website which has formsauthentication implemented on it.
One of the client requested us to implement a single sign on solution to this website with basic authentication and we want to keep formsauth for the rest of the clients.
So I created a new SSO folder, SSO/SSODefault.aspx page, which will be accessed by only one client and I configured basic authentication in IIS (enabled basic auth and disabled anonymous).
How do I configure/code at application level so that if a user access ~/SSO/SSODefault.aspx I need to perform basic authentication and if user access ~/Login.aspx or ~/any other page except the above SSO page I need to do FormsAuthentication.
You'll still do FormsAuthentication, but in your SSO page, you'll generate a ticket that the FormsAuthentication method will look at, discover that it is already authenticated, and let that user in. This blog post should get you started in the right direction.

global admin page for multiple applications in ASP.Net

I was wondering if anybody could give advice on a secure way to implement a global login. I have an admin page that accesses active directory admin groups after typing in your username and password.
current logged in account (on computer) does not matter
user in web browser goes to web app, redirects to global login page with query string of app name
types user name and password of an account in AD (not necessarily current computers logged in user)
authenticates (looks up user, pass etc and authentication is valid)
redirects back to original web app.
Once redirection happens, how do I securely tell the original web app that this user is ok until the original web session dies?
The way I'm thinking of implementing it:
My original thought was to pass the session ID of original app to the login page as well as the app
name. Store that session in a DB once authentication is checked. Master page of the other app validates on page load that the session ID matches. On session close, remove current session ID from DB.
You cannot depend on sessionID accurately in some cases. SessionID only becomes a constant value after a (any) page makes a request for a session variable, if you dont have Session_Start not defined in global.asax. If you log the user in and the default page does not access session, you will end up with a different session id for the subsequent requests until a request to session is made. Usually it is always constant as there is a default empty session_start event in global.asax.
But similar to your model, you could generate a GUID (or make sure you access session on login/authentication) and store it in the user table with an expiration. Your web sites can check this key and if currently valid, auto sign the user in.
You also cannot depend on session_end event since there is no real accurate way of detecting it (eg: user closing browser). So, you should store this ID with an expiration time along with it, preferably the same as session timeout. Just like session, you will need to extend this, something like a sliding expiration. This way, this id will be expired after a period of inactivity.
also, this Claims-Based Single Sign-On for the Web may be of interest to you.
What you're describing sounds like it would be better implemented using Windows Identity Foundation and Active Directory Federation Services 2.0. I don't know the extent of all your requirements, but it might be valuable to check out what these will give you out of the box.
You could use a cookie. If you pass the name of the application to the login page, you can set that application name to the PATH property of the formsauthentication cookie. By setting the PATH property, you're effectively limiting the readability of the cookie to the pages within that path. Because it's a common login interface, this may require some code being done perhaps in a common page base class that handles the parsing of the cookie information.
You can share authentication tokens between .NET applications, even between .NET CLR's if you want to. I think this will give you the single sign on you're looking for.
At a high level you need to share a machine key between the applications. You can generate the machine key using the key generator app code example found on this link, and providing you then reference the same key in each application the sign on can be shared:
http://msdn.microsoft.com/en-us/library/ms998288.aspx
There are some consideration when looking at things across domains or accross machines/web farms etc, but if all apps are on the same box it's not too hard.
The harder side of things is sharing things like session state which I don't believe can be done accross app pools. (I ended up recreating the session object on the new app manually last time I did it :( but that was between 1.1 and 2.0 apps )
EDIT: I did a rough write up of it last year and was using it as a test for a demo article on my site, might be worth a read through if you want things broken down a bit more (Ignore the unfinished site!):
http://www.dougmcdonald.co.uk/test/html5/v5/articles/2010/05/10/Sharing-forms-authentication-between-applications/

Categories