Binding tab-specific data to an HTTP GET request
Okay, after going through many different iterations, here is the implementation we ended up with:
Variables
- There are two kind of data stores:
- IndexedDB, which is shared across all tabs.
- sessionStorage which is unique per tab.
- We store the following variables:
- IndexedDB contains
publicToken
,nextTabId
. - sessionStorage contains
privateToken
,tabId
.
- IndexedDB contains
publicToken, privateToken
- See https://stackoverflow.com/a/1592572/14731 for a definition of an authentication token.
- There are two kinds of authentication tokens: public and private.
publicToken
is the token returned by the last login operation, across all tabs.privateToken
is the token returned by the last login operation of the current tab.
tabId
- Each tab is uniquely identified by a token called
tabId
. nextTabId
is a number that is accessible across all tabs.- If a tab does not have an id, it creates a new one based on
nextTabId
and increments its value. - For example,
tabId
could have a value of "com.company.TabX
" whereX
is the number returned bynextTabId
.
Login/Logout
- Every time a tab logs in,
privateToken
andpublicToken
are overwritten using the authentication token returned by the server. - When a user logs out, we delete
privateToken
andpublicToken
on the browser side, andprivateToken
on the server side. We do not deletepublicToken
on the server side. - This means that anytime a tab logs out, all all tabs sharing the same
privateToken
will get logged out as well. Any tabs using a different token will be unaffected. - When do multiple tabs share the same
privateToken
? When you open a link in a new window or tab, it inherits theprivateToken
of the parent tab. - If we were to delete
publicToken
on the server, a tab logging out withprivateToken
X,publicToken
Y would cause tabs withprivateToken
Y to get logged out (which is undesirable).
On page load
- Scan the page for HTML links.
- For each link, append a
tabId
query parameter to the URL. The parameter value is equal to the value oftabId
. - Strip the
tabId
URL parameter from the current page using history.replaceState() so users can share links with their friends (tabId
is user-specific and cannot be shared). - Delete the
tabId
cookie (more on this below).
When a link is clicked
- The tab creates a
tabId
cookie and follows the link. - The cookie has a name equal to the value of
tabId
and a value equal to the value ofprivateToken
When a server receives a request
- If the
tabId
parameter is missing, then redirect the browser toGetTabId.html?referer=X
whereX
is the current URL. - If
tabId
is present but the authentication token is invalid or expired, then redirect the browser to the login screen.
GetTabId.html
- If the tab does not have a
privateToken
, copypublicToken
intoprivateToken
. - If both
privateToken
andpublicToken
are undefined, redirect to the login page. - The page takes a URL parameter called
referer
which indicates where to redirect to on success. - If the tab has a
privateToken
, append thetabId
parameter to thereferer
page and redirect back to it. - Use
window.location.replace()
when redirecting to removeGetTabId.html
from the browser history.
Why do we keep on deleting/adding cookies?
- We try to minimize the number of cookies sent to the server on each request.
- If we did not delete the
tabId
cookie on page load, then each time a tab would make a request all of the other tabs' cookies would get sent as well.
Known issues
- "View Source" opens the URL missing
tabId
. As result, it gets the source-code of the page which redirects toGetTabId.html
instead of the actual page. - Awkwardly long page reloads (client is redirected to
GetTabId.html
and back to the original page).
Apologies for the long implementation details, but I could not find an easier/shorter solution.