Laravel Ajax Register and Login

In this tutorial we gonna show you how to do Ajax register and login with Laravel 5.3 We'll start by creating new Laravel project.

laravel new ajaxlogin

After the project is created, create new database called ajaxlogin and update .ENV file like so

DB_DATABASE=ajaxlogin
DB_USERNAME=root
DB_PASSWORD=

Next run make:auth which will scaffold controllers and views necessery for login and registration.

php artisan make:auth

After this command run

php artisan migrate

Now go to resources/views and copy home.blade.php content and paste it in welcome.blade.php This will result in following code

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Dashboard</div>

                <div class="panel-body">
                    You are logged in!
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Change the Dashboard and 'You are logged in!' to

<div class="panel-heading">FrontPage</div>

<div class="panel-body">
   You are not logged in!
</div>

Now open app.blade.php and copy this part

    <nav class="navbar navbar-default navbar-static-top">
        <div class="container">
            <div class="navbar-header">

                <!-- Collapsed Hamburger -->
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
                    <span class="sr-only">Toggle Navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>

                <!-- Branding Image -->
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
            </div>

            <div class="collapse navbar-collapse" id="app-navbar-collapse">
                <!-- Left Side Of Navbar -->
                <ul class="nav navbar-nav">
                    &nbsp;
                </ul>

                <!-- Right Side Of Navbar -->
                <ul class="nav navbar-nav navbar-right">
                    <!-- Authentication Links -->
                    @if (Auth::guest())
                        <li><a href="{{ url('/login') }}">Login</a></li>
                        <li><a href="{{ url('/register') }}">Register</a></li>
                    @else
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
                                {{ Auth::user()->name }} <span class="caret"></span>
                            </a>

                            <ul class="dropdown-menu" role="menu">
                                <li>
                                    <a href="{{ url('/logout') }}"
                                        onclick="event.preventDefault();
                                                 document.getElementById('logout-form').submit();">
                                        Logout
                                    </a>

                                    <form id="logout-form" action="{{ url('/logout') }}" method="POST" style="display: none;">
                                        {{ csrf_field() }}
                                    </form>
                                </li>
                            </ul>
                        </li>
                    @endif
                </ul>
            </div>
        </div>
    </nav>

and place it inside includes/header.blade.php after you have done that go back to app.blade.php and add following:

<body>
    @include('includes.header')

    @yield('content')

    <!-- Scripts -->
    <script src="/js/app.js"></script>
</body>

Now we are going to create modal window that will popup when user clicks on register or login. We gonna create register modal first. Go to includes/header.blade.php and paste in the following code at the end.

<div class="modal fade" hidden="true" id="registerModal" role="dialog" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="panel panel-filled">
                <div class="panel-body">
                    <form action="{{url('/register')}}" id="registerForm" method="post" name="registerForm">
                        <div class="form-group" id="register-name">
                            <label class="control-label" for="name">Name</label> <input class="form-control" id="name" name="name"
                            placeholder="choose name" required="" title="Please enter you name" type="text"> <span class=
                            "help-block"><strong id="register-errors-name"></strong></span> <span class="help-block small">Your name</span>
                        </div>
                        <div class="form-group" id="register-email">
                            {{ csrf_field() }} <label class="control-label" for="email">Email</label> <input class="form-control" id=
                            "email" name="email" placeholder="example@gmail.com" required="" title="Please enter you email" type="email"
                            value=""> <span class="help-block"><strong id="register-errors-email"></strong></span> <span class=
                            "help-block small">Your email</span>
                        </div>
                        <div class="form-group" id="register-password">
                            <label class="control-label" for="password">Password</label> <input class="form-control" id="password" name=
                            "password" placeholder="******" required="" title="Please enter your password" type="password" value="">
                            <span class="help-block"><strong id="register-errors-password"></strong></span> <span class=
                            "help-block small">Your strong password</span>
                        </div>
                        <div class="form-group">
                            <label class="control-label" for="password-confirm">Confirm Password</label> <input class="form-control" id=
                            "password-confirm" name="password_confirmation" placeholder="******" type="password"> <span class=
                            "help-block"><strong id="form-errors-password-confirm"></strong></span>
                        </div>
                        <div class="form-group" id="login-errors">
                            <span class="help-block"><strong id="form-login-errors"></strong></span>
                        </div>
                        <div>
                            <button class="btn btn-login right">Register</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Now while still inside header.blade.php go to

