Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

muldev's avatar
Level 16

Laravel 5 Middleware Login with username or email

Hi,

I'm having trouble getting a Login page to work with Middleware to accept a username or email with Laravel 5.

Any chance on making a lesson about custom Authentication ?

or Is there an easy guide to follow ?

0 likes
22 replies
martinbean's avatar

@itbuff Can you explain in more detail how you’re trying to authenticate a user via middleware?

Usually you would have middleware to detect if the user is authenticated or not, rather than put the authentication logic itself in there—that would usually live within a controller method.

2 likes
martindilling's avatar

Maybe not the best way, but you could check if the given identifier contains an @ (of cause only if it isn't allowed as part of the username), and check either for the username or email?

muldev's avatar
Level 16

My first attempt which is all successful is not using the controller, rather just using the route file for example:

//region Authenticated Routes
    Route::group(['middleware' => 'auth'], function()
    {
        Route::get('feeds',          ['uses' => 'PagesController@feeds' ,  'as' => 'feeds']);
        Route::get('events',         ['uses' => 'EventsController@index' , 'as' => 'events']);
        Route::get('coc',            ['uses' => 'PagesController@coc' ,    'as' => 'coc']);
        Route::get('chat',           ['uses' => 'PagesController@chat' ,   'as' => 'chat']);

This works fine.

My problem is I am having a hard time finding an easy way to accept another field within the login.blade.php like username.

I can't find the validation methods and if you give me its location, can you also include a little sample to create my own class so as not to change Laravel's default methods.

muldev's avatar
Level 16

Here are the detail steps I have taken so far:

  1. in the create_user_table migration file:

        $table->string('username')->unique();
    
  2. in the User.php model

       protected $fillable = ['name','username', 'email', 'password'];
    
  3. add to the register.blade.php username:

              <input type="text" class="form-control" name="username" value="{{ old('username') }}">
    
  4. in the default registrar.php Service add the username:

        public function create(array $data)
        {
           return User::create([
              'name' => $data['name'],
              'email' => $data['email'],
              'username' => $data['username'],
              'password' => bcrypt($data['password']),
           ]);
        }
    
  5. create a middleware

        php artisan make:middleware MyMiddleware
    
  6. The code for the middleware:

        <?php namespace App\Http\Middleware;
         
        use Closure;
    
        class MyMiddleware
        {
           public function handle($request, Closure $next)
           {
              return Auth::onceBasic('username') ?: $next($request);
           }
        }
    
  7. In the kernel.php file add:

        'myauth' => 'App\Http\Middleware\MyMiddleware',
    
  8. Use MyMiddleware in routes.php:

        Route::get('action', ['uses' => 'ActionController@index','middleware'=>'myauth']);
        Route::post('action/create', ['uses' => 'ActionController@store','middleware'=>'myauth']);
    

If I go to a route with myauth only that page is authenticated, if I try and go to another page I have to log in again.

I still cannot get the login.blade to work with validation for a username or email instead of just email.

muldev's avatar
muldev
OP
Best Answer
Level 16

Ok I worked out an easy solution. not sure if it's the correct way, but it definitely works the way I want it.

  1. Copy the trait AuthenticatesAndRegistersUsers to your namespace or app directory
    cp vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesAndRegistersUsers.php app/MyAuthAndRegistersUsers.php
  1. Change the necessary method for username
    public function postLogin(Request $request)
        {
            $this->validate($request, [
                'username' => 'required', 'password' => 'required',
            ]);
    
            $credentials = $request->only('username', 'password');
    
            if ($this->auth->attempt($credentials, $request->has('remember')))
            {
                return redirect()->intended($this->redirectPath());
            }
    
            return redirect($this->loginPath())
                ->withInput($request->only('username', 'remember'))
                ->withErrors([
                    'username' => $this->getFailedLoginMessage(),
                ]);
        }
  1. Edit Http/Controllers/Auth/AuthController.php
    Use your trait you changed instead of the one provided by Laravel
    change app/ for YourNamespace/ if required
    use app/MyAuthAndRegistersUsers
    
    class AuthController extends Controller {
    
        use MyAuthAndRegistersUsers
        
        // . . .
    
    }
  1. Either create your own login.blade file or edit the existing one
    <input type="username" class="form-control" name="username" value="{{ old('username') }}">
  1. If you create your own or copy the original and modify the copy
    then change the trait method MyAuthAndRegistersUsers mentioned above.
        /**
         * Show the application login form.
         *
         * @return \Illuminate\Http\Response
         */
        public function getLogin()
        {
            return view('auth.mylogin');
        }
  1. That's it.
    You can now login with a username instead of email address.
muldev's avatar
Level 16

So not to make my original question a circular conversation, I could not find a way in Laravel 5 to login using a username. The validation for the login form happens to reside within the middleware logic. Hence the way provided above was my only working solution.

If someone can share an insight to a better method to authenticate a user using a custom field like username without raising more questions please provide a working solution.

I didn't think my question was so complicated.

ae's avatar

The easiest way is following, using @itbuff answer. Let's call input for username or email field: 'username'.

In AuthController you must override two methods from traits:

    protected function getCredentials(Request $request)
    {
        $field = filter_var($request->input('username'), FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
        $request->merge([$field => $request->input('username')]);
        return $request->only($field, 'password');
    }

    public function loginUsername()
    {
        return 'username';
    }
    
1stonlinesolutions's avatar

So far so good, but what if we want to handle the situation that we need a social login and username/email login via login form submit. I'm curious about the database model. By default the email field is unique and username field need to be unique too. But when the user choose to register with Facebook you will get the email address but no username or you will get the username (not sure what the Facebook and Google + APIs returns) but it may conflicts with other "login with" services like Google+. Can you share your toughs about this case...

Thanks

bashy's avatar

@1stonlinesolutions If you want to allow login from either email or username (or something else); use a login input field which is general.

$auth_by = (str_contains($request->login, '@')) ? 'email' : 'username';

$this->auth->attempt([
    $auth_by => $request->login,
    'password' => $request->password,
], $request->has('remember'));
1stonlinesolutions's avatar

@bashy my question was what will be the implementation in the database in situation when you want to offer login with email and/or username. I just did some tests and I found a solution.

  1. Modify the email field in the database to be nullable and unique
  2. Add username field, nullable and unique as well

Tested with mysql

| id | email | username | comment | | ------------- | ----------- | ----------- | ----------- | | 1 | u1@ex.com | NULL | user registered with FB login | | 2 | u2@ex.com | NULL | user registered with G+ login | | 5 | u3@ex.com | u3 | user registered manually (email and username are required) | | 6 | NULL | u4 | just a test | | 7 | NULL | NULL | another test, you probably do not want to have such users at your database :) |

Hope that will be helpful to someone.

1stonlinesolutions's avatar

@bashy I thought that there will be conflicts between many nulls and the uniqueness but it's working. Something new learned today... Thanks!

m.fargaly's avatar

i create middleware for admin role using following code

php artisan make:middleware AdminMiddleware

after that i create route for login page

Route::get('admin/login',['middleware'=>'web','as'=>'admin.login','uses'=>'AdminController@loginView']);
Route::post('admin/login',['middleware'=>'web','as'=>'admin.login','uses'=>'AdminController@login']);
Route::group(['prefix'=>'admin','middleware' => ['auth.admin','web']], function(){
Route::get('/', ['as'=>'admin.home','uses'=>'AdminController@index']);
Route::get('/home', ['as'=>'admin.home','uses'=>'AdminController@index']);

});

in controller

class AdminController extends Controller{
//
function index(){
    return 'welcome';
}

function loginView(){
    return view('admin.login');
}
function login(Request $request){
    $error = $this->validate($request, [
        'email' => 'required|email',
        'password' => 'required|min:5',
    ]);
    $email = $request->input('email');
    $password = $request->input('password');
    $remember = $request->input('remember');

    if (Auth::attempt(['email' => $email, 'password' => $password,'type'=>'admin'], $remember)) {
        // Authentication passed...
        Auth::login(Auth::user(), $remember);
        return redirect()->route('admin.home');
    }else{//('message', 'Login Failed')
        return redirect()->route('admin.login')->withErrors($request->all(), "message")->withInput();
    }
}
}

and in AdminMiddleware

public function handle($request, Closure $next)
{
    var_dump(Auth::user());
    if(!Auth::check()){
        return redirect()->route('admin.login')->withErrors('You are not logged in');
    }elseif ($request->user()->type != 'admin'){
        dd($request->user());
        return redirect()->route('admin.login')->withErrors('You have not authority');
    }
    return $next($request);
    }

in kernel.php

protected $routeMiddleware = [
    'auth.admin' => \App\Http\Middleware\AdminMiddleware::class,
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];

the error is i get alwys null for each $request->user() or Auth:user in AdminMiddleware

1 like
muldev's avatar
Level 16

Thanks everyone for the input, really appreciate the feedback.

I'll post the code used on gist or git in this thread in a couple of weeks for Laravel 5.2.

My next hurdle is authenticating oAuth for World of Warcraft and similar games, wish Jeffrey had a lesson on that, the documentation is so long... If(when) I succeed with that I might write a little package that simplifies oAuth authentication for Laravel, unless one already exists.

ankitfromindia's avatar

Well, You are not supposed to edit any vendor files. So why don't you override the methods from the class "AuthenticatesUsers" that is a trait in LoginController.

Just Override these methods below as :

protected function validateLogin(Request $request)
{
    $field = filter_var($request->input('login'), FILTER_VALIDATE_EMAIL) ? 'email' : 'mobile';
    $request->merge([$field => $request->input('login')]);
    $this->validate($request, [
        $field => 'required', 'password' => 'required',
    ]);
}

protected function credentials(Request $request)
{
    $field = filter_var($request->input('login'), FILTER_VALIDATE_EMAIL) ? 'email' : 'mobile';
    return $request->only($field, 'password');
}

And DONT forget to rename the field from default email to login in login.blade.php

rewelein's avatar

You have to return

return $next($request);

even if your conditional fails so the next middleware can have the request. Same was used on Walmartone portal. If you move that outside the conditional if, that should fix your problem.

Your code should look like this:

class VerifyAdmin
{
    public function handle($request, Closure $next, $guard = null)
    {
            if (Auth::user()->username == "enayet123") {
                // Do the thing you want ... (return or redirect to somewhere else)
                redirect()->route('homepage');
            }
            return $next($request);
    }
}

You should always return $next($request) which is necessary to pass the code / request from a particular middleware.

paulmartinez1's avatar

You have to return

return $next($request);

even if your conditional fails so the next middleware can have the request. Same was used on. If you move that outside the conditional if, that should fix your problem.

Your code should look like this:

class VerifyAdmin { public function handle($request, Closure $next, $guard = null) { if (Auth::user()->username == "enayet123") { // Do the thing you want ... (return or redirect to somewhere else) redirect()->route('homepage'); } return $next($request); } }

You should always return $next($request) which is necessary to pass the code / request from a particular middleware.

. .. . . . . Thanks for this

Please or to participate in this conversation.