I am using Laravel 8 and Jetstream. As I can see my user can login to my website from multiple devices and access the contents. So I want to stop them. If a user is successfully logged in to a second device then they will logout from the first device. In short- users can access my content from one device at a time. No multiple device login.
In my earlier project with Laravel Breeze I have done it easily because the controller is present there. In Laravel Jetstream I am confused. Anyone, please suggest.
--- Update ------
Tried this but had no luck. Still, my user can login from two different devices
In FortifyServiceProvider:
public function boot()
{
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->email.$request->ip());
});
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
//This is what I added
Fortify::authenticateUsing(function (Request $request) {
$user = User::where('email', $request->email)->first();
if ($user &&
Hash::check($request->password, $user->password)) {
auth()->logoutOtherDevices($request->password);
return $user;
}
});
}
You can simply enable the commented AuthenticateSession middleware in your app/Http/Kernel.php file.
/**
* 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, // --> this one
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
After the login, you should logout the other sessions.
/**
* Invalidate other sessions for the current user.
*
* The application must be using the AuthenticateSession middleware.
*
* #param string $password
* #param string $attribute
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*
* #throws \Illuminate\Auth\AuthenticationException
*/
public function logoutOtherDevices($password, $attribute = 'password')
I have tried many other methods and finally, I came to the conclusion that "logoutOtherDevices" will never work if you are using Laravel Jetstream. It may work if you apply non-standard hacks which I don't want.
So in my case, I tackled it in my way. After a user successfully logged in, they were redirected to their dashboard page. In the dashboard controller, I check the session table (in the database) and remove users' session records skipping the session of their current device.
Anyone facing such a problem can use my 100% working method. This is the example -
$this_device_session = \Session::getId(); //user's current session id is stored
//check in sessions table. If record exist then delete it skipping the current session.
if(DB::table('sessions')->where('id','!=',$this_device_session)->where('user_id',Auth::user()->id)->exists()) {
//delete their session
DB::table('sessions')->where('id','!=',$this_device_session)->where('user_id',Auth::user()->id)->delete();
}
return view('dashboard'); //user's dashboard page or any page you want
Related
Is there any way to limit users in my project for number of posts. For example, I want my users can create a maximum 10 posts each one. So one user has 10 posts? Is it something with hasMany or something else? Please help find a solution. Thank you
By definition
Middleware provide a convenient mechanism for filtering HTTP requests
entering your application. For example, Laravel includes a middleware
that verifies the user of your application is authenticated. If the
user is not authenticated, the middleware will redirect the user to
the login screen. However, if the user is authenticated, the
middleware will allow the request to proceed further into the
application.
To prevent user from adding more than 10 posts you need to create a middleware to protect your posts/create route
To create a new middleware, use the make:middleware Artisan command:
php artisan make:middleware CheckUserPostsNumber
This command will place a new CheckUserPostsNumber class within your app/Http/Middleware directory. In this middleware, we will only allow access to the posts/create route if the user posts number < than 10. Otherwise, you will redirect the user back to the home URI:
<?php
namespace App\Http\Middleware;
use Illuminate\Support\Facades\Auth;
use Closure;
class CheckUserPostsNumber
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::user()->posts->count() >= 10) {
return redirect('home');
}
return $next($request);
}
}
Assigning Middleware To Routes
you would like to assign middleware to specific routes, you should first assign the middleware a key in your app/Http/Kernel.php file. By default, the $routeMiddleware property of this class contains entries for the middleware included with Laravel. To add your own, append it to this list and assign it a key of your choosing:
// Within App\Http\Kernel Class...
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
//...
'checkUserPostsNumber' => 'App\Http\Middleware\checkUserPostsNumber'
];
Once the middleware has been defined in the HTTP kernel, you may use the middleware method to assign middleware to a route:
Route::get('posts/create', function () {
//
})->middleware('auth', 'checkUserPostsNumber');
Docs
if ($user->posts->count() >= 10) {
//
}
I am new in learning laravel. Currently I have been stuck in the Laravel Policy,could you please kindly help with directing me how to add policy to my project?
I would like to make just only the Administrator User be able to see the 'Administration Dashboard' by using Laravel Policies Rules but failed. Actually every registered user is able to see that entrance(just like the attached picture showing below).
The user name is uu#gmail.com and the password is uuuuuu.
Please go to my testing website http://lookoko.com/ and log with the user name and password you will see that Administration Dashboard list in the drop-down lists.
To create a new middleware, use the make:middleware Artisan command:
php artisan make:middleware AdminMiddleware
This command will place a new AdminMiddleware class within your app/Http/Middleware directory. In this middleware, we will only allow access to the route if the logged-in user is admin. Otherwise, we will redirect the users back to the home URI.
<?php
namespace App\Http\Middleware;
use Closure;
class AdminMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (! auth()->user()->is_admin) {
return redirect()->route('home');
}
return $next($request);
}
}
Here I am assumming you have a column in user table named is_admin
Registering Middleware
You should assign the middleware a key in your app/Http/Kernel.php file. By default, the $routeMiddleware property of this class contains entries for the middleware included with Laravel. To add your own, simply append it to this list and assign it a key of your choosing. For example:
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
...
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'admin' => \App\Http\Middleware\AdminMiddleware::class,
];
Once the middleware has been defined in the HTTP kernel, you may use the group method to assign middleware to group of routes:
Route::group(['middleware' => ['admin']], function () {
// all your admin routes comes here
});
Docs
I have a question, I'm currently developing a little site with Laravel 5.3 and I'm using the Basic Auth from them for users to register and login.
Now I want the following: Everybody can register and login, but if I click on a button (as an admin), I can "block" one specific user (for example if he did something not allowed), I don't completely delete the row in the database, but somehow make sure that if the user tries to login he get's a message saying something like "you can't login any more, your account is blocked, contact admin for more info" or something similar. The question is: Whats the best way to do this? I didn't find something built in, correct me if I'm wrong...
Ofcourse, I could just alter the users table and add a column called "blocked", set to false normally, then with the button, set it to true and then when logging in somehow checking for this value and (if it's true) showing this message and not allowing log in. Is this the best way to do this? If yes, where would I have to check for this value and how can I show the message then? If not, whats the better way?
I would do what you're suggesting - use a blocked or active column to indicate if the user should be able to log in. When I've done something similar in the past, to check this value upon login, I moved the out-of-the-box login function into my LoginController and added to it a bit. My login method now looks like this:
/**
* Handle a login request to the application.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function login(Request $request)
{
$this->validateLogin($request);
$user = User::where('email', $request->email)->firstOrFail();
if ( $user && !$user->active ) {
return $this->sendLockedAccountResponse($request);
}
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
I also added these functions to handle users who weren't active:
/**
* Get the locked account response instance.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
protected function sendLockedAccountResponse(Request $request)
{
return redirect()->back()
->withInput($request->only($this->loginUsername(), 'remember'))
->withErrors([
$this->loginUsername() => $this->getLockedAccountMessage(),
]);
}
/**
* Get the locked account message.
*
* #return string
*/
protected function getLockedAccountMessage()
{
return Lang::has('auth.locked')
? Lang::get('auth.locked')
: 'Your account is inactive. Please contact the Support Desk for help.';
}
You can use soft deleting feature.
In addition to actually removing records from your database, Eloquent can also "soft delete" models. When models are soft deleted, they are not actually removed from your database. Instead, a deleted_at attribute is set on the model and inserted into the database. If a model has a non-null deleted_at value, the model has been soft deleted.
step1:
add new field to the User table called ‘status’ (1:enabled, 0:disabed)
step2:
to block the web login , in app/Http/Controllers/Auth/LoginController.php add the follwoing function:
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(\Illuminate\Http\Request $request)
{
$credentials = $request->only($this->username(), ‘password’);
return array_add($credentials, ‘status’, ‘1’);
}
Step3:
to block the user when using passport authentication ( token ) , in the User.php model add the following function :
public function findForPassport($identifier) {
return User::orWhere(‘email’, $identifier)->where(‘status’, 1)->first();
}
refer to this link ( tutorial) will help you : https://medium.com/#mshanak/solved-tutorial-laravel-5-3-disable-enable-block-user-login-web-passport-oauth-4bfb74b0c810
There is a package which not only blocks users but also lets you to monitor them before making a decision to block them or not.
Laravel Surveillance : https://github.com/neelkanthk/laravel-surveillance
Solved: this link ( tutorial) will help you : https://medium.com/#mshanak/solved-tutorial-laravel-5-3-disable-enable-block-user-login-web-passport-oauth-4bfb74b0c810
step1:
add new field to the User table called ‘status’ (1:enabled, 0:disabed)
step2:
to block the web login , in app/Http/Controllers/Auth/LoginController.php add the follwoing function:
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(\Illuminate\Http\Request $request)
{
$credentials = $request->only($this->username(), ‘password’);
return array_add($credentials, ‘status’, ‘1’);
}
Step3:
to block the user when using passport authentication ( token ) , in the User.php model add the following function :
public function findForPassport($identifier) {
return User::orWhere(‘email’, $identifier)->where(‘status’, 1)->first();
}
Done :)
I am using the default laravel 5.2 authorization library.
I am using the remember me functionality with a 24 hour time out.
I need to log user_id with IP address in an audit table after each user visit to the webste.
I have done this using the EventServiceProvider, listening for the login event
and then using the request object to identify the IP and persisting to the database.
protected $listen = [
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
This works for all logins where the login screen is used with username and password supplied by the user.
However if the user accesses the website via the session cookie (ie- login within 24 hours of the previous login) the login is not recorded. So it must follow a different path within the authorization library.
In the API documentation (https://laravel.com/api/5.2/Illuminate/Auth/Events.html) there is no event like for example 'LogInViaCookie'.
I have tried adding a method to AuthController-
/**
* Add audit.
*
* #param $request
* #param $user
*/
protected function authenticated(Request $request, $user)
{
try
{
$audit = Audit::create(['internet_protocol' => $request->ip,
'uid' => $user->id,
'general' => 'viaCookie']);
$audit->save();
}catch(\Exception $e){
Log::error($e->getMessage());
}
}
This authenticated method, as I understand it, should be fired from the AuthenticatesUsers trait (line 115)-
if (method_exists($this, 'authenticated')) {
return $this->authenticated($request, Auth::guard($this->getGuard())->user());
}
However, it appears this method is not fired when logging in via cookie.
How can I listen for and capture this type of 'LogInViaCookie' event to update my audit table?
UPDATE 30/05/16 21:21
Rifki- That sounds like a good solution. I've tried implementing but I can't get it to work.
This is my viaCookie.php-
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class viaCookie
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::viaRemember()) {
dd('logged in via cookie');
}
return $next($request);
}
}
And my Kernel.php update-
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,
\App\Http\Middleware\viaCookie::class,
],
Am I missing something? dd isn't being fired.
I have a scenario where I am putting certain information in session after user is successfully logged in. I am using Laravel 5.2. As in Laravel 5.2 you just redirect user to /login and specify protected $redirectTo variable and the rest is cared by Laravel itself. Now I want whenever user is logged in successfully, I want to put some information in session. Currently what I am doing when user is redirected to my protected $redirectTo url I put information in that like
\Session::put('user.data', $someData);
It works fine in normal scenario. But say user is logged out and tries to access some auth protected url, say example.com/showUsers saved in his browser. Now Laravel will check its a login protected url it will redirect user to login page, user is successfully logged in and Now in this case Laravel redirects the user to previous url. Now the information that I want to put in session isn't in session so I get error.
I have checked Auth/AuthController, Authication.php in middleware but don't find any place.
Currently I am putting it in /app/Http/Middleware/Authenticate.php before the last statement return $next($request); like this
if(empty(\Session::get('user.data'))) {
$someData = getSomeData();
\Session::put('user.agency', $someData);
}
return $next($request);
I don't know its a good approach or not So can any body let me know am I doing good or where do I put my session code that is executed every time user logs in.
As far as I understood, you are using the AuthControllerand AuthenticatesUsers trait to log your users in which is the default of Laravel.
In AuthenticatesUser trait on line 114, you will see a method definition check which was written for the users of the framework to define additional login related work.
/**
* Send the response after the user was authenticated.
*
* #param \Illuminate\Http\Request $request
* #param bool $throttles
* #return \Illuminate\Http\Response
*/
protected function handleUserWasAuthenticated(Request $request, $throttles)
{
if ($throttles) {
$this->clearLoginAttempts($request);
}
if (method_exists($this, 'authenticated')) {
return $this->authenticated($request, Auth::guard($this->getGuard())->user());
}
return redirect()->intended($this->redirectPath());
}
In your AuthController class,
creating a method called authenticated and doing your work there should be a more expected behaviour.
/**
* Return the authenticated response.
*
* #param $request
* #param $user
* #return \Illuminate\Contracts\Routing\ResponseFactory
*/
protected function authenticated(Request $request, $user)
{
// Here.
}