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?
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.
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"
See the next part
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.
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.
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?
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.