How to differ sessions in browser-tabs?
Asked Answered
G

20

144

In a web-application implemented in java using JSP and Servlets; if I store information in the user session, this information is shared from all the tabs from the same browser. How to differ sessions in the browser-tabs? In this example:

<%@page language="java"%>
<%
String user = request.getParameter("user");
user = (user == null ? (String)session.getAttribute("SESSIONS_USER") : user);
session.setAttribute("SESSIONS_USER",user);
%>
<html><head></head><body>
<%=user %>
<form method="post">
User:<input name="user" value="">
<input type="submit" value="send">
</form>
</body></html>

Copy this code in a jsp page (testpage.jsp), deploy this file in an existing context of a web application on the server (I use Apache Tomcat), then open a browser (FF, IE7 or Opera) using the correct URL (localhost/context1/testpage.jsp), type your name in the input and submit the form. Then open a new tab in the same browser, and then you can see your name (get from the session) on the new tab. Be careful with the browser-cache, sometimes seems that it doesn't happen, but it's in the cache, refresh the second tab.

Thanks.

Goldsberry answered 15/12, 2008 at 15:10 Comment(5)
related: #4480495Technic
This is a thing the user has to do: Open IE, click on "File->New Session"Croteau
@Quandary, your solution isn't a generic solution (in other browsers doesn't work) and, most important, it's not user friendly (the users don't know about sessions).Goldsberry
Some people seem unable to imagine what the purpose of this is. The problem domain is most any situation in which you want to allow different "views" of your web site. Once the user can have more than one view of your website, they inevitably long (or accidentally try) to access two different views at the same time. Examples include: temporal versioning (switch to viewing website as it existed at a certain point in the past); sandboxing (making changes to website others can't see yet); role-based views (see how website looks to less privileged user); etc.Interchangeable
As of today, there is a simple solution if tabs are in different browser windows, since several browsers now support profiles (see e.g. wired.com/story/…).Loats
P
100

You can use HTML5 SessionStorage (window.sessionStorage). You will generate a random id and save in session Storage per Browser Tab. Then each browser tab has his own Id.

Data stored using sessionStorage do not persist across browser tabs, even if two tabs both contain webpages from the same domain origin. In other words, data inside sessionStorage is confined to not just the domain and directory of the invoking page, but the browser tab in which the page is contained in. Contrast that to session cookies, which do persist data from tab to tab.

Prism answered 2/8, 2012 at 18:39 Comment(8)
From docs: "Data stored using sessionStorage do not persist across browser tabs, even if two tabs both contain webpages from the same domain origin. In other words, data inside sessionStorage is confined to not just the domain and directory of the invoking page, but the browser tab in which the page is contained in. Contrast that to session cookies, which do persist data from tab to tab." Simple testing confirms this behavior in Chrome and FF.Boulevard
Note, the ID will be duplicated when using Chrome's "Duplicate Tab" feature. This generally isn't a problem, but should be thought through.Sherrard
Except when you "Duplicate" a tab in Chrome or Firefox. In this case, SessionStorage is copied.Roydd
@Gonzalo Gallotti: And how will that help me if the session is server-side ? )))Croteau
@StefanSteiger. Then you can send the BrowserTab id (saved in Session Storage) with your Ajax Call. In the server side you will need custom logic, because the WebSession is the Same. But you can create a HashMap with Session objects by tab.Prism
@Gonzalo Gallotti: And where is your ajax call an a form-post (aka postback) ? Besides, I know that. My point is, if you do that, you'll have to make massive changes to each and every byte of code that uses a session. Hundreds of pages, potentially thousands of files. Perhaps libaries that are used in other projects, too. You're not gonna do that, ever. Even though, for a very small project, it's an option. However, if the project were that small, I'd rather completely remove the reliance on any session object, as that is usually an antipattern. (aka a very very very bad thing to do)Croteau
@Gonzalo Gallotti: Using multiple subdomains with a wildcard-dns-entry is most likely a much better alternative, as that requires changing only the login/SAML code. https://mcmap.net/q/158850/-how-to-differ-sessions-in-browser-tabsCroteau
I would disrecommend this because. Normal sessions will use cookies, session cookies can be httpOnly. In the other hand sessionStorage is on client side. This means that your APP will have high consequences if an XSS exploit arises, allowing attackers to easily steal sessions.Cappuccino
M
25

