Laravel: How to let user login with Email or Username
Tags: laravel, tips
Published: 2019-06-04 14:34:07

---
title: Laravel: How to let user login with Email or Username 
published: true
description: Ever wanted to let your user login either with email or username? This is how you might do it. (Friendly for Beginner)
tags: laravel, tips
---

Laravel by default only let users to login with their Email Address. Now, what if you want to let your users to login with email or even username..?

To achieve this, you only have to change some codes in the Laravel default authentication codes.

Let's start. And I assume you've already set up your Laravel project and migrating Laravel default auth. If you haven't just write command `php artisan make:auth` and then `php artisan migrate`. 

The commands above make Laravel provides basic and default authentication for you, so you don't have to make Login, Register, Forgot Password & Reset Password manually.

### Step by step

Open `loginController.php` 
By default, you will only see these codes inside your login controller

```php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }
}
```

This controller only has attributes and one constructor, so how does it handle the login function? It's where the `OOP` plays its part.

Notice there's `use AuthenticatesUsers;` statement, and this is where Laravel built its default login functionality. So now, let's check the `AuthenticateUsers` class.

```php
<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins;

    /**
     * Show the application's login form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showLoginForm()
    {
        return view('auth.login');
    }

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function login(Request $request)
    {
        $this->validateLogin($request);

        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if (method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

    /**
     * Validate the user login request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    protected function validateLogin(Request $request)
    {
        $request->validate([
            $this->username() => 'required|string',
            'password' => 'required|string',
        ]);
    }

    /**
     * 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');
    }

    /**
     * Send the response after the user was authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendLoginResponse(Request $request)
    {
        $request->session()->regenerate();

        $this->clearLoginAttempts($request);

        return $this->authenticated($request, $this->guard()->user())
                ?: redirect()->intended($this->redirectPath());
    }

    /**
     * The user has been authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  mixed  $user
     * @return mixed
     */
    protected function authenticated(Request $request, $user)
    {
        //
    }

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

    /**
     * Get the login username to be used by the controller.
     *
     * @return string
     */
    public function username()
    {
        return 'email';
    }

    /**
     * Log the user out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function logout(Request $request)
    {
        $this->guard()->logout();

        $request->session()->invalidate();

        return $this->loggedOut($request) ?: redirect('/');
    }

    /**
     * The user has logged out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    protected function loggedOut(Request $request)
    {
        //
    }

    /**
     * Get the guard to be used during authentication.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return Auth::guard();
    }
}

```

If you are a beginner and just new to the `OOP` world, you might be feeling overwhelming when you look at these lines of code. But don't. Now we've taken a look inside the class that handles login functionality, and now your question would likely be "how do we customize it to fulfil our needs?"

Well, you don't! Remember that the location of' AuthenticateUsers' class is at `Illuminate\Foundation\Auth\AuthenticatesUsers` under `Vendor` directory. Don't edit or change whatever lies in this class, or your Composer will upset!. 

Laravel is an `Object-Oriented` framework. So we are applying the overriding OOP method. 

We want our app to let user login with either email or username, so let's take a look at which method that handles this part inside `AuthenticateUsers`. And you may have already noticed that this method `username()` describe what it does.

```php
/**
* Get the login username to be used by the controller.
*
* @return string
*/
public function username()
{
    return 'email';
}
```

That comment above tells us that it returns `email` as the login username that used by the controller. Now, this is the method that we have to override.

Copy and paste that `username()` method to your `loginController.php`. Now instead of returning `email`, we need a way to return either `email` or `username`.

Now here's the part where we have to check our login form. In our default login form, look for the default email input and change the name from `name="email"` to `name="identity"`. Later, it sends `identity` as an HTTP request to our Laravel.

Back to `username()` method again, we need to implement a way to check whether the HTTP request `email` that comes in as an email address or username. We can do this by checking if there's an "@" within the data that sent. Luckily PHP has built a function to do this. If there's an "@", then the HTTP request `email` is indeed an email address. Otherwise, it's a username.

```php
/**
* Get the login username to be used by the controller.
*
* @return string
*/
public function username()
{
     $login = request()->input('identity');

     $field = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
     request()->merge([$field => $login]);

     return $field;
}
```

What the above code does is to filter the HTTP request that comes in if there's an @ or if it's an email by using `FILTER_VALIDATE_EMAIL`. If it's an email then return `email`, otherwise, return `username`. 

After doing this, we only have one to-do left, and it's to overriding the validation request. In your login controller class, add these codes below:

```php
/**
     * Validate the user login request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    protected function validateLogin(Request $request)
    {
        $messages = [
            'identity.required' => 'Email or username cannot be empty',
            'email.exists' => 'Email or username already registered',
            'username.exists' => 'Username is already registered',
            'password.required' => 'Password cannot be empty',
        ];

        $request->validate([
            'identity' => 'required|string',
            'password' => 'required|string',
            'email' => 'string|exists:users',
            'username' => 'string|exists:users',
        ], $messages);
    }
```

The default validation only checks for the email, now we've changed it to also check for both email and username.

Now you have a working solution to let your user login with either an email address or username. 

If you have any question, just let me know :)

*This post updated on Mei, 5th, 2020. Fixed some grammar mistakes, for a better reading experience for future readers.*