In my website i have implemented custom session values. In which, on log on i set the session value to some object. This object is used to extract user specific data from db.
now the problem is If user logs in with : test1.somesite.com and logs off and again logs in with: test2.somesite.com that user is still receiving the data from object specific to test1.somesite.com.
the point is whichever site user first logs in with the second time if he logs in with another subdomain he is always getting the data from previous sub domain login.
on log out from specific domain i cleared all the sessions(tried everything): by putting HttpContext.session["UserDetail"] = null;, HttpContext.Session.Abandon() and also HttpContext.Session.Clear();
but nothing seems to work
and i also don't have much idea how session variables are treated across subdomains.
I mean if i initialize a session with one value by visiting test1.somesite.com will that value also be visible if on the same computer and on same browser i also open test2.somesite.com.
any help please
Sounds like a cookie issue. Try clearing out the session cookie. Something along the lines of:
if (!User.Identity.IsAuthenticated)
{
if (Request.Cookies["ASP.NET_SessionId"] != null)
{
Response.Cookies["ASP.NET_SessionId"].Expires = DateTime.Now.AddDays(-1);
}
Session.Abandon();
}
What you need to do is set your asp.net session id cookie to write to a wild card domain.
Response.Cookies("ASP.NET_SessionId").Domain = "yourdomain.com" // or "*.yourdomain.com"
So that the cookie can float to your subdomains.
Related
I have an asp.net login web form that have ( username textBox - password textBox ) plus Remember Me CheckBox option
When user login i do the below code
if (provider.ValidateUser(username, password))
{
int timeOut = 0x13;
DateTime expireDate = DateTime.Now.AddMinutes(19.0);
if (rememberMeCheckBox.Checked)
{
timeOut = 0x80520;
expireDate = DateTime.Now.AddYears(1);
}
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(username, true, timeOut);
string cookieValue = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue);
cookie.Expires = expireDate;
HttpContext.Current.Response.Cookies.Add(cookie);
AddForLogin(username);
Response.Redirect("...");
}
as in code after user is authenticated i log that he login in db by calling method AddForLogin(username);
But if user choose remember me in login and then he try to go to site any time this login method isn't executed as it use cookies ... so i have many questions:
1- Is this the best way to log login operation or is there any other better ?
2- In my case how to log login operation in case of remember me chosen by user ?
The best way to implement login operation is to implement your login design.It seems in your code,you're using built-in functionality plus logging custom information.
In order to log login information you can customize your Provider.Indeed there will be some db changes around,but you can handle them by stored procedures with no change in entire code.For example,create a new table that holds as many information as you wish including login operation(i mean how user is logged in,is it first time or is user already chosen remember me option so that user logged in by cookie)
Best Regards
Myra
Currently you do not have enough info to distinguish the two cases. I think that the easiest way to do what you want would be to add a temp cookie when the user logs in and then in you page class, if the temp cookie is not present, then they are using the remember me option. Then you can log this and set the temp cookie.
What I ended up doing in one application I used was test in Session_Start() if they were logged in, and do the logging there. I found that this did capture people whose login was successful due to their login cookie still being valid (so they didn't need to explicitly log in)
You'll have to test what effect this has on regular log ins to assure you aren't logging them twice, but that's simple enough to figure out. My app actually excludes logging logins when the last login was within 5 minutes, but that's just to limit how many records are there, too.
Here's my opinion after read through all the other answers.
Below is my idea with performance consideration in mind:
Create a login logging interval settings, say 12 hours or 24 hours (your choice). You can store this settings in web.config appSettings. The smaller the value, the higher performance hit.
When users login successfully at the login page manually, log it to your datastore and set a cookie with the current datetime (Last logged login datetime).
Whenever users make a page request, compare the difference between the last logged login datetime stored in the cookie and the current request datetime. If the difference is less than the interval specified in the settings, do not log it. If it is larger, refresh the cookie value to the current datetime and log the login to your datastore. This step is to reduce performance hit.
If I understand what you are hoping to accomplish, maybe FormsAuthentication_OnAuthenticate in your application class (global.asax.cs) would be the right place to log the login?
I'm creating an HttpCookie, setting only the name and value and not the expires property, then adding it to the response. Simple enough. The cookie is created (but not persisted) as expected. The problem is when the session changes for some reason (like the website was rebuilt, or I rebuilt my app when debugging) then the cookie stays around. I want the cookie to be valid for only the original session it was created on.
According to MSDN it says: "If you do not specify an expiration limit for the cookie, the cookie is not persisted to the client computer and it expires when the user session expires."
I guess I don't know exactly what "session expires" encompasses. I figure the cookie gets deleted after 20 min when the session expires. But should the cookie get deleted if the session it was created on doesn't exist anymore for any number of reasons? The only time I've seen the cookie get deleted is when the user closes all browser windows and opens a new one.
If this is all true, I may have to store the original session id ("ASP.NET_SessionId") in the cookie, then check it against the current session id, if they're different, then delete the cookie or create a new one.
Here's the code (the only difference between my cookie and the one in the MSDN examples is I'm storing multiple values in the cookie):
private void SaveValuesToCookie(string[] names, string[] values)
{
HttpCookie cookie = new HttpCookie("MyCookie");
for (int i = 0; i < names.Length; i++)
{
string name = names[i];
cookie.Values[name] = values[i];
}
Response.Cookies.Add(cookie);
}
private string GetValueFromCookie(string name)
{
HttpCookie cookie = Request.Cookies["MyCookie"];
if (cookie == null)
return null;
return cookie.Values[name];
}
The only time I've seen the cookie get
deleted is when the user closes all
browser windows and opens a new one.
And that is exactly what MSDN means when it says the cookie will be deleted when the session expires. Unfortunately, I believe this isn't consistant across browsers anyway, so it's not much use to anyone.
You should always set an expiry date on Cookies.
If this is all true, I may have to
store the original session id
("ASP.NET_SessionId") in the cookie,
then check it against the current
session id, if they're different, then
delete the cookie or create a new one.
I hate to say it but this isn't going to help you either. The .NET Framework likes to recycle session IDs, so you can't guarantee it will be different.
Bad news out of the way, I would advise you to reconsider what you're trying to do from an architectural standpoint.
Restarting the app is something that happens entirely on the server; cookies are something that happen entirely on the client. While the client will talk to the server, it is purely a Request/Response relationship, the server cannot communicate events such as an application restart to the browser.
If you want to store a value somewhere which is only valid for the lifespan of a server session, why not store it in Session rather than in a Cookie?
I'm aware of all that, but it's not secure enough for our clients needs. The session can still get spoofed or injected. See: http://msdn.microsoft.com/en-us/magazine/cc163730.aspx#S9
Looks like I'm left to creating an expired secure cookie separate of the session, and refreshing the expiration date when I can (ie when the user accesses certain pages).
Why does the property SessionID on the Session-object in an ASP.NET-page change between requests?
I have a page like this:
...
<div>
SessionID: <%= SessionID %>
</div>
...
And the output keeps changing every time I hit F5, independent of browser.
This is the reason
When using cookie-based session state, ASP.NET does not allocate storage for session data until the Session object is used. As a result, a new session ID is generated for each page request until the session object is accessed. If your application requires a static session ID for the entire session, you can either implement the Session_Start method in the application's Global.asax file and store data in the Session object to fix the session ID, or you can use code in another part of your application to explicitly store data in the Session object.
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.sessionid.aspx
So basically, unless you access your session object on the backend, a new sessionId will be generated with each request
EDIT
This code must be added on the file Global.asax. It adds an entry to the Session object so you fix the session until it expires.
protected void Session_Start(Object sender, EventArgs e)
{
Session["init"] = 0;
}
There is another, more insidious reason, why this may occur even when the Session object has been initialized as demonstrated by Cladudio.
In the Web.config, if there is an <httpCookies> entry that is set to requireSSL="true" but you are not actually using HTTPS: for a specific request, then the session cookie is not sent (or maybe not returned, I'm not sure which) which means that you end up with a brand new session for each request.
I found this one the hard way, spending several hours going back and forth between several commits in my source control, until I found what specific change had broken my application.
In my case I figured out that the session cookie had a domain that included www. prefix, while I was requesting page with no www..
Adding www. to the URL immediately fixed the problem. Later I changed cookie's domain to be set to .mysite.com instead of www.mysite.com.
my problem was that we had this set in web.config
<httpCookies httpOnlyCookies="true" requireSSL="true" />
this means that when debugging in non-SSL (the default), the auth cookie would not get sent back to the server. this would mean that the server would send a new auth cookie (with a new session) for every request back to the client.
the fix is to either set requiressl to false in web.config and true in web.release.config or turn on SSL while debugging:
Using Neville's answer (deleting requireSSL = true, in web.config) and slightly modifying Joel Etherton's code, here is the code that should handle a site that runs in both SSL mode and non SSL mode, depending on the user and the page (I am jumping back into code and haven't tested it on SSL yet, but expect it should work - will be too busy later to get back to this, so here it is:
if (HttpContext.Current.Response.Cookies.Count > 0)
{
foreach (string s in HttpContext.Current.Response.Cookies.AllKeys)
{
if (s == FormsAuthentication.FormsCookieName || s.ToLower() == "asp.net_sessionid")
{
HttpContext.Current.Response.Cookies[s].Secure = HttpContext.Current.Request.IsSecureConnection;
}
}
}
Another possibility that causes the SessionID to change between requests, even when Session_OnStart is defined and/or a Session has been initialized, is that the URL hostname contains an invalid character (such as an underscore). I believe this is IE specific (not verified), but if your URL is, say, http://server_name/app, then IE will block all cookies and your session information will not be accessible between requests.
In fact, each request will spin up a separate session on the server, so if your page contains multiple images, script tags, etc., then each of those GET requests will result in a different session on the server.
Further information: http://support.microsoft.com/kb/316112
My issue was with a Microsoft MediaRoom IPTV application. It turns out that MPF MRML applications don't support cookies; changing to use cookieless sessions in the web.config solved my issue
<sessionState cookieless="true" />
Here's a REALLY old article about it:
Cookieless ASP.NET
in my case it was because I was modifying session after redirecting from a gateway in an external application, so because I was using IP instead on localhost in that page url it was actually considered different website with different sessions.
In summary
pay more attention if you are debugging a hosted application on IIS instead of IIS express and mixing your machine http://Ip and http://localhost in various pages
In my case this was happening a lot in my development and test environments. After trying all of the above solutions without any success I found that I was able to fix this problem by deleting all session cookies. The web developer extension makes this very easy to do. I mostly use Firefox for testing and development, but this also happened while testing in Chrome. The fix also worked in Chrome.
I haven't had to do this yet in the production environment and have not received any reports of people not being able to log in. This also only seemed to happen after making the session cookies to be secure. It never happened in the past when they were not secure.
Update: this only started happening after we changed the session cookie to make it secure. I've determined that the exact issue was caused by there being two or more session cookies in the browser with the same path and domain. The one that was always the problem was the one that had an empty or null value. After deleting that particular cookie the issue was resolved. I've also added code in Global.asax.cs Sessin_Start method to check for this empty cookie and if so set it's expiration date to something in the past.
HttpCookieCollection cookies = Response.Cookies;
for (int i = 0; i < cookies.Count; i++)
{
HttpCookie cookie = cookies.Get(i);
if (cookie != null)
{
if ((cookie.Name == "ASP.NET_SessionId" || cookie.Name == "ASP.NET_SessionID") && String.IsNullOrEmpty(cookie.Value))
{
//Try resetting the expiration date of the session cookie to something in the past and/or deleting it.
//Reset the expiration time of the cookie to one hour, one minute and one second in the past
if (Response.Cookies[cookie.Name] != null)
Response.Cookies[cookie.Name].Expires = DateTime.Today.Subtract(new TimeSpan(1, 1, 1));
}
}
}
This was changing for me beginning with .NET 4.7.2 and it was due to the SameSite property on the session cookie. See here for more info: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
The default value changed to "Lax" and started breaking things. I changed it to "None" and things worked as expected.
Be sure that you do not have a session timeout that is very short, and also make sure that if you are using cookie based sessions that you are accepting the session.
The FireFox webDeveloperToolbar is helpful at times like this as you can see the cookies set for your application.
Session ID resetting may have many causes. However any mentioned above doesn't relate to my problem. So I'll describe it for future reference.
In my case a new session created on each request resulted in infinite redirect loop. The redirect action takes place in OnActionExecuting event.
Also I've been clearing all http headers (also in OnActionExecuting event using Response.ClearHeaders method) in order to prevent caching sites on client side. But that method clears all headers including informations about user's session, and consequently all data in Temp storage (which I was using later in program). So even setting new session in Session_Start event didn't help.
To resolve my problem I ensured not to remove the headers when a redirection occurs.
Hope it helps someone.
I ran into this issue a different way. The controllers that had this attribute [SessionState(SessionStateBehavior.ReadOnly)] were reading from a different session even though I had set a value in the original session upon app startup. I was adding the session value via the _layout.cshtml (maybe not the best idea?)
It was clearly the ReadOnly causing the issue because when I removed the attribute, the original session (and SessionId) would stay in tact. Using Claudio's/Microsoft's solution fixed it.
I'm on .NET Core 2.1 and I'm well aware that the question isn't about Core. Yet the internet is lacking and Google brought me here so hoping to save someone a few hours.
Startup.cs
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder
.WithOrigins("http://localhost:3000") // important
.AllowCredentials() // important
.AllowAnyMethod()
.AllowAnyHeader(); // obviously just for testing
}));
client.js
const resp = await fetch("https://localhost:5001/api/user", {
method: 'POST',
credentials: 'include', // important
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
Controllers/LoginController.cs
namespace WebServer.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
[HttpPost]
public IEnumerable<string> Post([FromBody]LoginForm lf)
{
string prevUsername = HttpContext.Session.GetString("username");
Console.WriteLine("Previous username: " + prevUsername);
HttpContext.Session.SetString("username", lf.username);
return new string[] { lf.username, lf.password };
}
}
}
Notice that the session writing and reading works, yet no cookies seem to be passed to the browser. At least I couldn't find a "Set-Cookie" header anywhere.
I have an app with multiple subdomains, subone.parent.com, subtwo.parent.com.
I have a logon page at parent.com/login. When a user logs in I redirect them to the proper domain based on which one they are a member of. This works fine.
FormsAuthenticationTicket ticket = new FormsAuth...
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
cookie.Domain = subone.parent.com
Response.Cookies.Add(cookie)
This properly authenticates the user for subone.parent.com and not subtwo.parent.com. However I would like to do the following.
If the user goes back to parent.com, I would like to know that they are logged in and redirect them back to subone.parent.com.
Is there a best practice for accomplishing this? Or do I have to set another cookie for parent.com?
I'm working in asp.net mvc if it matters.
THanks!
You can share cookies across domains like you are trying to do, but its not straight forward, example here.
Another options is to set the cookie to be ".parent.com" rather than specifying the sub-domain explicitly and use the cookie store the details of the sub-domain. Then you can access the cookie from any of your sub-domains (and parent assuming its www.parent.com).
If your using MVC, you can pretty easily create a custom filter and add to the www.parent.com controllers to check for the existence of the cookie, and if so redirect to the sub domain the cookie specifies. Further details of filters here.
I would set the cookie for the explicit domain as you have there because that maintains any security information within that specific domain's cookie. You can also add a non-encrypted cookie at the *.parent.com level that holds information about which domains have been authenticated. There is no real way to tie this together though without using maybe timestamps and having a logical connection between the applications (ie - sub2 has a session timeout of 20 minutes so if the domain + valid timestamp occur in the parent cookie it would be valid, however this is business logic).
I'm not sure the reasoning behind the disconnect between domains, but you might actually prefer to have a single cookie that has encrypted text behind encrypted text. EG:
1) Sub1 logs in, sets the parent.com cookie as valid. Sends a piece of user data to an authentication web service.
2) The authentication service recognizes sub1 as the sender, encrypts the user data, and adds it to a custom cookie object.
3) The custom cookie object constructs a composite string on a unique split character (or sequence) and makes it available to the service method.
4) The service, using the forms encryption, encrypts the entire ticket and sends it back to the original login.
That way each server would be able to unencrypt the global ticket, but each piece of data would be encrypted using a common algorithm but a server based salt. So if sub2 attempts to read the cookie data from sub1, it gets the encrypted version rather than raw data.
you could share the same session on all subdomains. That is the code we use to accomplish that :-)
void MasterPage_Unload(object sender, EventArgs e)
{
///ASP.NET uses one cookie per subdomain/domain,
///we need one cookie for _all_ subdomains.
if (Context.Response.Cookies["ASP.NET_SessionId"] == null)
return;
var sessionCookie = new HttpCookie("ASP.NET_SessionId", Context.Session.SessionID);
sessionCookie.Domain = ".yourdomain.com" ;
Context.Response.SetCookie(sessionCookie);
}
inside the Page_Load method is:
Unload += MasterPage_Unload;
it works great :-)
robert
Looking for a robust and efficient implementation where I can restrict a user to only log in to my web app from a single computer.
If a the same user is already logged in and another person tries to log in from another computer then I should either have the option to
end the session of the currently logged in user, or
show a message to the new person trying to log in with the same user account.
Currently authentication is done using Forms Authentication with custom membership and role providers.
Our server is showing it's age so I'm looking for something that uses the least processing power and hopefully does very few db reads (if at all needed). My initial naive implementation is to store the IP (in db? app state?) on a successful login and then check on each page request or each other log in attempt depending on scenario. Would like to hear of better ideas =)
You can do it this way:
Store the current Session Id (HttpContext.Current.Session.SessionID) in the Application object, along with a time stamp.
At the next request (e.g. in Global.asax), check if the current session is the same as before and if less than 20 minutes have passed. If the session is the same, let them work normally. If they are different, only let them work if 20 minutes have passed. Do update the Application object.
This will allow one user at a time, on one computer at a time. It is probably not 100% safe, but it is a very viable way to do it.
Earlier i got a similar situation, and followed the below appraoch
Along with login name maintain a session id and timestamp in each request.
And allow the user to gain access only if both login & session id combination are same.
If the combination differs,you can either
log off the first logged in user (by
showing notification to them
saying the some other user logged into your
account ) 0r
log off the newly enterd user saying already this
account is in use
you can use timestamp of the request to validate the session timeouts..
Hi
It's the way I've implemented this (I've added these lines to onload of my Page's base class):
bool authenticated = UserIsAuthenticated();
if (Session["~~~loginprocessed"] == null)
{
if (authenticated)
{
// user just logged in:
if (Application[Page.User.Identity.Name] != null)
Application.Remove(Page.User.Identity.Name);
Application.Add(Page.User.Identity.Name, Session.SessionID);
Session.Add("~~~loginprocessed", true);
}
}
else
{
if (!authenticated)
{
Session.Remove("~~~loginprocessed");
}
}
if (authenticated)
{
if ((Application[Page.User.Identity.Name] == null) || (Application[Page.User.Identity.Name].ToString() != Session.SessionID))
{
System.Web.Security.FormsAuthentication.SignOut();
// show some message to user which will tell he has signed out because of login from somewhere else!
}
}
If I understand your question correctly, you wish to make sure each user can only be logged on once at a given time. As far as I know the ASP.NET Membership provider only stores the last activity date and the last login date. You could keep a list of user id's that are logged in and display your message when a new user tries to login as a user that is already in that list. A complication would be that you need to remove the user from this 'LoggedOn' list when he logs out. You can perhaps use session_end in Global.asax