You have to realize that server-side sessions are an artificial add-on to HTTP. Since HTTP is stateless, the server needs to somehow recognize that a request belongs to a particular user it knows and has a session for. There are 2 ways to do this:

  • Cookies. The cleaner and more popular method, but it means that all browser tabs and windows by one user share the session - IMO this is in fact desirable, and I would be very annoyed at a site that made me login for each new tab, since I use tabs very intensively
  • URL rewriting. Any URL on the site has a session ID appended to it. This is more work (you have to do something everywhere you have a site-internal link), but makes it possible to have separate sessions in different tabs, though tabs opened through link will still share the session. It also means the user always has to log in when he comes to your site.

What are you trying to do anyway? Why would you want tabs to have separate sessions? Maybe there's a way to achieve your goal without using sessions at all?

Edit: For testing, other solutions can be found (such as running several browser instances on separate VMs). If one user needs to act in different roles at the same time, then the "role" concept should be handled in the app so that one login can have several roles. You'll have to decide whether this, using URL rewriting, or just living with the current situation is more acceptable, because it's simply not possible to handle browser tabs separately with cookie-based sessions.

Meill answered 15/12, 2008 at 15:40 Comment(2)
It's for a big application that keeps the user information and the envirnoment. When somebody login with differents users in differents tabs, for test or for differents roles, then he's crossing information from tabs.Goldsberry
Just a very late add-on, as to why: because I need to know this in order to know WHICH page on my site, for example, a user is focused on, and not just that they've gone from page to page (instead they went from tab to tab).Peralta
G
16

The window.name Javascript property, is the only thing that will persist across tab activity, but can remain independent (instead of URL guff).

Goldstein answered 20/6, 2011 at 20:51 Comment(3)
window.sessionStorage does as wellDiscontinuity
careful as session storage gets copied into a new tab when the user chooses "open in new tab" ... wish i had link for details but see: bugzilla.mozilla.org/show_bug.cgi?id=818389Discontinuity
Be aware that the thing you save in the window.name setting will still be available in other domains, when the user changes to other pages in the same tab.Resentment
P
15

You shouldn't. If you want to do such a thing either you need to force user to use a single instance of your application by writing URLs on the fly use a sessionID alike (not sessionid it won't work) id and pass it in every URL.

I don't know why you need it but unless you need make a totally unusable application don't do it.

Proximo answered 15/12, 2008 at 15:35 Comment(10)
It's for a big application that keeps the user information and the envirnoment. When somebody login with differents users in differents tabs, for test or for differents roles, then he's crossing information from tabs.Goldsberry
I see as I say this is bad idea. I assume this app will be for a team or something like that, not public to everyone. In that case I'd say running a new Internet Explorer or running a new profile on Firefox can save the day. Other solutions will bite you in the arse for the rest of the project-IMHO.Proximo
Running a new IE as in executing IE again as a new window. This will separate sessions.Proximo
I guess this got downvoted for saying "this is a bad idea", but his answer is correct. Unless you want to pass data through GET or POST with every request, you can't do this. Sessions alone don't cut it, as pretty much every other answer has pointed out.Memberg
Adam, as you see it's not easy to be honest :)Proximo
old answer I know, but google mail allows you to have different accounts per tab and it's not a "totally unusable application"Pegasus
@George, The answer still right, and for your example you will see in the url number represent the index of accounts stored in cookies, google mail is just storing all cookies and select one according to the url.Tonettetoney
Not sure the answer is still right, you clearly can do it. Gmail does it. As for how it does it, it may not be elegant but it works and that's what's importantPegasus
I don't agree on totally unusable application part. Lets say you have a login then looking at different accounts in your application. You don't want to pass an account id parameter each time you do something on each account. So you store currently selected account in session to get it back when you need it. Then also you may open different tabs to look different accounts at the same time. In that case, you have the latest account ID in session, but if you go back another previous tab, the account you see on browser is not same as what server has.Mcgann
Old answer but I may add that Whatsapp and Telegram don't allow you to have two tabs open at the same time. This doesn't make them unusableSophisticate
W
13

