28

In our web app, If I use a single browser, login to our application as user A, open another tab and login as user B - User A loses his session data. I assume this is due to a shared cookie made out with the user-agent. Is there a way to concat its name with a username? so that sessions can co-exist between concurrent logged in users using the same browser on the same machine?

We use Laravel 5. Is there any way around it?

Chris
  • 54,599
  • 30
  • 149
  • 186
JasonGenX
  • 4,952
  • 27
  • 106
  • 198
  • 1
    Can you provide more detail why you need multiple users to log in on different tabs? Is it just for testing? People sharing a computer? – Ryan Bemrose Oct 26 '15 at 01:15
  • 1
    Some users can log into multiple types of accounts they have in our applications. Some choose to do it within the same browsers. Hence the problem. – JasonGenX Oct 26 '15 at 18:33
  • If you can allow the constraint that a user can log in at most one account *per type*, then I think this becomes an easier problem. – Ryan Bemrose Oct 28 '15 at 00:16
  • See my answer on just using session ID in the url. the linked code also locks to IP and browser agent to deal with security risks. – Eric Kelly Nov 01 '15 at 16:58
  • I suddenly recognise that my answer doesn't match your question! Your question is about different user in different tab of one browser. I think this is not about server side because one session cannot used by more than one user. I think we can use something like `Token` but not really php seesion facility to approach that! – ruibin Nov 16 '15 at 12:57

8 Answers8

34

Laravel Session Background

Sessions

Skip this section for a quick easy solution

In Laravel, session cookies are created via the Illuminate\Session\SessionManager class, namely through the buildSession method:

SessionManager::buildSession

protected function buildSession($handler)
{
    if ($this->app['config']['session.encrypt']) {
        return new EncryptedStore(
            $this->app['config']['session.cookie'], $handler, $this->app['encrypter']
        );
    } else {
        return new Store($this->app['config']['session.cookie'], $handler);
    }
}

In this method we can clearly see that the name of the session comes from our config\session.php, looking in particular this line:

session.php

'cookie' => 'laravel_session', # ~~ ln 121 at time of writing

Ok, but that doesn't help a lot, changing this, changes it everywhere, as noted by the comment proceeding it in the config.

The name specified here will get used every time a new session cookie is created by the framework for every driver.

And even if we could pass it some dynamic value, something like:

'cookie' => 'laravel_session' . user()->id,

This creates a paradoxical, time ending, universe imploding outcome because you are requesting the id from the user which is accessed via the session looked up by the cookie name laravel_session.. (mindblown)

Let's leave SessionManager and it's session.php configuration alone. We can see from above that regardless of how we approach this, all our session info will be fall under that single laravel_session key.

Guard

Maybe Guard will have some more information.

Guard is your key to auth into your app, and one of the many things that makes Laravel awesome for quickly creating applications.

The method to look at is Guard::user().

One of the first things Guard::user() does after some initial cache and logged out checking, is a session check.

Guard::user()

$id = $this->session->get($this->getName()); 

So here, Laravel is fetching the session values that match the result of getName() - awesome - all we need to do is mod getName() to return a value, let's take a took at that method:

Guard::getName()

public function getName()
{
    return 'login_'.md5(get_class($this));
}

That's pretty straight forward. $this refers to the Guard class, so the md5 will effectively always be the same (if anyone knows the 'why' behind md5'ing the class name which would be the same each time, leave a comment).

There are a few places where this should be updated, such as getRecallerName.

So from here, you can extend the core Guard class and splice in your getName and getRecallerName methods.

You will probably want to wrap some service provider around this, write some unit tests, possibly even overwrite the original auth manager.

"Geez, that seems like a lot of work"

"It sure is Billy, it sure is"

https://www.youtube.com/watch?v=dTxQ9yhGnAg

See the next part

The quick "I just need an answer" answer

Ollie Read has already created a solution, found here:

https://github.com/ollieread/multiauth

I encourage you to have a look, especially the custom Guard class which extends core Guard with custom getName methods.

Chris
  • 54,599
  • 30
  • 149
  • 186
  • In response to your question about md5. md5'ing the same value will always result in the same hash, because md5 does not use any salt by default, unlike bcrypt. – Dmitri Chebotarev Jun 12 '16 at 12:43
  • I guess I was more curious about why md5 it in the first place if the result is the same? It's not as a security measure because md5 is less than average, is it just so the the class name is somewhat masked? – Chris Jun 12 '16 at 12:58
  • 1
    Ah yes, i misunderstood your question. The only other thing that pops into my mind, is that you might want to store something in a session variable named 'login_guard', and it would be really bad if you accidentally override something that is reserved by the framework. So by md5'ing the name, it's more error-proof for the developer. – Dmitri Chebotarev Jun 13 '16 at 09:51
  • Thanks Chris. I have checked your suggested solution but github says 'THIS PACKAGE HAS BEEN DISCONTINUED'. – Kamlesh Dec 08 '21 at 13:10
  • 1
    @Kamlesh Probably because the answer is 5 years old :) Everything above the "quick answer" still applies though – Chris Dec 09 '21 at 01:14
2

Any major browser will only store one session cookie for a site, but the site developer gets to choose what's in that cookie. It seems like your site is storing user information in the session cookie, which is then getting overwritten when the other tab stores different information in the same cookie.

You don't provide much detail about how your specific site operates, but here are a few general ways of approaching this problem.

