cshelswell's avatar

Another Auth::attempt issue. Fails everytime

Hi I've searched through previous questions but none seem to answer my problem. I need to have my own loginController so I'm using Auth::attempt but it fails every time.

I've tested back through the laravel framework and it seems in the Illuminate\Hashing\BcryptHasher class my password is failing on the check function. I've checked it against what's in my database and it is giving a different hash for some reason.

I create my users like this:

public function insert()
{
        $input = $this->request->input();

        $input['password'] = Hash::make($input['password']);

        $this->garage->insert($input);

        return redirect('garage-admin/garage/view-garages');

}

So my passwords are hashed already. (I've tried using bcrypt instead of Hash::make but no difference).

I then have a loginController

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;


class LoginController extends Controller
{
    protected $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function login()
    {
        $login_status = false;
        return view('auth.login',compact('login_status'));
    }

    public function attemptLogin()
    {
        $input = $this->request->input();

        if(Auth::attempt(['username' => $input['username'], 'password' => $input['password']]))
        {
            $login_status = 'logged in';
        }
        else
        {
            $login_status = 'logged in failed';
        }

        return view('auth.login', compact('login_status'));
    }

    public function username()
    {
        return 'username';
    }
}

but I just can't get it to pass the Auth::attempt. It's obviously trying as I can debug back to the password so it must be the hashing somewhere. I just can't figure it out.

Any help would be great - thanks

0 likes
16 replies
Screenbeetle's avatar

Hi @cshelswell

Have you considered using bcrypt helper directly - example below?

And it may be clearer to do this using an Eloquent model rather than the query builder. Eloquent also has advantages like adding timestamps.

You would just create a simple model for user.php then set up something like a RegistrationController to handle the creation of our new user e.g.

public function create(Request $request)
{
    return User::create(
    [
        'first_name' => $request->input('first_name'],
        etc etc
        ...

                'password' => bcrypt($request->input('password'),
    ]
    );
}
cshelswell's avatar

@Screenbeetle Thanks for the idea but that first function:

public function insert($input)

is actually using the model to enter the user. Or are you suggesting to not do the hashing in the controller and only in the model?

If so can I ask why that should make a difference? Just curious, if it works I'll be very happy :)

Snapey's avatar

you insert the password into wherever the garage relationship points, but Auth::attempt will be looking at the User model?

cshelswell's avatar

@snapey - thanks - I had updated my User model to be as such:

<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Authenticatable
{
    use Notifiable;

    use SoftDeletes;

    protected $primaryKey = 'garage_id';
    protected $table = 'garage';
    protected $dates = ['deleted_at'];

    protected $fillable = [
        'username', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

}

So I was hoping that'd work. It's certainly how I've done it in the past

Snapey's avatar

its somewhat of a strange pattern

personally, users are users, they should belong to a garage rather than the garage itself logging in.

anyway, i assume when you do the insert it sets garage_id and password fields, but your login form inputs username?

Where do you set username whilst doing the insert?

cshelswell's avatar

@snapey it's actually a real pain. I don't want a users table as they'll all be garages instead (essentially a user). In straight PHP I can do this easily but I'm finding it tough in Laravel.

Basically, and as weird as this may sound it's what the client wants. They need one username but two passwords. One password will be the admin password the other will be a basic level password so I need to test against both as they login. My preference, obviously would be to have two usernames but they don't want that.

and yes you're correct about the insert of the garage the insert is the following (i've stripped down some of the not so interesting parts of it):

public function insert($input)
{
        $garage = new Garage;

        $garage->email = $input['email'];
        $garage->username = $input['username'];
        $garage->password = $input['password']; //hashed in the controller
        $garage->admin_password = $input['admin_password']; //hashed in the controller too but I'd remove that bit to save confusion.
        $garage->garage_name = $input['garage_name'];
        
        return $garage->save();
    }

So essentially I want to have a 'user' table called 'garage'. I know it is trying to do the password check on the right table after dropping a few dd() in to the laravel code it's just the hashed password isn't matching up.

It's very similar to another site I made, I just can't figure why this one puts a different hashed password in to the database than it pulls to check

thanks so much for your help :)

Snapey's avatar
Snapey
Best Answer
Level 122

A few points

  1. You cannot compare the hashes. The bcrypt algorithm will never produce the same hash twice. Don't ask me how they are checked, just trust that they are.

  2. Are you sure that the password in the controller is the right plaintext. I've seen people struggle for days hashing null because the form field was 'pwd' or 'Password' and they are just grabbing 'password'

  3. You seem to be missing quite a few fields on $fillable in your model

  4. You don't need to use $request->input->field, just $request->field will work fine

  5. You should validate that username is unique as you cannot support two usernames being the same.

Personally, my approach would be;

a) standard Auth b) add a garage_admin boolean column to the users table c) add a garage_id column to the users table d) change to using username instead of email (plenty of tutorials for this)

