realtebo
realtebo

Reputation: 25691

Laravel: how to check an extra field while logging in, in addition to email and password?

I want my users validate their email address before the can access to website.

I think there is no a prebuilt system, so I added an enabled boolean field, default false, to my users table.

How can I handle this?

I'm looking at source code here: https://github.com/laravel/framework/blob/5.5/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php#L74-L79

/**
 * Attempt to log the user into the application.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function attemptLogin(Request $request)
{
    return $this->guard()->attempt(
        $this->credentials($request), $request->filled('remember')
    );
}
/**
 * Get the needed authorization credentials from the request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
protected function credentials(Request $request)
{
    return $request->only($this->username(), 'password');
}

I'd like to override the 'attempt' method of guard ... but I've not idea of what is a 'guard' (I'll for for documentation, I promise) and where / how to override or extend or implement to check the enabled field

EDIT

I found this documentation: https://laravel.com/docs/5.5/authentication#authenticating-users

I REALLY cannot understand where the call to this method come from... and how to populate the $email and $password vars...... Anyway I tried this, but I obtained NOTHING. No error, but user logged even if it has enabled set to false. Note that my 'username' field is litterally 'username'. I'm not using email field.

public function authenticate()
{
    if (Auth::attempt(['username' => $username, 'password' => $password, 'enabled' => true])) 
    {
        // Authentication passed...
        return redirect($this->redirectTo());
    }
}

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

Upvotes: 0

Views: 2387

Answers (2)

realtebo
realtebo

Reputation: 25691

I found a well- working solution

In my LoginController.php I override two function from the trait AuthenticatesUsers;.

Note that the 2nd override is just to override the message to the user.

/**
 * Attempt to log the user into the application.
 * NOTE: this override DO NOT uses at all the trait version
 * See: https://laravel.com/docs/5.5/authentication#authenticating-users
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function attemptLogin(Request $request)
{
    return Auth::attempt(
        $this->credentials($request) + ["enabled" => true],
        $request->filled('remember')
    );
}

/**
 * Get the failed login response instance.
 * NOTE: this override DO NOT uses at all the trait version
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Symfony\Component\HttpFoundation\Response
 *
 * @throws ValidationException
 */
protected function sendFailedLoginResponse(Request $request)
{
    throw ValidationException::withMessages([
        $this->username() => [trans('auth.failed_or_account_not_activated')],
    ]);
}

I add some explanations.

About attemptLogin

Default implementation of the framework is

    return $this->guard()->attempt(
        $this->credentials($request), $request->filled('remember')
    );

attempt accepts an array as first argument to be used for credentials, and a boolean as second argument, with the meaning of "remember user login'

The credentials function is implemented as

protected function credentials(Request $request)
{
    return $request->only($this->username(), 'password');
}

In my implementation, instead of passing username and password I pass

$this->credentials($request) + ["enabled" => true],

so I am passing 3 fields instead of only one; I pass

array [ 'username', 'password', 'enabled' ]

Internally the framework will do a lookup into db for a user with given username, password and enabled field values.

About sendFailedLoginResponse

This override is only for showing a more precise error message to the user. Because user in this case can have right username and password, but he/she could be not enabled.

So I don't want a probably misleading error like "wrong user credentials', but a more contextualized "wrong user credentials or account not activated".

Default sendFailedLoginResponse implementation is

    throw ValidationException::withMessages([
        $this->username() => [trans('auth.failed')],
    ]);

I simply asked Laravel to send a different string

throw ValidationException::withMessages([
    $this->username() => [trans('auth.failed_or_account_not_activated')],
]);

The strings to be translated must blaced into resources/lang/<lang_code>/auth.php

I added a string translation near the default

'failed' => 'Le credenziali inserite non corrispondono a nessun utente',

I added

'failed_or_account_not_activated' => "Le credenziali inserite non corrispondono a nessun utente oppure l'account non è stato ancora attivato",

Upvotes: 4

Blues Clues
Blues Clues

Reputation: 1848

I want my users validate their email address before the can access to website.

  • You can just use Auth::attempt() of laravel to login the user using the email. For more information, visit this link.

Note: If you also want to pass other field, you can do it like so:

Auth::attempt(['email' => $request->input('email'), 'enabled' => 1]);

Upvotes: 1

Related Questions