3

I'm using Laravel 9 authentication. It's a successful login, but on the main page, it is not logged in. So I'm expecting it to authenticate.

Login controller

if (Auth::attempt([
    'x_username' => $data['username'],
    'x_user_password' => $data['password']
])) {
    return true; //login success
} else {
    return false;
}

Dashboard controller (it shows 2)

if(Auth::check()) {
    echo "1"; //not happened, but login before is success?
} else {
    echo "2"; 
}

$user = Auth::user();
var_dump($user); //it shows NULL

Models

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $table = 'tbl_x_user';
    
    protected $fillable = [
        'x_username',
        'x_user_email',
        'x_user_password',
    ];

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

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function getAuthPassword()
    {
        return $this->x_user_password;
    }
}

auth.php

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
],
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
],
'passwords' => [
    'users' => [
        'provider' => 'users',
        'table' => 'password_resets',
        'expire' => 60,
        'throttle' => 60,
    ],
],
'password_timeout' => 10800,

kernels.php

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    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,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \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,
        ],
    ];

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];
}

Notes

  • changing the primary key and primary key name with and without disable increment is not working
  • the session in /storage/framework/sessions/ is created
  • session.php is not edited (defaults laravel)
  • my password field using varchar(255)
Abdulla Nilam
  • 36,589
  • 17
  • 64
  • 85
