How to authenticate a user when the "username" field receives values that can reference different columns in the database table?

Asked

Viewed 87 times

2

I have a login form where I have the fields "username" and "password". The difficulty I have been finding is the following: I want to give the option of the user to log in by CPF, email or phone (in the "username" field). Since in the Attempt() method of the Auth() class, which I use to authenticate, I can only pass values that reference fixed columns. See how my code is in the login method:

public function login(Request $request){
      $array = ["error" => ''];
    
      $username = $request->input('username'); //Será passado o email,cpf ou telefone
      $password = $request->input('password');
    
      if($user && $password){
    
        $token = Auth()->attempt(['email' => $username, 'password' => $password]);
    
        if(!$token){
          return $this->unauthorized();
        }
        
        $array['token'] = $token;
      }
  }

Above, that $username variable can be Cpf, email, or phone (which are distinct columns from the users table).

In short, I want to give the opportunity for the user to log in with the option he wants between these 3 (Cpf, email and phone).

Roughly, just to illustrate my need, it would be something like this:

$token = Auth()->attempt(['email/cpf/phone' => $username, 'password' => $password]);

Could you help me with that ? If I am not clear, let us know in the comments that I edit and add information.

OBS: I’m wearing Laravel 7.

  • If you have a way fully deterministic to differentiate email of cpf and of phone provided by the user, you can dynamically set the first key of the associative array. Otherwise, the most ideal is to mount the query manually and, after validating the authentication, log the user using the method login (or setUser), see the API. I don’t know much about Laravel (I’ve never used it, but I think it’s the same method). Anyway, this is the idea, I hope I made it clear. :) [...]

  • 1

    looking at the documentation here I see no other way to do it than by retaining the call from Attempt, ex if(Auth()->Attempt(['email' => $username, 'password' => $password] || Auth()->Attempt(['Cpf' => $username, 'password' => $password]);

  • @Lucasmiranda, puts, I looked at the doc to hell too. I wanted to avoid the maximum does that. I would only do so if there was no other alternative.

  • @Luizfelipe , then, I asked the question more in order to know if there is something in the Lake that I use specifically for this, without making possible "juggling". If I don’t, I’ll look for alternatives like the ones you and Lucas mentioned.

2 answers

1


I needed to customize a login query in a work project and figured out how to do it. The process is undocumented and needs to read enough code to understand in detail how it works. The solution is simple: overwrite the method EloquentUserProvider::retrieveByCredentials, the original method has a $query where you can make your business rule with conditions orWhere.

For this you need to follow a few steps:

  1. Create a class that extends \Illuminate\Auth\EloquentUserProvider (in this example MeuEloquentUserProvider;
  2. Overwrite the method retrieveByCredentials according to your need;
  3. Inform the framework to use the custom class MeuEloquentUserProvider in the method register of some service Provider, recommend the \App\Providers\AuthServiceProvider:
use \Illuminate\Auth\AuthManager;

public function register()
{
    $this->app->extend(AuthManager::class, function(AuthManager $manager) {
        return $manager->provider('eloquent', function($app, $config) {
            return new MeuEloquentUserProvider($app['hash'], $config['model']);
        });
    });
}

The related methods are:

  • Wow, Pedrão. This answer was based on which version of Laravel ? Does it work in my version that I mentioned in the question ? Worse than Friday I did more engineering, rs, to make this situation work. I’ll look at yours tomorrow, see if it’s worth replacing with all the engineering I’ve been forced to do for not seeing anything that would satisfy my need.

  • 1

    It works. I used this structure in 5.5, it should work in 7 and 8 because the code involved has not changed almost anything.

  • 1

    Pedro, although his reply is useful, I consulted some programmers in Laravel (Laravel Brasil group of Telegram) and they said that she manages the code a little. I will accept your answer, because it is functional, but I will leave as an answer the alternative that I found to solve the problem and that I found to be more readable in case future mistakes happen. Thank you!

  • is absolutely right, precisely because this solution modifies the behavior of classes of the framework, in addition to being "hidden" from those who will maintain the code in the future

0

I made a customization (a separate method) that checks whether the data, which the user used to try to log in, matches one of the database columns (email, Cpf or phone). See this method that has been created:

public function verifyLogin($user){
        
   $u = User::where('email', $user)->orWhere('cpf', $user)->orWhere('phone',$user)->first()->toArray();
     
   if($u)
   {
     return  $u;
   }

   return false;
}

If one of the data matches, the user data is returned and in the Auth Attempt() method, we use the id of that user that was found to log in. See how the code looks in the login() method, which is the method that authenticates.

public function login(Request $request){

       $array = ["error" => ''];

       $username = $request->user; //Será passado o email,cpf ou telefone
       $password = $request->password;

    if($username && $password){

       //Chamando método para verificar o tipo de login
       $data = $this->verifyLogin($username);

          
       if(!$data)
       {
           $array["error"] = "Dados Inválidos";
           return $array;
       }

       $token = Auth()->attempt(['id' => $data['id'], 'password' => $password, 'is_active' => 1]);

       if(!$token){
          return $this->unauthorized();
       }

          
       $array['token'] = $token;
       
       $user = User::select('id', 'name', 'surname', 'email', 'internal_client_id', 'is_client','is_client_employee',
       'permission_id','is_active','is_spreadsheet')->where('id', '=', $data['id'])->get();
           
        $array['userData'] = $user;

        return $array;

   }else{
      $array = ["error" => 'Dados não enviados'];
   }

   return $array;
}

OBS: In my case, this logic was applied taking into account that the fields are "Unique" and that they are not mandatory.

OBS-2: These two methods are within the Authcontroller class.

OBS-3: The Illuminate Support Facades Auth, the Illuminate Http Request and the App Models User are imported into the file for the methods of these classes to be used in the Authcontroller class.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.