each garage can then have two or more usernames (little or nothing to change if they decide they need extra), one admin user but more if required in the future, and can leverage all the existing forgot password, login, logout etc functionality.

1 like
cshelswell's avatar

@snapey thanks for that. Num 1 & 2 I think I'm good with (like i say i have this up an running on a 5.4 laravel already, i'm just using a different file structure with repositories).

Password is definitely plaintext (I've dd'd everywhere I can :) )

Number 3. I'm not sure about what else should be in there, that might be my issue?

Number 4: cool, but it shouldn't really make a difference should it?

Number 5: The username is unique in the database, I was planning on adding validation afterwards. I just wanted to get the login working first. There are only 3 test users in there anyway just now :).

Your approach actually sounds quite sensible and separates the users from the garages which would be good. I hadn't thought of that!

I'll have a play with that tomorrow.

It still doesn't make a lot of sense why it's not working anyway as it does seem to be hitting all right spots and somewhere along the line 2 hashes must match otherwise how do you login? Unless I've got that wrong ?

Thanks again

cshelswell's avatar

@madman nice one thanks - I'm just about to shutdown but I'll give that a try tomorrow.

I know it's the hashes not matching up - I've just not been able to work out why :)

thanks!

Snapey's avatar

The hashes will never match.

tinker;

>>> bcrypt('hello')
=> "$2y$10$FEc1oJ5ZkFwmRh.vDyAFp.KCOmaAarU/YosQUd3q9cqe/yzPF0bk."
>>> bcrypt('hello')
=> "$2y$10$5sWzeIQPz95PFiLCF2Lzo.mf2jxdOfOAYP9ODLPM73ym/il09m3ny"
>>> bcrypt('hello')
=> "$2y$10$o3HpLXnOD7tuTPHyIK2qJOavGc7Bj.Av5AWVSBmM37j.9iqt10udS"
>>> Hash::check('hello','$2y$10$FEc1oJ5ZkFwmRh.vDyAFp.KCOmaAarU/YosQUd3q9cqe/yzPF0bk.')
=> true
>>> Hash::check('hello','$2y$10$5sWzeIQPz95PFiLCF2Lzo.mf2jxdOfOAYP9ODLPM73ym/il09m3ny')
=> true
>>> Hash::check('hello','$2y$10$o3HpLXnOD7tuTPHyIK2qJOavGc7Bj.Av5AWVSBmM37j.9iqt10udS')
=> true
>>> Hash::check('hello','$2y$10$o3HpLXnOD7tuTPHyIK2qJOavGc7Bj.Av5AWVSBmM37j.9iqt10ud')
=> false
>>>

three examples of hashing hello

three examples of comparing hello to each of the three hashes

final example with last character removed from the hash

use this principle to test the hash that you have in the database.

Snapey's avatar

I know it's the hashes not matching up - I've just not been able to work out why :)

not necessarily the password - it also depends on finding the right record in the database using the username field.

cshelswell's avatar

@Snapey just read your approach again. I don't think that would work as the whole idea is to only have 1 username name. It's a pain as personally it would obviously be much easier to have "username" and "username_admin"

The username is definitely unique and when I try to login I'm getting the correct query:

select * from `garage` where `username` = 'test' and `garage`.`deleted_at` is null limit 1

So I can only imagine it's getting the correct record. I've tested with a couple of users and I'm definitely using the correct password.

It's so annoying as this all works perfectly on another site.

Thanks for your help :)

cshelswell's avatar

@Snapey ok so this is a little embarrassing... You might have been right about the password thing. I'd been trying to use the admin password.

(going to go hang my head in shame now) :)

jekinney's avatar

Why $input = $request every time?

Just use the request, clean up your code some instead of two variables that point to the same object, then once you manipulate the password (array instead of oop?) you now have two different request objects in memory. Do it enough and your app will be slow (probably blame Laravel too :().

1 like
cshelswell's avatar

@jekinney funny you should say that I'm just editing the code to remove all that now :)

thanks

1 like

Please or to participate in this conversation.