Lezir Opav
  • 674
  • 3
  • 6
  • 21
  • If you change the primary key name you need to add `$primaryKey` poperty in your model like [here](https://laravel.com/docs/9.x/eloquent#primary-keys) – apokryfos Sep 20 '22 at 03:51
  • i'm already do that and still have same problem – Lezir Opav Sep 20 '22 at 04:03
  • Can you do an `Auth::user()`? – Sachin Bahukhandi Sep 20 '22 at 06:40
  • $user = Auth::user(); var_dump($user); it shows NULL; – Lezir Opav Sep 20 '22 at 06:43
  • 2
    I think there is some problem inside routes files with middleware group. Please share your routes file. – NIKUNJ PATEL Oct 04 '22 at 11:41
  • does `attempt` return true when user/password is invalid – Chinh Nguyen Oct 05 '22 at 08:03
  • you don't seem to be using the default auth system that comes with Laravel ... the password field used for the credentials passed to `attempt` **MUST** be named `password`; which does not relate to the actual name of the password field on the table as this field is left out of the query since the password is hashed and a hash check needs to be performed ... so it seems you are leaving out details here – lagbox Oct 06 '22 at 02:12
  • Is your route has `auth` middleware? because you must have to attach `auth` middleware with your desired route. – M-Khawar Oct 08 '22 at 18:12

4 Answers4

0

You can try this:

In Blade:

@auth
    // User is Authenticated
@else
    // User is Not Authenticated
@endauth
Hamid Raza
  • 28
  • 1
  • 6
0

Have you added the middleware to the route?

Route::get('/', function () {})->middleware('auth');

or you can do this inside the controller

public function __construct()
{
   $this->middleware('auth');
}
samurai
  • 29
  • 6
0

After wasting a couple of hours found a solution for this. The issue cause is

auth()->attempt always check fields such as username, email, password. In your case, you have used a custom column name on the table.

The solution that I found is

  1. Installed two new libraries via composer. (Mappable)
  2. Mapped your custom table fields to standard nameing fields.

Bonus

Now, you can use username to get the field data. Ex:

$user = User::where(['username' => 'ab'])->first();
$user->username(); # bonus

Install Mappable and Eloquence

composer require sofa/eloquence 
composer require sofa/eloquence-mappable

In the User model

Import

use Sofa\Eloquence\Eloquence;
use Sofa\Eloquence\Mappable;

use

use Eloquence, Mappable;

In model body

protected array $maps = [
    'username' => 'x_username', # I used x_username as my local test 
    'password' => 'x_user_password',
];

In Controller

if (auth()->attempt(['username' => 'ab', 'password' => 'ab@123'])) {
    return view('welcome');
} else {
    return "not logged in";
}

IMPORTANT: Laravel encrypts passwords using Hashing. So make sure your password is hashed in the DB. Because Auth attempt() always works with Hashing


For testing purposes, I used welcome.blade.php.

@auth
    <h3>Logged In</h3>
@else
    <h3>Not yet Logged In</h3>
@endauth

And the output was

enter image description here


Test data

Migration

Schema::create('x_users', static function (Blueprint $table) {
    $table->increments('id');
    $table->string('x_username');
    $table->string('x_user_email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('x_user_password');
    $table->rememberToken();
    $table->timestamps();
});

User create

User::create([
    'x_username' => 'ab',
    'x_user_email' => 'ab@abc.com',
    'x_user_password' => Hash::make('ab@123'),
]);

In model

Used $guarded to ignore MassAssignment error (Not recommended. Just for time save)

protected $table = 'x_users';
protected $guarded = [];
Abdulla Nilam
  • 36,589
  • 17
  • 64
  • 85
0

Set up

I've tested it on a new Laravel app with simple Breeze authentication, see below for the set-up used.

$ laravel new testing
$ cd testing
$ composer require laravel/breeze --dev
$ php artisan breeze:install

Then I've updated the users migration like this:

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('x_username')->unique();
    $table->string('x_user_email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('x_user_password');
    $table->rememberToken();
    $table->timestamps();
});

Then I've run php artisan migrate:fresh, which will rebuild the database from scratch.

Inside the RegisteredUserController I've made an update to use the new fields you specified:

$user = User::create([
    'x_username' => $request->name,
    'x_user_email' => $request->email,
    'x_user_password' => Hash::make($request->password),
]);

I've updated the model according to what you set in your question.

Then I've registered a new user through visiting /register.

I'm automatically logged in after registering, so I'll logout using the logout link provided in the navigation from Breeze.

Now, inside resources/views/auth/login.blade.php I've changed the email address field to use the username:

<div>
    <x-input-label for="username" value="Username" />

    <x-text-input id="username" class="block mt-1 w-full" type="text" name="username" :value="old('username')" required autofocus />

    <x-input-error :messages="$errors->get('username')" class="mt-2" />
</div>

Actually getting to the login problem.

So now that the project is set up, let's have a look at what isn't working. To log in with the new fields we'll need to change the login code (in Breeze's case the App\Http\Requests\Auth\LoginRequest.

I've changed the authenticate method inside the request as follows:

public function authenticate()
{
    $this->ensureIsNotRateLimited();

    if (! Auth::attempt([
        'x_username' => $this->input('username'),
        'password' => $this->input('password'),
    ], $this->boolean('remember'))) {
        RateLimiter::hit($this->throttleKey());

        throw ValidationException::withMessages([
            'email' => trans('auth.failed'),
        ]);
    }

    RateLimiter::clear($this->throttleKey());
}

Note that credentials should contain two types of fields:

  1. One or more fields that are unencrypted, and can thus be used to fetch the possible user from the database.
  2. The password field (which is encrypted).

In the example code the field that's used to fetch the user from the database is x_username (Laravel filters out all fields containing the key password, and then uses the others to find a user).

The field used to verify the password is password. You've already specified the getAuthPassword method in your user to make sure that it uses the x_user_password field. Laravel will thus check if the password credential entered can be hashed to the same value as in x_user_password.

As far as I can see this is the only mistake you made: not using the key password in your Auth::attempt() call.

If you have any issues with your session not sticking, ensure that the session cookie is properly sent to the server. Check your browser console under "Storage" and then "Cookies" to see if the cookie was sent to the browser. If the cookie isn't sent, the session will not be recognised, and you will not be logged in.

Sometimes the cookie will not be sent to the server, for example if you set secure => true in session.php (which can be done through SESSION_SECURE_COOKIE=true in your .env). The cookie will then only be sent if you're visiting your app over HTTPS.

Other than the secure setting, the domain and path of course also matter. Since you've said you hadn't touched the session.php configuration I'll assume that there's no issues there.

JiFus
  • 959
  • 7
  • 19
  • This is wrong. In `auth::attempt()` it will check fields like `username` and `password`. Test it, before you answer – Abdulla Nilam Oct 10 '22 at 09:46
  • @AbdullaNilam I'm not sure where you found that. Of course I will not post an answer without testing it when I provide an answer. He's using the eloquent user provider, see `Illuminate\Auth\EloquentUserProvider@retrieveByCredentials`. It allows you to use any key, so it's not required for it to be `username`. The only required one is `password` since it's hardcoded in `Illuminate\Auth\EloquentUserProvider@validateCredentials`. – JiFus Oct 19 '22 at 09:32