Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

PHP

Jenny Swift
Jenny Swift
21,999 Points

how to add a condition to Laravel's login

Hi, I am trying to add a custom condition for logging in a user (with Laravel), so that I can prevent a brute force attack. So for example, only letting the user log in if they have less than 3 failed log-in attempts in the last 10 minutes.

I tried adding the following code to AuthController.php (it is a simplified version of the code provided in the docs) to test if it controls who can log in, but it did not work. I'm not sure where I would need to call the function:

public function authenticate()
    {
        if (Auth::attempt(['email' => 'someone@example.com']))
        {
            return redirect()->intended('dashboard');
        }
    }

I would rather not do the authentication manually. Is there a way of using the provided authentication but just adding an extra condition myself, please?

The reason I would rather not do the authentication manually is because I am still new to Laravel and have trouble reading it’s code and so I figure I could mess something up if I tried. But if I had a clear tutorial on how to do it maybe I would do it manually.

I have tried using Laravel Throttle as dawiyo suggested here, but I’m having trouble getting it to work, and, although I don’t mind using a package, I would really like to know if there is a way I can do it myself without a package.

2 Answers

Petros Sordinas
Petros Sordinas
16,181 Points

Jenny,

Although I would recommend you use Laravel Throttle, a quick and dirty way would be the following:

public function authenticate()
    {
        // Set login attempts and login time
        $loginAttempts = 1;

        // If session has login attempts, retrieve attempts counter and attempts time
        if (Session::has('loginAttempts')) 
        {
            $loginAttempts = Session::get('loginAttempts');
            $loginAttemptTime = Session::get('loginAttemptTime');

            // If attempts > 3 and time < 10 minutes
            if ($loginAttempts > 3 && (time() - $loginAttemptTime <= 600)
            {
                return redirect()-back()->with('error', 'maximum login attempts reached. Try again in a while');
            }
            // If time > 10 minutes, reset attempts counter and time in session
            if (time() - $loginAttemptTime > 600)
            {
                Session::put('loginAttempts', 1)
                Session::put('loginAttemptTime', time());
            }
        } else // If no login attempts stored, init login attempts and time
        {
            Session::put('loginAttempts', $loginAttempts);
            Session::put('loginAttemptTime', time())
        }
        // If auth ok, redirect to restricted area
        if (Auth::attempt(['email' => 'someone@example.com']))
        {
            return redirect()->intended('dashboard');
        }
        // Increment login attempts
        Session::put('loginAttempts', $loginAttempts + 1);
    }

I wrote this out of my head without testing it, so it may not work as it is... just giving you an idea.

The idea is that you save the login attempts counter and login attempt time in the session. If the time elapsed is less than 10 mins and the attempt time is > 3, go back with error. If time elapsed is > 10 mins, reset the variables in the session. In any other case, increment the attempt counter.

If your application is fairly large, I would advise against putting this code in the controller and instead refactor it to a class of its own, but that is another topic entirely.

Jenny Swift
Jenny Swift
21,999 Points

Hi Petros, thanks so much for taking the time to respond! Is it safe to use a session to do it, though? I thought bots would be able to use many computers and therefore I would need to keep track of login attempts for a specific user's email address in the database so that it wouldn't matter if the failed logins were with multiple computers?

Jenny Swift
Jenny Swift
21,999 Points

And would you mind please helping me with Laravel Throttle? I've followed the installation and configuration instructions. But when I try the following example code from the docs (replacing 'foo' with my own path, and using smaller numbers for the throttle parameters), it doesn't seem to work:

use Illuminate\Support\Facades\Route;

Route::get('foo', array('before' => 'throttle:50,30', function () {
    return 'Why herro there!';
}, ));

I have also tried this other example from the docs, and each time I refresh the page, count($throttler) is still 1:

use GrahamCampbell\Throttle\Facades\Throttle;
use Illuminate\Support\Facades\Request;

// let's quickly get the current request object
$request = Request::getFacadeRoot();

// now let's get a throttler object for that request
// we'll use the same config as in the previous example
// note that only the first parameter is "required"
$throttler = Throttle::get($request, 50, 30);

// let's check if we've gone over the limit
var_dump($thottler->check());

// we implement Countable
var_dump(count($thottler));

I'm assuming $thottler is a typo in the example and should be $throttler.

I figure count($throttler) should increment each time I load the page? But I'm not sure how to get it to do that.

Petros Sordinas
Petros Sordinas
16,181 Points

If you are worried about brute force attacks of that scale, I would seriously recommend you go with some proven and tested solution :)

You can include the email address in the stored variables and check against that (and time and attempts).

You could also implement the same idea using a database table and including the email with the attempt and attemptTime variables. That would complicate implementation a little bit, but it all depends on what you want to do.