I've come up with a new solution, which has a tiny bit of overhead, but seems to be working so far as a prototype. One assumption is that you're in an honour system environment for logging in, although this could be adapted by rerequesting a password whenever you switch tabs.

Use localStorage (or equivalent) and the HTML5 storage event to detect when a new browser tab has switched which user is active. When that happens, create a ghost overlay with a message saying you can't use the current window (or otherwise disable the window temporarily, you might not want it to be this conspicuous.) When the window regains focus, send an AJAX request logging the user back in.

One caveat to this approach: you can't have any normal AJAX calls (i.e., ones that depend on your session) happen in a window that doesn't have the focus (e.g. if you had a call happening after a delay), unless you manually make an AJAX re-login call before that. So really all you need do is have your AJAX function check first to make sure localStorage.currently_logged_in_user_id === window.yourAppNameSpace.user_id, and if not, log in first via AJAX.

Another is race conditions: if you can switch windows fast enough to confuse it, you may end up with a relogin1->relogin2->ajax1->ajax2 sequence, with ajax1 being made under the wrong session. Work around this by pushing login AJAX requests onto an array, and then onstorage and before issuing a new login request, abort all current requests.

The last gotcha to look out for is window refreshes. If someone refreshes the window while you've got an AJAX login request active but not completed, it'll be refreshed in the name of the wrong person. In this case you can use the nonstandard beforeunload event to warn the user about the potential mixup and ask them to click Cancel, meanwhile reissuing an AJAX login request. Then the only way they can botch it is by clicking OK before the request completes (or by accidentally hitting enter/spacebar, because OK is--unfortunately for this case--the default.) There are other ways to handle this case, like detecting F5 and Ctrl+R/Alt+R presses, which will work in most cases but could be thwarted by user keyboard shortcut reconfiguration or alternative OS use. However, this is a bit of an edge case in reality, and the worst case scenarios are never that bad: in an honour system configuration, you'd be logged in as the wrong person (but you can make it obvious that this is the case by personalizing pages with colours, styles, prominently displayed names, etc.); in a password configuration, the onus is on the last person who entered their password to have logged out or shared their session, or if this person is actually the current user, then there's no breach.

But in the end you have a one-user-per-tab application that (hopefully) just acts as it should, without having to necessarily set up profiles, use IE, or rewrite URLs. Make sure you make it obvious in each tab who is logged into that particular tab, though...

Winwaloe answered 15/7, 2009 at 20:53 Comment(1)
+1 because you took the time to really think this through! :-) Looking at all the caveats you just know it's a bad idea. My lessons learned from this stuff is to truly understand what data should go into sessions and which data shouldn't.Pavo
G
10

I'll be honest here. . .everything above may or may not be true, but it all seems WAY too complicated, or doesn't address knowing what tab is being used server side.

Sometimes we need to apply Occam's razor.

