I am planing to implement a mult- tenant MVC application, where each tenant gets a "sub site" URL, so that rooting would look like:
www.mysite.com/{TenantId}/{Controller}/{Action}
When a user logs into the application, the login shall always be associated with a single tenant (there is no need for single sign on). However, it should be possible that he registers with two or more tenants in the same application. In such case, I need him to be able to simultaneously use both "sub sites".
As I understand it, the FormsAuthentication is using one cookie with a name specified in the web.config, visible in the code through FormsAuthentication.FormsCookieName.
I was thinking about imlementing an approach similar to this one: implement custom cookie creation and checking (using FormsAuthentication.Encrypt\Decrypt for creating the tickets and then manually creating cookies with different names for different tenants). In this way the user could have several cookies, one for each tenant "subsite".
I am wondering, if this approach seems sensible/secure? I was dotPeeking the FormsAuthentication stuff and there is quite some additional stuff under the hood - with a reason I suppose. Also reading articles like this (where the cookie expiration in the secure connection scenario was not handeled properly) makes one wonder, if custom security implementation is really the best way to go...
Alternative to several cookies might also be setting the cookie Path property. If I understand it correctly, the cookie shall be sent only with requests starting with {TenantId} if I set its path when creating it? Will FormsAuthentication know how to handle such cookies? When new ticket will be reissued, will the Path be respected?
And of course, all other suggestions are appreciated as well.
Related
I am creating an identity service using IdentityServer4 and AspNetCore.Identity. The service will serve multiple websites which need to have separate users. As such I was thinking the simplest way to keep users separate is to have separate user collections in my database (in my case MongoDb), and simply use a different one based on the client_id.
I am registering my IUserRepository like so:
services.AddSingleton<IUserRepository>(x => new UserRepository(mongoClient));
I was thinking the easiest way to achieve what I want is to handle it at the DI level, so I have come up with so far is to change this to:
services.AddScoped<IUserRepository>(x => GetUserRepo(x, mongoClient));
Where GetUserRepo is a method that reads the client_id out of the request and returns a repository for the correct user collection.
This does seem to work but my issue is how "hacky" the implementation of GetUserRepo has to be:
I have to check if the request url is /connect/token and if it is, manually read the request body to find the client_id
If the user is passing a Bearer token (my identity service also includes a management API for creating users etc) then I have to manually decode the token and find the client_id claim
Really I am just wondering if there is a nicer way to do this (anything provided by IdentityServer4?) and also if this is "ok" to do at all?
The only thing I have found that could maybe help is IIdentityServerInteractionService.GetAuthorizationContextAsync however this requires a returnUrl which I don't have as I am using client credentials or resource owner password credentials methods to authenticate.
Not specifically an answer but as it might help put others on the right track:
After thinking more carefully about our scenario I realised it wasn't really necessary to do what I was trying to do. Instead each website will simply use AspNetCore.Identity to handle their own users (with their own data stores) and I am just using IdentityServer to secure my API and set each website up as a client.
I read the documentation of Nancy Forms Authentication. As far as I can tell, the approach recommended there leads to lifelong session IDs:
"The identifier is the token that will be put in the authentication
cookie which will be used to re-establish the identity of the user
that is performing the request, so that you do not need to enter your
credentials for each request."
As far as I understand, that "identifier" is what most people call a session ID.
It is also important to know that the identifier should be treated as
permanent for the user that it was generated for and will be reused
across requests and application sessions.
Is this really the recommended approach? If I understand correctly, this means that session IDs never change and never expire. So the session ID is the equivalent of a password, which
is retransmitted in a cookie with every request
is probably stored in clear-text in the DB, if you follow the docs to the end
I know that I could implement this differently with Nancy, but my point is that such an approach should not be explained in the docs as reference.
So if an attacker ever succeeds in stealing that session ID, e.g. by an XSS attack, he gains lifelong access to the system.
Please correct me and show me the mistake in my thoughts!
The identifier you are referring to isn't a session id, it's an unpredictable user identifier, which is then mapped (if necessary) to the real user identifier in the back end. This is so if someone logs in as user X, and somehow manages to decrypt, re-encrypt and re-sign the cookie they can't just change the user ID to "admin" or something similar and gain admin access (which is how the ASP.Net Oracle attack worked). It's also HttpOnly, so not really capturable via XSS, although technically it could be captured using XST.
Creating and expiring a session (and deleting the auth cookie if necessary) is a different task altogether - how and when you determine if an auth cookie should be accepted, removed, or confirmed with an additional password request is application specific. This is a common pattern now where a site will consider you "logged in" eternally, until you do something "secure", in which case it will ask you to revalidate if you haven't done so recently.
Hope that makes sense.
that "identifier" is what most people call a session ID.
It's not. It's something like UserId. as the documentation states:
We have chosen to use a GUID as the identifier. The reason for this is that using something like the username of id is a potential vulnerability, because, if the cookie encryption was somehow compromised, it would be easy to spoof the identity of another user by guessing their username or id.
They're just using a GUID assigned to the user for more security. Of course, (cookie based) FormsAuthentication has all the disadvantages of cookies. If someone can get access to your auth cookie, they can authenticate themselves as you. But session cookies and forms authentication cookies are completely different things, This answer states the differences pretty clearly.
I've got an application that's using the MVC4 Simple Membership provider. I've added some code to the Login method that sets up some session information I need to deal with some security things.
If I close the browser and come back to it, MVC still shows me logged in in the top left corner and the User.Username properties are still filled out, but the extra stuff I put in there, obviously, isn't.
When or where does this "authentication" take place? I tried checking the request and user objects in the Application_Start in Global.asax, but they're still null when that runs.
Is there somewhere else in that authentication pipeline that I can override or call my method to extract the things I need that would be more appropriate?
Thanks!
"Remember me" functionality has nothing to do with Simple Membership, or any membership. And no actual "login" occurs when using it. It's a persistent cookie that is placed on the users system, and that cookie is read when a page is loaded. If it contains the correctly encrypted data, then the user is considered authenticated without having to go through Membership validation again.
What you need to do depends on how you are doing it. If you're storing data in the session, this is bad regardless, because the session can be reset at any time, and session is not connected to authentication. What you need to do, is check to see if the data you need is in the session, and if not, rebuild it. This way it works when you come back later, or if your session is reset.
Session probably shouldn't be used anyways, because it doesn't scale well. A better choice would be to hook into the OnAuthorization method of the Controller class and do what you need there, that way it's done on every page request regardless of what the session may or may not be.
Another option is to create a custom AuthorizationFilter.
I have an ASP.NET application where most of the pages are accessible to all authenticated users via a single sign on module that sets the username into the Session array variable. Now I have one folder A containing one page B.aspx and a list of usernames who are allowed to access this page B.aspx.
My question: how do I elegantly authorize only these users for this one page, or better, for this one folder. Can it be done with the location tag in a Web.config file inside folder A ? If so, how do I connect that config with custom code to check whether the username stored in the session variable is one of the authorized for that folder or page ? Can I use a custom membershipprovider ?
Thanks in advance !
First, you scrap the kludged security methodology, as user name in a session cookie is not a good way to handle this. Okay, maybe a bit too overboard, as low security may be fine for you. If so, you can write a custom handler for the page that examines user name and compares to an updateable list.
NEW: With Session object, you are a bit more security, as the session token is sent over and the name is kept in session, but the Membership bits (below) handle translation of a particular session to a user without rewriting with your custom "this user is using this session" methodology. Yeah, ultimately you can argue Microsoft does something very similar to your software, but you leave the maintenance to them.
Going back to my original direction, there is the concept of roles and membership built into ASP.NET. If you use these bits, you can security trim the page (or even better folder so you can additional pages) to certain users (not as good) or roles (better) by setting up a new web.config with the security constraints.
The cool thing about the built in stuff is you can declaratively set up security and have the pipeline determine whether a user is valid or not without any heavy lifting on your part.
There is plenty of information on Membership and Roles on the various ASP.NET oriented sites.
that can be achieved specifying the user's name that can access the directory separate by commas.
As your username is not defined in web.config rather defined in some session variable you have to create a Form Authentication Ticket for this e.g.
FormsAuthenticationSupport formsAuthenticationSupport = new FormsAuthenticationSupport();
formsAuthenticationSupport.SignIn(UsernameInSession, RoleName, true);
Now you can set authentication rules and location tag in web.config for UsernameInSession.
Currently, I'm using the FOrmsAuthentication.SetAuthCookie to store an Id so I can use it on the next page that is "Authorized" (using custom authorizeattribute controller). But I'm currently thinking of making a custom cookie using httpcookie so I can store more data, or easily maintainable data. Was wondering if having the kind of cookie will I be able to authorize the current user to access the "Authorized" controllers? If so how do I go about it.
Hope that made sense.
Please let me know your thoughts.
Just put your extra stuff in a different cookie. And if forms auth says the user isn't authenticated, don't read the other cookies. No need to overload the purpose of the auth cookie (and non trivial to do so securely)
There is a UserData property of the FormsAuthenticationTicket. It is a string so you will have to be able to serialize/deserialize any complex data.
Good security design says dont store this information in a cookie - figure out another way (server side). Recently (octoberish) the ASP.Net POET vulnerability taught us that forms auth tickets could be forged because the machinekey could be determined and hence data encrypted as it would be on the server. I know - not exactly what you asked but I think it's important to not store sensitive data on the client side.