Laravel 5.2 - event handling - log a user login via remember me - events

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.

Related

Laravel Jetstream - Stop multiple device login

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

allow only requests from domain and block other sources

I want to allow some routes to only respond to requests made by my front-end website, meaning block other sources like postman and allow only the request from domain of the front-end for security reasons.
Is it possible?
for example, I have a webpage to check the dynamic value of the link and verify if the token on link is on database or not, I can think of putting captcha so a bot can't check all possible combinations, but it's not 100% safe.
Laravel API Throttling
if your main problem is bots getting all combinations, the throttling middleware alongside using captchas will help you with that.
By default, all your API routes (in routes/api.php) allow for a maximum of 60 requests per minute per IP. You can modify this amount to your own need in app/Http/Kernel.php file by changing the throttle:api section:
/**
* 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,
],
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
changing it to throttle:30:1 for example will mean you will allow 30 requests per minute per ip.
if you only want some routes on your api to be throttled, you can use the middleware elsewhere:
Route::get('my-method', MyController::class)->middleware('throttle:30:1');
Create a middleware that limits hosts
if you want to limit exactly by domain, what you are looking for is probably a custom middleware. Middlewares allow you to inspect various request properties (including the request's host through $request->getHost()) and prevent any controllers or methods.
Although Laravel's default TrustHosts middleware provides a global host validation, you could create your own custom middleware for specific paths that would look like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class LocalOnly
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if($request->getHost() != 'localhost')
{
return response('', 400);
}
return $next($request);
}
}
Note: if you are creating new middlewares, you will need to register them. Laravel has its own guide on this here.
in this example, when used on any route, Laravel will reject any host other than localhost (so even 127.0.0.1 will be rejected)
Personally, I don't recommend doing this as the built-in throttling is a much more elegant solution but in case you really need to do it, here you go.
You can simply do this by creating middleware. I added some more efficient way to add restrications.
1- Create Middleware ApiBrowserRestricationMiddleware
2- Add this \App\Http\Middleware\ApiBrowserRestricationMiddleware::class in
App\Http\Kernel $middleware array
3- Add the code in ApiBrowserRestricationMiddleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ApiBrowserRestricationMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if(in_array($request->getHost(), ['127.0.0.1']) == false)
{
return response('', 400);
}
return $next($request);
}
}

how to redirect with https for particular page only in laravel

i want to redirect particular page only to https, rest of page will remain in normal http.
i want to do this for payment page only.after successful payment site will run with normal http.
so please help me for do this.
i already try this one.
Route::resource('paynow', ['uses' => 'Account\PaymentController', 'https' => true]);
but this will not work for me.
I would go about it by creating a custom middleware in app\http\middleware to intercept the request before it hits those routes.
<?php
namespace App\Http\Middleware;
use Closure;
class SecurePayment
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!$request->secure()) {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
}
Then add it to app/http/kernel.php in the route middleware group
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* #var array
*/
protected $routeMiddleware = [
.....
'secure-payment' => \App\Http\Middleware\SecurePayment::class,
];
and finally wrap your route in the group
Route::group(['middleware' => ['secure-payment']], function() {
Route::resource('paynow', ['uses' => 'Account\PaymentController']);
}):

Make session expiration redirect back to login?