<li><a href="{{ url('/register') }}">Register</a></li>

and change it to

<li><a href="#" data-toggle="modal" data-target="#registerModal">Register</a></li>

Now we have associated the link with register modal. If you go to ajaxlogin.dev and press register and you should see this

If you try to fill in correct values and submit the form new user will be created and you will be logged in, however if there are any errors they will not be shown and form will close and no user will be registered. We'll fix that next. Open header.blade.php and paste in the following code.

<script>
    $(document).ready(function(){
        var registerForm = $("#registerForm");
        registerForm.submit(function(e){
            e.preventDefault();
            var formData = registerForm.serialize();
            $( '#register-errors-name' ).html( "" );
            $( '#register-errors-email' ).html( "" );
            $( '#register-errors-password' ).html( "" );
            $("#register-name").removeClass("has-error");
            $("#register-email").removeClass("has-error");
            $("#register-password").removeClass("has-error");

            $.ajax({
                url:'/register',
                type:'POST',
                data:formData,
                success:function(data){
                    $('#registerModal').modal( 'hide' );
                    location.reload(true);
                },
                error: function (data) {
                    console.log(data.responseText);
                    var obj = jQuery.parseJSON( data.responseText );
                   if(obj.name){
                        $("#register-name").addClass("has-error");
                        $( '#register-errors-name' ).html( obj.name );
                    }
                    if(obj.email){
                        $("#register-email").addClass("has-error");
                        $( '#register-errors-email' ).html( obj.email );
                    }
                    if(obj.password){
                        $("#register-password").addClass("has-error");
                        $( '#register-errors-password' ).html( obj.password );
                    }
                }
            });
        });
    });
</script>

If you reload the page and open console, you'll see the following error

ReferenceError: $ is not defined