1) Use different browsers for different users. Different browsers don't share cookies between them. If your goal is simply to test your site with multiple users, this is the way. You can also use Incognito/Private mode to log in a separate user, as this mode doesn't share cookies either.

2) Don't use session cookies to store user information. This is a non-starter on most websites, but if this is an internal site or strictly controlled environment, you may be able to pass user identification via the URL, POST data, or some other hidden identifier in the request.

3) Store data in the session cookie for all currently logged in users. Depending on the web framework, it may be possible to create a map of user -> cookieData and look up the correct one based on which user is making the request. This is an advanced technique, and I don't actually know if Laravel exposes this level of control.

Ryan Bemrose
  • 9,018
  • 1
  • 41
  • 54
2

Multi userlogin with same browser like google add account. for that you need follow some steps and re-write auth library which provided by the Laravel,

Steps

Tack backup of your Auth file.

Change all session store functionality to store it first in array and then store that array to session

Now you need to create the new session variable which will store the current user instance id like user 0 1 2 ...

Now you need to change all the function from you will get the values from the session you need to check if the session object is empty then user is logout else you need to get data of the user base on the user instance.

You need to change your instance when user want to switch from one account to another.

Mitul
  • 3,431
  • 2
  • 22
  • 35
2

The easiest is just a URL based sessionID which could be a security issue depending on how your application is designed, especially when sharing urls with non-expired sessions.

Since L5 doesn't support php native sessions anymore, you'll have to use a custom provider like below:

This will use sessionID in the url for laravel V5:

https://github.com/iMi-digital/laravel-transsid

Basically the session is URL based, so you can just login in a different tab and get a new sessionID, and that person can easily do a "open page in new tab" to have two pages of the same user if needed as well.

The library above locks the session to the IP and User Agent so link sharing won't accidentally leak a session.

Eric Kelly
  • 435
  • 3
  • 8
1

tl;dr: Yagni

Consider a person (http client in your case) with 2 identities: Dr Jekyll and Mr Hyde.
He visits his new friend Sir RM1970 (http server in your case): "How do you do, RM1970!".
Here is the problem. Poor RM1970 need to welcome back the monster, and there are few options:

  • fall deep into this rabbit hole: "How do you do both Dr Jekyll and Mr Hyde!", which incredibly complicates further conversation (your ACl, for example, will need to operate with list of identities, and make a decision about priorities if they conflict realtime)
  • make a decision on your own: "How do you do Dr Jekyll!" and pray you made the right choice (randomly pick user's identity and bring your users some fun with unpredictable responses)
  • be sly and shift this responsibility back to him: "Pardon me? Who are you? Name yourself!" (require single identity per request)

The later is how it actually works. The browser provides the latest confirmed identity.
You've been asked to change this, but do you really want it? Hold the line and don't accept this responsibility.
If you are not going with first 2 dead-end options, you will need to ask user on which behalf he sends the request. The best option here is to make your frontend stateful, maintain list of opened sessions, and provide a UI for user to pick one. It is almost the 3rd Ryan Bemrose's option, but store this data on client side, and send only the chosen one. No changes in laravel backend required.

The problem here is switching tabs will not automatically switch user, and will be rather confusing, providing very little difference with logout/login path, which is already implemented.

Some browsers support multiple profiles (example), which may be an acceptable alternative. Basically it is the same as 1st Ryan Bemrose's option, but does not require multiple browsers installed, and can benefit from permanent cookies, aka 'remember-me'.

Community
  • 1
  • 1
Alex Blex
  • 34,704
  • 7
  • 48
  • 75
0

I don't exactly know what do you need this for, but as a developer I sometimes have to log into an application with multiple users. To do that I usually use incognito mode or if its more than 2 users I had some luck using this extension in chrome.

I know its not an answer to your question but it just might be what your looking for.

Pawel Bieszczad
  • 12,925
  • 3
  • 37
  • 40
  • 1
    Looking for a more programmatic solution to this. I cannot control my users' environments nor can I force them to use chrome or some extensions of it. – JasonGenX Oct 26 '15 at 21:25
  • I looked more into your question and [this](http://stackoverflow.com/a/11783754/1335617) describes something I can see working. You would have to add a tab id to you session and overwrite laravel authentication to pull the user associated with a given tab. Quite some work and testing to write but I can see it working. There are some some downsides of course. You would have to login to every new tab (probably able to get that working with more workarounds) and someone mentioned the duplicated tab. Hope that helps! – Pawel Bieszczad Oct 28 '15 at 15:45
0

Different seesions coexist between concurrent logged in users cannot just only implemented by session cookie,because cookie is stored by browser. So the logged in user's seesion must be stored by Server. As all we know, Once session_start is called,SessionID is created and then temp file is created in server's temporary directory.

Diffent user has different SessionID and after session_destory called then all SessionIDs stored in Server and Cookies are recovered. You can rewrite this behavior by implementing SessionHandlerInterface. Of cause many web framework support this,Laravel has not exception. Here is the document: custom-session-drivers

ruibin
  • 113
  • 9
0

I don't know how complicate it is to code it into laravel but this could be one solution:

You use a different session name, has to be a string, and code it into the url every time so the application knows which user made a request. So you can call the session variables by a normal name.

<?php
if(isset($_GET['id']) && !empty($_GET['id']))
    session_name($_GET['id']);
session_start();

if(isset($_GET['user'])) {
    $_SESSION['user'] = $_GET['user'];
}

if(!empty($_SESSION['user']))
    echo "Hello ".$_SESSION['user'];
cre8
  • 13,012
  • 8
  • 37
  • 61