When user logs in and is authenticated, I use Auth::user()->username; to show username of user on dashboard. However, for some reason when session expires the class Auth doesn't seem to work and dashboard page throws error as trying to get property of non-object for Auth::user()->username;. How can I redirect the user back to the login page when he clicks any link or refreshes the page after the session has expired?
I tried the Authenticate.php middleware but it always redirects back to login page,whatever you put the credentials either correct or incorrect.However,when I don't use this middleware it logins the user.Am I missing something?
Route.php
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
/*
Actions Handled By Resource Controller
Verb Path Action Route Name
GET /photo index photo.index
GET /photo/create create photo.create
POST /photo store photo.store
GET /photo/{photo} show photo.show
GET /photo/{photo}/edit edit photo.edit
PUT/PATCH /photo/{photo} update photo.update
DELETE /photo/{photo} destroy photo.destroy
Adding Additional Routes To Resource Controllers
If it becomes necessary to add additional routes to a resource controller beyond the default resource routes, you should define those routes before your call to Route::resource:
Route::get('photos/popular', 'PhotoController#method');
Route::resource('photos', 'PhotoController');
*/
// Display all SQL executed in Eloquent
// Event::listen('illuminate.query', function($query)
// {
// var_dump($query);
// });
define('ADMIN','admin');
define('SITE','site');
Route::group(['namespace' => ADMIN], function () {
Route::get('/','UserController#showLogin');
});
////////////////////////////////////Routes for backend///////////////////////////////////////////////////
Route::group(['prefix' => ADMIN,'middleware' => 'auth'], function () {
Route::group(['namespace' => ADMIN], function () {
//Route::get('/','EshopController#products');
//sumit routes for user registration
//Route::resource('users','UserController');
Route::get('/users/destroy/{id}','UserController#destroy');
Route::get('UserProf','UserController#userProf');
Route::get('users','UserController#index');
Route::get('/users/create','UserController#create');
Route::get('/users/adminEdit/{id}','UserController#adminEdit');
Route::post('/users/adminUpdate','UserController#adminUpdate');
Route::post('/users/store','UserController#store');
Route::get('/users/edit/{id}','UserController#edit');
Route::post('/users/update/{id}','UserController#update');
//airlines route
Route::get('airlines','AirlinesController#index');
Route::get('/airlines/create','AirlinesController#create');
Route::post('/airlines/store','AirlinesController#store');
Route::get('/airlines/edit/{id}','AirlinesController#edit');
Route::post('/airlines/update','AirlinesController#update');
Route::get('/airlines/destroy/{id}','AirlinesController#destroy');
//end sumit routes
//flight routes
Route::get('flights','FlightController#index');
Route::get('showFlightBook','FlightController#showFlightBook');
Route::get('flights/create','FlightController#create');
Route::post('flights/store','FlightController#store');
Route::get('flights/book','FlightController#book');
Route::get('flights/edit/{id}','FlightController#edit');
Route::post('flights/update','FlightController#update');
Route::get('flights/destroy/{id}','FlightController#destroy');
//Route::resource('flight','FlightController');
//hotels route
Route::get('hotels','HotelsController#index');
Route::get('/hotels/create','HotelsController#create');
Route::post('/hotels/store','HotelsController#store');
Route::get('/hotels/edit/{id}','HotelsController#edit');
Route::post('/hotels/update','HotelsController#update');
Route::get('/hotels/destroy/{id}','HotelsController#destroy');
//end sumit routes
//book-hotel routes
Route::get('hotel-book','HotelBookController#index');
Route::get('showHotelBook','HotelBookController#showHotelBook');
Route::get('hotel-book/create','HotelBookController#create');
Route::post('hotel-book/store','HotelBookController#store');
Route::get('hotel-book/book','HotelBookController#book');
Route::get('hotel-book/edit/{id}','HotelBookController#edit');
Route::post('hotel-book/update','HotelBookController#update');
Route::get('hotel-book/destroy/{id}','HotelBookController#destroy');
//Route::resource('hotel','HotelController');
//close flight routes
//for admin login
//Route::get('initlogin','UserController#lgnPage');
Route::get('login','UserController#showLogin');
// Route::get('privilegeLogin','UserController#privilegeLogin');
// Route::post('privilegeCheck','UserController#privilegeCheck');
Route::post('login','UserController#doLogin');
Route::get('/dashboard','DashController#index');
Route::get('logout','UserController#doLogout');
//user login
//Route::get('userLogin','UserController#showUserLogin');
//Route::post('userLogin','UserController#doUserLogin');
Route::get('/userDashboard','DashController#userIndex');
Route::get('Logout','UserController#doUserLogout');
//password reset
Route::get('forget-pass','UserController#showReset');
//Route::get('home', 'PassResetEmailController#index');
});
});
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
Authenticate.php:
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate {
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest())
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
// return redirect()->guest('auth/login');
return redirect()->guest('/');
}
}
return $next($request);
}
}
All you have to do is just put this constructor at the top of the controller for your dashboard. It seems Laravel has a middleware that handles this already. At least I can confirm from 5.4 and up.
public function __construct()
{
$this->middleware('auth');
}
If the session expires then you can redirect to log in like as
open this file app/Exceptions/Handler.php add this code
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
return redirect('/login');
}
return parent::render($request, $exception);
}
If you want a middleware to be run during every HTTP request to your application, simply list the middleware class in the $middleware property of your app/Http/Kernel.php class.
So, to protect every route from being accessed without authentication do this
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\VerifyCsrfToken',
'App\Http\Middleware\Authenticate',// add this line according to your namespace
];
it will redirect the user if not logged in. UPDATE Keep in mind that adding auth middleware as global will create redirect loop so avoid it.
Or if you want specific routes to be protected then attach the middleware auth to that route
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
I think you are not attaching the auth middleware to your routes.
Create a middleware like this
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
}
Then Group the routes and protect them like this
Route::group(['middleware' => 'auth'], function()
{
Route::get();
Route::get();
Route::get();
Route::get();
}
Offcourse, in the routes you have to specify your links etc, it will only allow the user when he is authenticated and if not then login page will be shown
To make session redirect to your login just add ->middleware('auth') in your router files as shown below I am using laravel 5.3
Ex:
Route::post('controllerName','folderName\fileName#fnNmae')->middleware('auth');
Or visit https://laravel.com/docs/5.3/authentication

Laravel 5 Middleware Doesn't work

I have problem with my custom middleware. It doesn't work. I have registered it in Kernel.php, only in $routeMiddleware. Here is my code:
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'test' => \App\Http\Middleware\TestMiddleware::class
];
}
Here is my Controller Code:
/**
* Middleware Activated
*/
public function __constructor()
{
$this->middleware('test');
}
and here is my custom middleware code:
protected $auth;
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!$this->auth->check())
{
return redirect('/');
}
return $next($request);
}
When I'm logout and type in url
profile/21
it shows me the profile of user with id 21. I want to prevent that with middleware but it won't work for me.
Does anyone have an idea how to do that or where is the mistake?
To make sure if the middleware gets triggered put something like die('middleware triggerd'); inside the handle function of the middleware.
I noticed you have function __constructor() instead of function __construct().
That might be the problem.
If it does trigger the middleware but you still have the same problem try replacing:
if (!$this->auth->check()) with if (!\Auth::check())

Resources