I tried a number of ways from the Internet, but they are not able to achieve, I hope someone can give me a way of achieving ideas or methods, thank you for your help.
Let me provide an example. Define a SessionTimeout middleware in the app\Http\Middleware folder.
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
use Session;
class SessionTimeout
{
/**
* Check the incoming request for session data, log out if session lifetime is exceeded.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
//$isLoggedIn = $request->path() != '/logout';
$bag = Session::getMetadataBag();
$max = $this->getTimeOut();
if (($bag && $max < (time() - $bag->getLastUsed()))) {
//$cookie = cookie('intend', $isLoggedIn ? url()->current() : 'auth/login');
$email = Auth::user()->email;
$returnPath = url()->current();
$request->session()->flush(); // remove all the session data
Auth::logout(); // logout user
return redirect('auth/login')
->withInput(compact('email', 'returnPath'))
//->withCookie($cookie)
->withErrors(['Please login']);
//you could also redirect to lock-screen, a completely different view
//and then pass the returnPath to controller method maybe via hidden filed
//to redirect to the last page/path the user was on
//after successful re-login from the lock-screen.
}
return $next($request);
}
/**
* Set a variable in .env file TIMEOUT (in seconds) to play around in the development machine.
*/
protected function getTimeOut()
{
return (env('TIMEOUT')) ?: (config('session.lifetime') * 60);
}
}
The add SessionTimeout to the app\Http\Kernel.php
class Kernel extends HttpKernel {
/**
* The application's global HTTP middleware stack.
*
* #var array
*/
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\SessionTimeout'
];
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated'
];
}
Then in the view for login form generally in resources\views\auth\login.blade.php
#extend('app-layout')
#section('content')
//code to display errors here
#if($email) //check if the request has $email returned by SessionTimeout middleware
//if so display lock screen like
//code to display the profile image
//code to display the user email (or whatever id is used)
#else
//display email input field for a new login
//code to input the email (whatever id is used) for a new login
#endif
//here the code common for lock screen as well as new login.
//code to display input password
//code for submit button and rest of the things like remember me field
#stop
You can also use partials for lock screen and new login form and display based on #if($email).
Hope this would get you started.
Assuming you are using the session driver to handle your authentication, you can change the time period for an idle session to expire in the
/app/config/session.php file.
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to immediately expire on the browser closing, set that option.
|
*/
'lifetime' => 120, // minutes
'expire_on_close' => false,
Related
I am using Laravel 7 and using PayTabs payment gateway for payments. When the user is redirected back from the Paytabs, all the sessions and Auth are cleared.
Before redirecting to the Paytabs, im saving the session when the data is put in the session.
as
Session::put('data', $data);
Session::save();
And the redirection to Paytabs is as follows:
if ($response->response_code == "4012") { //Page created
return redirect()->to($response->payment_url);
} else {
abort(404);
}
I have also excluded the return url from CSRF Token check as follow:
VerifyCsrfToke.php
protected $except = [
'/paytab_return'
];
Also I have checked that the Paytabs redirects to the correct URL with https and www.
Favor needed to tackle this issue. Thanks
This worked for Laravel 6.19.1:
I added a GET variable to my success, error or cancelUrls of the payment gate
This variable was called exactly the same as the name of the session cookie
$sessionKey = config('session.cookie') . '=' . session()->getId();
$successUrl = route('wirecardSuccess') . '?' . $sessionKey;
The URL I'd got is e.g.
http://beatbox.vnr:8082/vnr/payment/wirecard/success?self_service_local_vnr_session=qNSQ7SessionIdtEA3Z72ReuvgsFt
as the url, where self_service_local_vnr_session is my session cookie name and qNSQ7SessionIdtEA3Z72ReuvgsFt the ID of the current session.
Then I needed to extend the StartSession Middleware with this code
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Illuminate\Contracts\Session\Session;
use Illuminate\Http\Request;
/**
* Class StartSession
* #package App\Http\Middleware
*/
class StartSession extends \Illuminate\Session\Middleware\StartSession
{
/**
* Get the session implementation from the manager.
*
* #param Request $request
* #return Session
*/
public function getSession(Request $request): Session
{
return tap($this->manager->driver(), static function ($session) use ($request) {
$sessionCookieName = config('session.cookie');
if ($request->has($sessionCookieName)) {
$sessionId = $request->input($sessionCookieName);
} else {
$sessionId = $request->cookies->get($session->getName());
}
$session->setId($sessionId);
});
}
}
The payment was made and the redirection url (with the session id) allowed me to retrieve the old session information.
I hope it'll help someone, who lands on this page :)
edit this fields in config/session.php
'path' => '/;samesite=none',
'secure' => true,
'same_site' => 'none',
Bottom line:
How to logout the user on session time out?
Detailed Question:
I have a Laravel 5.6.* application and the project demands the user to logout whenever the user is idle. I have tried the solutions that are given here, but none of them worked for me.
Then I stumbled upon this post:
https://laravel-tricks.com/tricks/session-timeout-for-logged-in-user and made my way through it to no success.
What I want:
Logout the user automatically on session timeout. Before logging out, set is_logged_in attribute to false or 0 on the Users table. How do I achieve this?
Code that I have tried so far:
session.php
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to immediately expire on the browser closing, set that option.
|
*/
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
SessionTimeOut.php Middleware
<?php
namespace App\Http\Middleware;
use Closure;
use App\Traits\CacheQueryResults;
class SessionTimeOut
{
use CacheQueryResults;
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// session()->forget('lastActivityTime');
if (! session()->has('lastActivityTime')) {
session(['lastActivityTime' => now()]);
}
// dd(
// session('lastActivityTime')->format('Y-M-jS h:i:s A'),
// now()->diffInMinutes(session('lastActivityTime')),
// now()->diffInMinutes(session('lastActivityTime')) >= config('session.lifetime')
// );
if (now()->diffInMinutes(session('lastActivityTime')) >= (config('session.lifetime') - 1) ) {
if (auth()->check() && auth()->id() > 1) {
$user = auth()->user();
auth()->logout();
$user->update(['is_logged_in' => false]);
$this->reCacheAllUsersData();
session()->forget('lastActivityTime');
return redirect(route('users.login'));
}
}
session(['lastActivityTime' => now()]);
return $next($request);
}
}
Kernel.php
/**
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SessionTimeOut::class,
],
];
You are comparing session lifetime same as in middleware.
That Means when session will expire, your middleware will not(never) called.And user will move to login page.
If you want to save entry in Database, You can set long-time session lifetime, and in middleware use your custom time to logout.
Change in config/session.php
'lifetime' => 525600, // for one year, it will be in minute, use as you want.
Change in middleware as below, log out after two hours.
if (now()->diffInMinutes(session('lastActivityTime')) >= (120) ) { // also you can this value in your config file and use here
if (auth()->check() && auth()->id() > 1) {
$user = auth()->user();
auth()->logout();
$user->update(['is_logged_in' => false]);
$this->reCacheAllUsersData();
session()->forget('lastActivityTime');
return redirect(route('users.login'));
}
}
By this way your session will not expire automatically and you can manipulate data.
Need to update the database before logout. Because after logout can't perform $user->update(). so that try following way:
if (auth()->check() && auth()->id() > 1) {
$user = auth()->user();
$user->update(['is_logged_in' => false]);
$this->reCacheAllUsersData();
session()->forget('lastActivityTime');
//Add Logout method here..
auth()->logout();
return redirect(route('users.login'));
}
please check less than 120 in middleware,ex 115 or 119 in below if condition and then check it
if (now()->diffInMinutes(session('lastActivityTime')) == config('session.lifetime')) {
....
}
I use Laravel 5.2, and I want to know how to force a user to log out by id. I'm building an admin panel with the option to deactivate a specific user that is currently logged in to the web application. Laravel gives you this option for a current user.
Auth::logout()
But I don't want to log out the current user, as I am an authenticated user. So I need to force log out of a specific user by its id. Just like when we log in a user with a specific id.
Auth::loginUsingId($id);
Is there something like the following?
Auth::logoutUsingId($id);
Currently, there's no straightforward way to do this; As the StatefulGuard contract and its SessionGuard implementation don't offer a logoutUsingId() as they do for login.
You need to add a new field to your users table and set it to true when you want a specific user to be logged out. Then use a middleware to check if the current user needs a force logout.
Here's a quick implementation.
1. Add a new field
Let's add a new field to users table migration class:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
// ...
$table->boolean('logout')->default(false);
// other fields...
});
}
// ...
}
Make sure you run php artisan migrate:refresh [--seed] after changing the migration.
2. Force logout middleware
Let's create a new middleware:
php artisan make:middleware LogoutUsers
Here's the logic to check if a user needs to be kicked out:
<?php
namespace App\Http\Middleware;
use Auth;
use Closure;
class LogoutUsers
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Auth::user();
// You might want to create a method on your model to
// prevent direct access to the `logout` property. Something
// like `markedForLogout()` maybe.
if (! empty($user->logout)) {
// Not for the next time!
// Maybe a `unmarkForLogout()` method is appropriate here.
$user->logout = false;
$user->save();
// Log her out
Auth::logout();
return redirect()->route('login');
}
return $next($request);
}
}
3. Register the middleware in HTTP kernel
Open up the app/Http/Kernel.php and add your middleware's FQN:
/**
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\LogoutUsers::class, // <= Here
],
'api' => [
'throttle:60,1',
'bindings',
],
];
It's untested code, but it should give you the idea. It'd be a good practice to add a couple of API methods to your User model to accompany with this functionality:
markedForLogout() : Checks user's logout flag.
markForLogout() : Sets user's logout flag to true.
unmarkForLogout() : Sets user's logout flag to false.
Then on the administration side (I suppose it's your case), you just need to call markForLogout() on the specific user model to kick him out on the next request. Or you can utilize the query builder to set the flag, if the model object is not available:
User::where('id', $userId)
->update(['logout' => true]);
It can be a markForLogoutById($id) method.
Related discussions
[Proposal] Log out users by ID
Multiple statements when logged users are deleted
Use the setUser to find a soluion
get current user
$user = Auth::user();
logout user you want to, by id
$userToLogout = User::find(5);
Auth::setUser($userToLogout);
Auth::logout();
set again current user
Auth::setUser($user);
I suggest you to override App\Http\Middleware\Authenticate handle function with custom check
if ($this->auth->user()->deleted_at) {
$this->auth->logout();
return redirect()->route('login');
}
The setup and below code I use it to logout a user and also lock his account(optional)using checklist, I did it through Ajax Request:
-In config/session.php, change to:
'driver' => env('SESSION_DRIVER', 'database'),
-run php artisan session:table
-run php artisan migrate //a session table is created.
-in .env file: change SESSION_DRIVER=file to SESSION_DRIVER=database
Now the coding part:
-In controller:
function ajaxCheckList(Request $request)
{
// \Log::info($request);
$user = \App\Models\User::findOrFail($request['user_id']);
$locked = 0;
if ($user->locked == 1) {
$user->locked = 0;
$locked = 0;
} else {
$user->locked = 1;
$locked = 1;
DB::table('sessions')
->where('user_id', $request['user_id'])
->delete();
}
$user->update(['locked' => $locked]);
}
Access to my application (in order)
Whitelist ip address
redirect to 404 on invalid ip
Check if last activity was > 2 hours ago
redirect to login page and expire session
Check if user is logged in, by looking at user data in $_SESSION
redirect to login page if not valid
Index.php
(notice it is very similar to this question):
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new RepositoryFactory($db), new EntityFactory);
$serviceFactory->setDefaultNamespace('\\MyApp\\Service');
$request = new Request();
$session = new Session();
$session->start();
$router = new Router($request, $session);
/*
* Whitelist IP addresses
*/
if (!$session->isValidIp()) {
$router->import($whitelist_config);
/*
* Check if Session is expired or invalid
*/
} elseif (!$session->isValid()) {
$router->import($session_config);
/*
* Check if user is logged in
*/
} elseif (!$session->loggedIn()) {
$router->import($login_config);
} else {
$router->import($routes_config);
}
/*
* Find matched route, or throw 400 header.
*
* If matched route, add resource name
* and action to the request object.
*/
$router->route();
/*
* Initialization of View
*/
$class = '\\MyApp\\View\\' . $request->getResourceName();
$view = new $class($serviceFactory);
/*
* Initialization of Controller
*/
$class = '\\MyApp\\Controller\\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
echo $view->render();
The $router->import() function takes a json file with the route configuration and creates the routes (Haven't decided if I'm going to keep that). My router is a modified version of Klein.
My Question
Is this a proper implementation of how to check the session data?
I would prefer to check that the user data in the session can be found in the database, but I would need to use a Service for that, and Services should only be accessed by the controller(?). I wouldn't know which controller to send the user to since the route configuration would change if the user was logged in.
For example, if someone was trying to go to www.myapp.com/orders/123, I would send them to the Orders controller if they were logged in, or the Session controller (to render the login page) if they weren't.
I have read the ACL Implementation from this question. But, unless I'm mistaken, that is for controlling access for users who are already logged in, not for users who aren't logged in. If this is not the case, could someone please explain how I would implement my ACL to check this?
I greatly appreciate any help since the search for this answer has given me really mixed solutions, and most of them I don't like or don't seem like clean solutions. Like a Session Manager, which is basically what I'm doing, but pretending not to. =/
UPDATED index.php (my solution)
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new MapperFactory($db), new DomainFactory);
$serviceFactory->setDefaultNamespace('\\MyApp\\Service');
include CONFIG_PATH.'routes.php';
$request = new Request();
$router = new Router($routes,$request);
/*
* Find matched route.
*
* If matched route, add resource name
* and command to the request object.
*/
$router->route();
$session = $serviceFactory->create('Session');
/*
* Whitelist Ip address, check if user is
* logged in and session hasn't expired.
*/
$session->authenticate();
/*
* Access Control List
*/
include CONFIG_PATH.'acl_settings.php';
$aclFactory = new AclFactory($roles,$resources,$rules);
$acl = $aclFactory->build();
$user = $session->currentUser();
$role = $user->role();
$resource = $request->getResourceName();
$command = $request->getCommand();
// User trying to access unauthorized page
if (!$acl->isAllowed($role, $resource, $command) {
$request->setResourceName('Session');
$request->setCommand('index');
if ($role === 'blocked') {
$request->setResourceName('Error');
}
}
/*
* Initialization of View
*/
$class = '\\MyApp\\View\\' . $request->getResourceName();
$view = new $class($serviceFactory, $acl);
/*
* Initialization of Controller
*/
$class = '\\MyApp\\Controller\\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view, $acl);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
$view->{$command}
$view->render();
I start the session and authorize the user in the Session model. The session's currentUser will have a role of 'guest' if not logged in, or 'blocked' if it's IP address is not in the whitelist. I wanted to implement the Controller wrapper as suggested teresko's previous ACL post, but I needed something that would redirect the user's instead. I send them to their homepage (Session#index) if they try to access a page they aren't allowed to, or to Error#index if they are blocked. Session#index will let the View decide whether or not to display the homepage for a logged in user, or the login page if they aren't logged in (by checking the user's role). Maybe not the best solution, but doesn't seem too terrible.
Single Responsibility
Your session object is doing too many things. Sessions are more or less just for persistence across requests. The session shouldn't be doing any authentication logic. You store an identifier for the logged in user in the session, but the actual validation, logging in/out should be done in an authentication service.
Route Management
Importing different routes based on the users authentication status will not scale well and will be a pain to debug later when you have a lot more routes. It would be better to define all your routes in one place and use the authentication service to redirect if not authorized. I'm not very familiar with that router but looking at the documentation you should be able to so something like
$router->respond(function ($request, $response, $service, $app) {
$app->register('auth', function() {
return new AuthService();
}
});
Then on routes you need to be logged in for you can do something like
$router->respond('GET', '/resource', function ($request, $response, $service, $app) {
if( ! $app->auth->authenticate() )
return $response->redirect('/login', 401);
// ...
});
I am quite new to Laravel and I am making a small test application.
But now I am stuck on the following issue.
After a user logged in to the appilcation I want him/her to see a form where he/her have to fill in more information about him/her self before they can continue.
My problem is that I dont know where to put the code for this, I tried placing it in the Controller.php but it does not seem to work (It does sort of only on the main page but not on the profile page), I also tried to put it in the AppServiceProvider.php as my main menu always recives some data from the database but that also didnt seem to work.
Does any one have an idea where to place the following code ?
if (\Request::path() !== 'info' && (\Auth::User()->firstname === NULL || \Auth::User()->lastname === NULL)
{
return \Redirect::to('info');
}
The info page will be the page where the user will see the additional information form.
I think you should create a middleware for that.
First run php artisan make:middleware AccountInfoMiddleware to create the needed file.
Then open app/Http/Middleware/AccountInfoMiddleware.php and add your code to the handle() method:
public function handle($request, Closure $next)
{
$user = \Auth::user();
if($request->path() !== 'info' && $user && ($user->firstname === NULL || $user->lastname === NULL)
{
return redirect('info');
}
return $next($request);
}
After that you have different ways to use your middleware:
Add App\Http\Middleware\AccountInfoMiddleware to the $middleware array in app/Http/Kernel.php. This means the middleware will run for every request.
Add the same thing with a name to the $routeMiddleware in Kernel.php and use it for specific routes or enable it from the controller. For more information, visit the Laravel documentation
If you means you want to redirect user if the user dont have firstname and lastname, You can use middleware to solve your problem. Place your code in middleware file.
Create Middleware file
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Routing\Middleware;
class UserMiddleware implements Middleware {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (\Request::path() !== 'info' && (\Auth::User()->firstname === NULL || \Auth::User()->lastname === NULL)
{
return \Redirect::to('info');
}
return $next($request);
}
}
Register middleware for route in app/Http/Kernel.php
/**
* The application's global HTTP middleware stack.
*
* #var array
*/
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'user' => 'App\Http\Middleware\UserMiddleware' // Register this new middleware
];
Use that middleware for the route you want
Route::get('/', [
'middleware' => 'user',
'uses' => 'ExampleController#index'
]);
After you add the middleware for routes, every request, handle() function inside middleware file will be run to check if the user have firstname or lastname. If the user dont have, it will redirect to info and if the user have all the info, it will process next request.