$(document).ready(function()

This is happening because Laravel by default puts app.js which contains Jquery at the end of file. So since we are referencing
$(document).ready(function() which is Jquery function before Jquery has loaded we get an error. The solution is to move

<script src="/js/app.js"></script>

up inside head tag so it loads before we render header.blade.php content which uses Jquery.

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Styles -->
    <link href="/css/app.css" rel="stylesheet">

    <!-- Scripts -->
    <script src="/js/app.js"></script>

    <script>
        window.Laravel = <?php echo json_encode([
            'csrfToken' => csrf_token(),
        ]); ?>
    </script>
</head>

If we now reload the page and enter some wrong values for example email we already used when we registered first time and short password we should get some errors displayed like so

If we now enter unique email and password that is atleast 6 characters, page will be reloaded and we will be logged in as user we just registered. Now that we have implemented register as ajax we will do same for login. So go ahead and logout current user.

Open header.blade.php and add following code for login modal

<div class="modal fade" id="loginModal" tabindex="-1" role="dialog" hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="panel panel-filled">
                <div class="panel-body">
                    <form action="{{url('/login')}}" method="POST" id="loginForm" novalidate>
                        <div class="form-group" id="email-div">
                            {{ csrf_field() }}
                            <label class="control-label" for="email">Email</label>
                            <input id="email" type="email" placeholder="example@gmail.com" title="Please enter you email" required value="" name="email" class="form-control">
                            {{-- <div id="form-errors-email" class="has-error"></div> --}}
                            <span class="help-block">
                                <strong id="form-errors-email"></strong>
                            </span>
                            <span class="help-block small">Your email</span>
                        </div>
                        <div class="form-group" id="password-div">
                            <label class="control-label" for="password">Password</label>
                            <input type="password" title="Please enter your password" placeholder="******" required value="" name="password" id="password" class="form-control">
                            <span class="help-block">
                                <strong id="form-errors-password"></strong>
                            </span>
                            <span class="help-block small">Your strong password</span>
                        </div>
                        <div class="form-group" id="login-errors">
                            <span class="help-block">
                                <strong id="form-login-errors"></strong>
                            </span>
                        </div>
                        <div class="form-group">
                            <div class="checkbox">
                                <label>
                                    <input type="checkbox" name="remember"> Remember Me
                                </label>
                            </div>
                        </div>

                        <div>
                            <button class="btn btn-login right">Login</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

You'll also need to change the login link from

<li><a href="{{ url('/login') }}">Login</a></li>

to

<li><a href="#" data-toggle="modal" data-target="#loginModal">Login</a></li>

so that it will open login modal when user clicks on login link. If you now try to login as a user that you previously registered, you will be logged in as that user. If you however enter wrong login information no errors will be displayed and page will just reload. To display errors we need to add some javascript and override some AuthenticatesUsers.php functions so they return errors if login call was made with ajax. So now go ahead and add this to header.blade.php Put it inside script tags where you already have the code to handle registration.

var loginForm = $("#loginForm");
loginForm.submit(function(e) {
    e.preventDefault();
    var formData = loginForm.serialize();
    $('#form-errors-email').html("");
    $('#form-errors-password').html("");
    $('#form-login-errors').html("");
    $("#email-div").removeClass("has-error");
    $("#password-div").removeClass("has-error");
    $("#login-errors").removeClass("has-error");
    $.ajax({
        url: '/login',
        type: 'POST',
        data: formData,
        success: function(data) {
            $('#loginModal').modal('hide');
            location.reload(true);
        },
        error: function(data) {
            console.log(data.responseText);
            var obj = jQuery.parseJSON(data.responseText);
            if (obj.email) {
                $("#email-div").addClass("has-error");
                $('#form-errors-email').html(obj.email);
            }
            if (obj.password) {
                $("#password-div").addClass("has-error");
                $('#form-errors-password').html(obj.password);
            }
            if (obj.error) {
                $("#login-errors").addClass("has-error");
                $('#form-login-errors').html(obj.error);
            }
        }
    });
});

This code will ajax submit the form and display any errors that Laravel returns. However currently Laravel will not return any errors when you try to login using ajax, so you will need to override 2 functions to get some errors back that you can display. Open LoginController.php and add this function.

/**
 * Get the failed login response instance.
 *
 * @param \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
protected function sendFailedLoginResponse(Request $request)
{
    if ($request->ajax()) {
        return response()->json([
            'error' => Lang::get('auth.failed')
        ], 401);
    }

    return redirect()->back()
        ->withInput($request->only($this->username(), 'remember'))
        ->withErrors([
            $this->username() => Lang::get('auth.failed'),
        ]);
}

What we are doing here is checking if the request was ajax and if it was we reply back with json and 401 code which stands for " The request has not been applied because it lacks valid authentication credentials for the target resource".  We also output the error with Lang::get('auth.failed') If we now enter wrong password when trying to login we should see the following

If we repeat this more then 6 times Laravel will start throttling the login attempts. Here is what happens "By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts." However since we are using ajax to login Laravel will not by default send as back correct data that we can display as error. If you try this now no errors will be displayed. We want to display these errors so for that we need to override another method which is found in ThrottlesLogins.php, this method is sendLockoutResponse. Add it to LoginController.php

protected function sendLockoutResponse(Request $request)
{
    $seconds = $this->limiter()->availableIn(
        $this->throttleKey($request)
    );

    $message = Lang::get('auth.throttle', ['seconds' => $seconds]);

    if ($request->ajax()) {
        return response()->json([
            'error' => $message
        ], 401);
    }

    return redirect()->back()
        ->withInput($request->only($this->username(), 'remember'))
        ->withErrors([$this->username() => $message]);
}

We check if the request is ajax and if it is we send an error back. Let's try it, enter wrong password serveral times. You should see the following, we get an error message saying that we had too many login attempts.

And that's all you have to do to enable ajax registration and login in Laravel 5.3