Here's the Occam's approach: (no, I'm not Occam, he died in 1347)

  1. assign a browser unique id to your page on load. If, and only if, the window doesn't have an id yet (so use a prefix and a detection)

  2. on every page you have (use a global file or something) simply put code in place to detect the focus event and/or mouseover event. (I'll use jquery for this part, for ease of code writing)

  3. in your focus (and/or mouseover) function, set a cookie with the window.name in it

  4. read that cookie value from your server side when you need to read/write tab specific data.

Client side:

//Events 
$(window).ready(function() {generateWindowID()});
$(window).focus(function() {setAppId()});
$(window).mouseover(function() {setAppId()});


function generateWindowID()
{
    //first see if the name is already set, if not, set it.
    if (se_appframe().name.indexOf("SEAppId") == -1){
            "window.name = 'SEAppId' + (new Date()).getTime()
    }
    setAppId()
}

function setAppId()
{
    //generate the cookie
    strCookie = 'seAppId=' + se_appframe().name + ';';
    strCookie += ' path=/';

    if (window.location.protocol.toLowerCase() == 'https:'){
        strCookie += ' secure;';
    }

    document.cookie = strCookie;
}

server side (C# - for example purposes)

//variable name 
string varname = "";
HttpCookie aCookie = Request.Cookies["seAppId"];
if(aCookie != null) {
     varname  = Request.Cookies["seAppId"].Value + "_";
}
varname += "_mySessionVariable";

//write session data 
Session[varname] = "ABC123";

//readsession data 
String myVariable = Session[varname];

Done.

Gran answered 15/10, 2013 at 14:42 Comment(6)
I really like your simplified answer and Occam's razor reference. Just wanted to double check that this solution is still working for you.Balm
It worths noticing that if refreshing the page should keep its session this approach would not work. The other suggested approach in another answer of using window.sessionStorage seems more reasonable to me.Dissimulate
@quijibo Still works perfectly in my application, yes. As for refreshing the page, may be true for some browsers, using the window.sessionStorage may solve the issue, haven't tried itGran
What is the function "se_appframe()"?Krystin
What is se_appframe ?Whistle
se_appframe() is a function i have that walks up the framework (iframes, parents and such) and finds the topmost frame for my applicaiton. It's an old beast that has framesets. We're re-writing the beast to an SPA, but for now, we're stuck in framesets. in most cases you're just looking for somethign like window.topmostGran
D
9

We had this problem and we solved it very easy. I mean easy because no programming involved. What we wanted to do was to let a user login to multiple account within same browser window without conflicting the sessions.

So the solution was random subdomains.

23423.abc.com
242234.abc.com
235643.abc.com

So we asked our system admin to configure the SSL certificates for *.abc.com rather abc.com Then with little code change, every time a user try to login, he gets logged in a tab with a random subdomain number. so each tab could have its own session independently. Also to avoid any conflict, we developed the random number using a hash or md5 of user id.

Drawknife answered 15/4, 2014 at 6:17 Comment(3)
This seems like a clever alternative to URL-rewriting. You end up with desired variable (session ID) in an HTTP-conformant place that's visible but not obtrusive. Nits that spring to mind: 1) need wildcard on DNS, obviously, 2) your entire website must avoid any absolute URLs that would route the user back to (e.g.) the "www" subdomain, 3) probably want to keep web crawlers out, but this is probably a private-web situation so that may be done already. Thanks for sharing this!Interchangeable
@user3534653: I like the approach. But you said "no programming involved". Then you go on to say "with little code change". So really, a little code change is not programming ? ;) Also, this approach might not work if you have multiple applications with canonical links where the domain is hard-coded/configured (canonically).Croteau
You may already have thought of this but... what is to stop someone from guessing the subdomain. I hope there is more to it than just a random subdomain. Would take a few seconds to find all the subdomains that are active, and then view the page (unless there was other code to keep that from happening). Isn't that kinda like session "hijacking" but in this case it would be subdomain "hacking". :) I'm sure you have more code to protect that... can't rely on anonymity for security.Binate
B
2

You can use link-rewriting to append a unique identifier to all your URLs when starting at a single page (e.g. index.html/jsp/whatever). The browser will use the same cookies for all your tabs so everything you put in cookies will not be unique.

Burgos answered 15/12, 2008 at 15:15 Comment(1)
I think rewriting links to encode sessions is a tedious and error-prone. I have a big application, with a lot of links, I need a transparent solution or easy to implement.Goldsberry
R
2

I think what you probably want is to maintain navigation state across tabs and not specifically creating a single session per tab. This is exactly what the Seam framework achieves with their Conversation scope/context. Their implementation relies on the fact that a conversation id is propagated with each request and creates the notion of a conversation on the server side, which is something that lies between a session and a request. It allows for navigation flow control and state management.

Although that's mainly aimed at JSF, have a look and check if that's something where you can take some ideas from: http://docs.jboss.org/seam/latest/reference/en-US/html_single/#d0e3620

Randellrandene answered 2/8, 2012 at 18:51 Comment(0)
R
2

In javascript, how can I uniquely identify one browser window from another which are under the same cookiedbased sessionId

Essentially use window.name. If its not set, set it to a unique value and use it. It will be different across tabs that belong to same session.

Rochet answered 5/10, 2012 at 7:9 Comment(0)
S
2

Note: The solution here needs to be done at application design stage. It would be difficult to engineer this in later.

Use a hidden field to pass around the session identifier.

For this to work each page must include a form:

<form method="post" action="/handler">

  <input type="hidden" name="sessionId" value="123456890123456890ABCDEF01" />
  <input type="hidden" name="action" value="" />

</form>

Every action on your side, including navigation, POSTs the form back (setting the action as appropriate). For "unsafe" requests, you could include another parameter, say containing a JSON value of the data to be submitted:

<input type="hidden" name="action" value="completeCheckout" />
<input type="hidden" name="data" value='{ "cardNumber" : "4111111111111111", ... ' />

As there are no cookies, each tab will be independent and will have no knowledge of other sessions in the same browser.

Lots of advantages, particularly when it comes to security:

  • No reliance on JavaScript or HTML5.
  • Inherently protects against CSRF.
  • No reliance on cookies, so protects against POODLE.
  • Not vulnerable to session fixation.
  • Can prevent back button use, which is desirable when you want users to follow a set path through your site (which means logic bugs that can sometimes be attacked by out-of-order requests, can be prevented).

Some disadvantages:

  • Back button functionality may be desired.
  • Not very effective with caching as every action is a POST.

Further information here.

Sharecrop answered 8/6, 2015 at 14:31 Comment(0)
K
1

Another approach that works is to create a unique window id and store this value along with the session id in a database table. The window id I often use is integer(now). This value is created when a window is opened and re-assigned to the same window if the window is refreshed, reloaded or submitted to itself. Window values (inputs) are saved in the local table using the link. When a value is required, it is obtained from the database table based on the window id / session id link. While this approach requires a local database, it is virtually foolproof. The use of a database table was easy for me, but I see no reason why local arrays would not work just as well.

Kiersten answered 15/12, 2008 at 15:11 Comment(0)
Y
1

Spring Session supports multiple session in same browser Look at the samples and implementation detail http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html

Yi answered 20/2, 2015 at 7:45 Comment(0)
F
1

I resolved this of following way:

  • I've assigned a name to window this name is the same of connection resource.
  • plus 1 to rid stored in cookie for attach connection.
  • I've created a function to capture all xmloutput response and assign sid and rid to cookie in json format. I do this for each window.name.

here the code:

var deferred = $q.defer(),
        self = this,
        onConnect = function(status){
          if (status === Strophe.Status.CONNECTING) {
            deferred.notify({status: 'connecting'});
          } else if (status === Strophe.Status.CONNFAIL) {
            self.connected = false;
            deferred.notify({status: 'fail'});
          } else if (status === Strophe.Status.DISCONNECTING) {
            deferred.notify({status: 'disconnecting'});
          } else if (status === Strophe.Status.DISCONNECTED) {
            self.connected = false;
            deferred.notify({status: 'disconnected'});
          } else if (status === Strophe.Status.CONNECTED) {
            self.connection.send($pres().tree());
            self.connected = true;
            deferred.resolve({status: 'connected'});
          } else if (status === Strophe.Status.ATTACHED) {
            deferred.resolve({status: 'attached'});
            self.connected = true;
          }
        },
        output = function(data){
          if (self.connected){
            var rid = $(data).attr('rid'),
                sid = $(data).attr('sid'),
                storage = {};

            if (localStorageService.cookie.get('day_bind')){
              storage = localStorageService.cookie.get('day_bind');
            }else{
              storage = {};
            }
            storage[$window.name] = sid + '-' + rid;
            localStorageService.cookie.set('day_bind', angular.toJson(storage));
          }
        };
    if ($window.name){
      var storage = localStorageService.cookie.get('day_bind'),
          value = storage[$window.name].split('-')
          sid = value[0],
          rid = value[1];
      self.connection = new Strophe.Connection(BoshService);
      self.connection.xmlOutput = output;
      self.connection.attach('bosh@' + BoshDomain + '/' + $window.name, sid, parseInt(rid, 10) + 1, onConnect);
    }else{
      $window.name = 'web_' + (new Date()).getTime();
      self.connection = new Strophe.Connection(BoshService);
      self.connection.xmlOutput = output;
      self.connection.connect('bosh@' + BoshDomain + '/' + $window.name, '123456', onConnect);
    }

I hope help you

Formalize answered 8/12, 2015 at 19:30 Comment(0)
M
0
How to differ sessions in browser-tabs?

The most straightforward way to differ sessions in browser tabs is to disallow your particular domain to set cookies. That way, you can have separate sessions from separate tabs. Say you disallow cookies from this domain: www.xyz.com. You open Tab 1, login and start browsing. Then you open Tab 2, and you can login either as a same user or a different one; either way, you will have a session separate from Tab 1. And so on.

But of course this is possible when you have control over the client side. Otherwise, the solutions prescribed by the folks here should apply.

Macle answered 15/12, 2008 at 15:11 Comment(0)
P
0

I've been reading this post because I thought I wanted to do the same thing. I have a similar situation for an application I'm working on. And really it's a matter of testing more than practicality.

After reading these answers, especially the one given by Michael Borgwardt, I realized the work flow that needs to exist:

  1. If the user navigates to the login screen, check for an existing session. If one exists bypass the login screen and send them to the welcome screen.
  2. If the user (in my case) navigates to the enrollment screen, check for an existing session. If one exists, let the user know you're going to log that session out. If they agree, log out, and begin enrollment.

This will solve the problem of user's seeing "another user's" data in their session. They aren't really seeing "another user's" data in their session, they're really seeing the data from the only session they have open. Clearly this causes for some interesting data as some operations overwrite some session data and not others so you have a combination of data in that single session.

Now, to address the testing issue. The only viable approach would be to leverage Preprocessor Directives to determine if cookie-less sessions should be used. See, by building in a specific configuration for a specific environment I'm able to make some assumptions about the environment and what it's used for. This would allow me to technically have two users logged in at the same time and the tester could test multiple scenarios from the same browser session without ever logging out of any of those server sessions.

However, this approach has some serious caveats. Not least of which is the fact that what the tester is testing is not what's going to run in production.

So I think I've got to say, this is ultimately a bad idea.

Princess answered 18/7, 2013 at 17:40 Comment(0)
L
0

Storing the timeStamp in window.sessionStorage if it is not already set. This will give a unique value for each tab(even if the URLs are same)

http://www.javascriptkit.com/javatutors/domstorage.shtml

https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage

Hope this helps.

Lugworm answered 5/10, 2013 at 20:54 Comment(1)
This is probably okay, but is theoretically unsafe. Some operating systems (e.g. Windows) have a very coarse timestamp granularity which means you might look up the timestamp multiple times and get back the same value. Furthermore, if you reopen a browser after a crash and it reopens all tabs at once they might get the same timestamp.Cown
T
0

you will need to do

1- store a cookie for accounts list

2- optional store a cookie for default one

3- store for each account with it's index like acc1, acc2

4- put in the url something represent the index of accounts and if not you will select the default one like google mail domain.com/0/some-url >> 0 here represent the index of account also you may need to know how to use urlwrite

5- when select a cookie, select it according to your urlpath represent the account index

Regards

Tonettetoney answered 25/10, 2014 at 1:2 Comment(0)
Y
0

I see many implementations which have client side changes to manipulate session id cookies. But in general session id cookies should be HttpOnly so java-script cannot access otherwise it may lead to Session Hijack thru XSS

Yi answered 16/3, 2015 at 5:23 Comment(0)
G
0

If it's because each tab will be running a different flow in your application, and mixing both flows causes problems, then it's better to "Regionalize" your session objects, so that each flow will use a different region of the session

This region can be implemented as simply as having different prefixes for each flow, or session object will hold multiple maps (one for each flow), and you use those maps instead of session attributes, the best though would be to extend your session class and use it instead.

Grummet answered 19/4, 2016 at 8:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.