I am using Laravel 4.2's authentication mechanism and I am trying to figure out when and how the "remember me" cookie token gets set.
For some reason when I do the following code, even though the second parameter is false, when I close the browser and reopen it I still get logged in automatically without needing to type in the username and password. So practically it is always acting as if the 'Remember Me' is checked, even if it is false.
My authentication code:
//called on POST of login page with credentials
$userdata = array(
'email' => Input::get('email'),
'password' => Input::get('password')
);
// attempt to do the login
if (Auth::attempt($userdata, false))
{
//redirect to main secure page
My check on the login page to check if the user was already authenticated:
//called on GET of login page
if (Auth::check())
{
//user already logged in, redirect to main secure page immediately
Even though Auth::attempt() is being passed false, the Auth::check() still returns true when I close the browser and reopen it, which normally clears any sessions.
When I used to do authentication manually in PHP (not using Laravel's Authentication method), I used to use something like:
//authenticate the user with the DB and get the $user object
$_SESSION['user'] = $user; //set it in the session
And then in my secure pages I would check if the user is logged in by doing:
if (isset($_SESSION['user']))
{
//user already logged in ...
In this case, closing the browser and reopening it cleared the session. (I just verified this again with one of my older webapps and it still behaves that way, so its not some browser issue).
Is there any reason why Laravel is not behaving in this way? If it is retaining the authentication session across browser sessions anyway, what is the point of passing the 'remember me' flag?
Is there a way to disable this behaviour and make it work normally (i.e. when the browser is closed the session is no longer retained)?
OK seems that apart from the config/auth.php configuration there is a more detailed session configuration in config/session.php, and what I want is actually there in that file.
The setting 'expire_on_close' => false in that file is what is causing this behaviour, and changing it to true immediately solves the issue.
Not sure why its false by default. Its not secure if people just close their browser without logging out of the application (since they wouldn't have checked the 'remember me' flag they would think that the application wouldn't remember them and the session would have been destroyed), and someone else opens the browser and would be able to access the secure pages just the same.
Anyway, posted answer in case someone needs it.
Related
I'm trying to login a user by Ajax with the Auth::attempt method.
if (Auth::attempt(['email' => $datas['user_email'], 'password' => $datas['user_password']])) {
$request->session()->regenerate();
$request->session()->save();
return response()->json(['success' => $response_object]);
}
The method returns true and after an Auth::check(), I can see that the user is logged in. But on ajax success, I redirect with js with window.location.href and the user is not logged in anymore.
My get and post routes use the StartSession middleware.
I don't have any errors and really don't know how to proceed.
Does anyone have an idea?
As I understand , you are trying to login using the js , the request reach the server and you login the user and Regenerate the session , that means there is a new session ID for the user after login , but I think that the browser still use the previous session data when it sends requests to the server. try not to generate the session and see if the problem still exists, or try to replace the session cookie in the browser with the new one.
If your ajax request is using the API routes then there won't be a session, maybe is the problem.
https://laracasts.com/discuss/channels/requests/laravel-ajax-request-dont-set-session-variable#reply-447467
I need to develop a very secure login system.
I have an API layer, and my plan is to create a token table. I'll send the username/password (Post) to my API and I generate a unique token (One time token with limited time), I return the token in JSON.
My questions are:
What should be saved in the sessions while user is logged in. I want to store as little as possible information. Should I save the token only? Is it safe?
I don't want to store the access levels and user info in session, then I need to get these info each time via token.. what do you think? Advice!
Any other advice to develop more secure login system.
Thank you in advance.
Since you'd like to keep sessions as logged in identification, you will probably encounter a problem: some clients can't be kept session.
For example, cell phones, web-based app.
I'm creating a similar project. And my solution is
Creating a table named session (or whatever you want), that keeps UserToken (randomly generated), UserID, TokenExpire.
Once the user logged in, create a record at session table, and return the token to the user (encoded into JSON).
User keeps the token their own, and attached the token on every request. (No matter body or header, I'm using header to separate from the data).
Check the token every time before they asking for something. For Example, Is the token exists? Is the token expired? Is the user be blocked?
By the step 4., you can also get what the user is by querying relatively.
That is my solution. It's similar to your way.
Additionally, To improve the security, follow the ways if could
Use SSL (HTTP) to secure the connection between server and clients.
Don't keep their Password plaintext. You should encrypt them.
You can generate the token as longer as possible.
The token field of session table, should be Case Sensitive. Change the collation different from _ci (means Case Insensitive)
Check the POST data to prevent SQL Injection. Never trust what users give to you.
The instructions above are fundamental. I always do that.
this is just a small list on how you can handle it with sessions, cookies and of course a database and php:P not with JSON
you can use a database like this
so the session should contain an array with an index of login, so you can later check if the session isset and restrict acces to a user_only_page. like this:
$_SESSION = array(
"login" => true,
"data" => array(
"username" => $row["username"], // is not a must and not unsafe / you can let it out if you want
"email" => $row["email"], // this is also not a must
"time" => time()+60*10 // so here you can set a time how long the session is available. for this example it is 10min.
)
);
set a cookie for a "remember_me" checkbox like this:
if (isset($_POST["remember_me"]) && $_POST["remember_me"] === "accepted") {
$_SESSION = array(
"login" => true,
"data" => array(
"username" => $row["username"], //not a must
"email" => $row["email"], //also not a must
"time" => time() +3600*24*30 //set the time higher, so that the user won't bee kicked out after 10min when he logged in.
)
);
setcookie('remember_me', md5($emailUsername . time()), time() +3600*24*30);
}
then you can write a file, that is included in every page and handles all sessions or cookies. like this:
ob_start(); //Turns on the output buffering
if (isset($_SESSION["login"])) { //here we check if the session isset(true)
if ($_SESSION["data"]["time"] >= time()) { //here we check if the session time we set before to 10min is greater than the actual time
if(isset($_COOKIE["remember_me"])) { //if you want to let the user stayed in check if the cookie isset, and if redirect him directly to the userarea
header('Location: ../path_to_theuserarea/userarea.php'); //the redirect
exit();
}
}
else { // here we go if the session time is lower than the actual time and kick the user out
$_SESSION["login"] = false; //he has to log_in again
header('Location: ../path_to_the_login/login.php'); //redirect to the login.php
exit();
}
}
else { //here we check if the requested basename of a file is not login.php and if, redirect him to login.php. thats the part that will be included in your userarea.php
if (basename($_SERVER["REQUEST_URI"]) !== "login.php") {
header('Location: ../path_to_the_login/login.php'); //redirect back to login
}
}
ob_end_flush(); //end buffering
also good for a secure login/register system are:
Account_Activation_Token: send a token after successful registered to the email that the user entered and only let him log in if he clicked on that link. I used this method for myself and also used $_GET for the activation token. Send a link like https://YOUR_URL/log_reg/activateAccountWithToken.php?id=USERID&activation_token=RANDOMLY-STRING-WITH-50-CHARAKTERS
you can also .htaccess to rewrite the url so that the URL is shown better. like this:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /log_reg/
RewriteRule ^activate/([a-z]+)/?$ activationToken.php?id=$1 [NC,L]
</IfModule>
after that you can use a url like https://YOUR_URL/log_reg/activate/USERID/
You can use a Cronjob for deleting all activation tokens from your db after some time, so that the user has to activate his account again.
NEVER store PLAINTEXT_PASSWORDS, use functions like password_hash() and password_verify()
use password_needs_rehash() function when the user loggs in so that the hash from the db will be regenerated everytime he loggs in.
force SSL with .htaccess and use prepared-statements for inserting the data to the database
I am tracking the login history of a user in my website, and for that I need to display if there is any other session open in another browser or device. Plus, I need to provide a functionality wherein a user can logout a session that is open in a different browser/device from the current session. I am using redis-server. The things which I have done so far are as follows.
In the .env file, I have updated the CACHE_DRIVER and SESSION_DRIVER to redis.
In session.php and cache.php, I have set driver to redis.
Next, at the time of login, I connect to the redis server and store the session id for a specific user.
$redis = Redis::connection();
$redis->sadd('user:sessions:'. Auth::user()->id, $session_id);
Once this is done, I can see the multiple session id's per user using redis-cli command.
> SMEMBERS user:sessions:1
> "95008658737a7f937c614bccbb748443a649c515"
> "f1f14db9b1760254a4072fe9b440f9acbacc8974"
> GET laravel:95008658737a7f937c614bccbb748443a649c515
> "s:332:\"a:5:{s:6:\"_token\";s:40:\"HAuA200irzF63fgFw4vCVkrsZNuqTk6o2XLkHzlu\";s:9:\"_previous\";a:1:{s:3:\"url\";s:33:\"http://localhost:8000/security\";}s:5:\"flash\";a:2:{s:3:\"old\";a:0:{}s:3:\"new\";a:0:{}}s:50:\"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d\";i:1;s:9:\"_sf2_meta\";a:3:{s:1:\"u\";i:1464957596;s:1:\"c\";i:1464957416;s:1:\"l\";s:1:\"0\";}}\";"
This output makes me assume that the redis option for storing sessions in laravel is working fine.
Next, for logging out a user from a specific session, I am using the following set of instructions:
public function logoutSession($session_id) {
$redis = Redis::connection();
$redis->del('laravel:' . $session_id);
$redis->srem('user:sessions:' . Auth::user()->id, $session_id);
}
Once this function is called, the corresponding session id is successfully deleted.
Running the SMEMBERS and GET commands again in redis-cli proves it.
So the scenario is, I try to delete a session opened in say, Firefox from another browser say, Chrome. Cliking on the logout link (for Firefox session from Chrome session) deletes the Firefox session from redis-server successfully, but if I try to refresh the Firefox browser, I am still logged in.
Just an image displaying the functionality I am trying to achieve
I cannot understand where I am going wrong exactly. Any sort of help will be highly appreciated.
Thanks
NOTE: I am already using the 'auth' middleware, so that cannot be the problem. Plus, if I try to debug the code, I am seeing different session_ids in $_COOKIE variable and http request variable. Is this normal?
I had a similar issue once myself. I was frustrating myself with cache invalidation etc... and then after going through the "log in" process again I had forgotten to check for the "remember me" cookie.
https://laravel.com/docs/master/authentication#remembering-users
Turns out I had cleared the Cache successfully, but needed to call:
Auth::logout();
In order to remove the "remember_token" from the database. Otherwise, Laravel sees this as a valid way to reboot the Auth system, logging the user back in and setting a new session cookie.
Using your code:
public function logoutSession($session_id) {
$redis = Redis::connection();
$redis->del('laravel:' . $session_id);
$redis->srem('user:sessions:' . Auth::user()->id, $session_id);
Auth::logout();
}
But this logs out from ALL sessions, including the one you're working with.
For your problem, I propose using the Auth::viaRemember() method to determine if the client creating the request along with the cookie is the one you're currently operating with. It differentiates between your current session cookie and a "remember me" cookie:
if (Auth::viaRemember())
{
// Check Redis for session info
// Do something if not found, otherwise just proceed
}
**EDIT**: I found this after posting my answer: Laravel Auth::logout not removing remember me cookie
So to recap:
if (Auth::viaRemember())
{
if (!Redis::get($key))
{
// Get remember_me cookie name
$rememberMeCookie = Auth::getRecallerName();
// Tell Laravel to forget this cookie
$cookie = Cookie::forget($rememberMeCookie);
return Redirect::to('/')->withCookie($cookie);
}
}
I'm trying to manually login the user using this snippet (after verifying the login data of course):
public function loginUser($user) {
$userArray = array('uid' => $user->getUid());
$GLOBALS['TSFE']->fe_user->is_permanent = true;
$GLOBALS['TSFE']->fe_user->checkPid = 0;
$GLOBALS['TSFE']->fe_user->createUserSession($userArray);
$GLOBALS['TSFE']->fe_user->user = $GLOBALS['TSFE']->fe_user->fetchUserSession();
$GLOBALS['TSFE']->fe_user->fetchGroupData();
$GLOBALS['TSFE']->loginUser = true;
//this somehow forces a cookie to be set
$GLOBALS['TSFE']->fe_user->setAndSaveSessionData('dummy', TRUE);
}
If I leave out the "setAndSaveSessionData" Part, the login doesn't work at all, after a page redirect the login data are gone and the user is logged out again. But the setAndSaveSessionData stores the session in a cookie and the user will remain logged in even after closing the browser - which is a behaviour I do not want (not without the user's consent). Is there a way to manually login the user without the "setAndSaveSessionData" part? I'm using Typo3 6.2.12 with extbase and felogin
Thank you very much in advance!
In some TYPO3 6.2.x (I don't remember which x exactly) there was a change introduced, which causes that you need to call AbstractUserAuthentication::setSessionCookie() method yourself... Unfortunately it has protected access so the best way to login user is creating some util class extending it:
typo3conf/your_ext/Classes/Utils/FeuserAuthentication.php
<?php
namespace VendorName\YourExt\Utils;
use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
class FeuserAuthentication extends AbstractUserAuthentication {
function __construct($uid) {
return $this->authByUid($uid);
}
// Authenticates user by uid
public function authByUid($uid) {
/** #var $fe_user FrontendUserAuthentication */
$fe_user = $GLOBALS['TSFE']->fe_user;
$fe_user->createUserSession(array('uid' => $uid));
$fe_user->user = $fe_user->getRawUserByUid($uid);
$fe_user->fetchGroupData();
$GLOBALS['TSFE']->loginUser = true;
$fe_user->setSessionCookie();
return $fe_user->isSetSessionCookie();
}
}
so to login your user by uid you just need to create new object of this class with $uid of fe_user as a constructor's param:
$this->objectManager->get(
'\VendorName\YourExt\Utils\FeuserAuthentication',
$user->getUid()
);
P.S. As you can see this class doesn't check if account exists and/or is enabled, so you need to check it yourself before authentication attempt.
A website is delivered HTTP(S), which is a stateless protocol. This means that something has to be saved on the client computer, because otherwise the server couldn't reliably recognize the user again. There are several ways to do this:
Use a session cookie (The way TYPO3 does it, PHP also does that)
Manually add a session ID to each request on a page, Java-based applications do that sometimes. This breaks if the user leaves the page and comes back later (in the same session, or in another tab without copying a link).
There are probably some more ways, but I can't come up with them right now
So my answer is: Since I believe it is hard to get TYPO3 to switch to another way of setting the session information (would be quite user unfriendly), there is no good way to avoid setting the cookie.
However:
The cookie is a session cookie, which expires when the browser session is ended. So closing the browser and reopening it should end the login, except if the browser restores the previous session when opened. Many modern browsers do this, especially mobile browsers. Ways to get around that:
Use the incognito or private mode of the browser
Set the browser to start a fresh session on each start and make sure the browser terminates when you are finished (again: watch mobile devices).
For background, here's my scenario:
I'm building a self-hosted platform for a particular niche, and part of that platform is the login process, which if validated sets a session for the user. On every administration page load, the session is started and the variable $_SESSION['key'] checked to see if it's true.
If it's false, the user is re-directed to the login page with an error telling them to login again.
The problem is this is dependant on a session cookie being set or not set, in the sense that when the session cookie expires and the session is started with session_start() to check $_SESSION['key'], therefore creating a new session cookie with default values (I use session_set_cookie_params() to alter the path, timeout etc) making it impossible to re-login as session cookies do not overwrite from what I can see.
In order to fix this I've thought about using something like session_set_cookie_params(5, ..) before session_start() when checking $_SESSION['key'], which will create a 5 second session cookie, therefore allowing a new one to be created when re-logging in, but I'm not sure whether this would work - and I'm also sure there must be a more efficient way to set/unset a session variable?
Here's my code:
Start session if validated login
if($validated){
session_set_cookie_params(1800, "/inst", $server_name['home']);
session_name("_inst");
session_start();
$_SESSION['key'] = true;
}
Check if $_SESSION['key'] is still true
session_name("_inst");
session_start();
if(!$_SESSION['key']) {
header('Location: ' . $server['home'] . '/login/?error=1');
}
Any answers or advice would be great, and please do ask if you need clarification on anything!
Just issue a session_start() on all pages. This'll unconditionally create a session for you, then you can check for the presence/abscence of the key:
<?php
session_start();
if (!isset($_SESSSION['key'])) { // never logged in, or were logged in and got logged out
... redirect to login ...
exit();
}
if ($_SESSION['key'] != 'expected value') { // logged in, but no longer a valid login.
... redirect to login ...
exit();
}
You shouldn't have to mess with the session cookie settings at all. Leave them at default and simply check for the relevant key(s). if they're not there, then the user is not logged in and should be redirected.
If their session had expired and PHP cleaned up the session file, then they've been effectively logged out, even though they have a it-was-valid-at-one-point-in-time session key, and should get redirected to login again.