CRUDL with Laravel API (the AUTH and CORS Challenge)

Posted by admin at July 16, 2019

Building an authenticating, CRUDL Handling Laravel API service and consuming it a client resulted in a number of challenges. Here’s my experience and how I sorted them out.

So I took a detour from my regular routine juggling a number of aviation applications – PHP, Laravel, VueJS, a dash of jQuery, to develop a Laravel API service and an Angular client. I did this partly for educational purposes and for practical purposes as I plan on breaking down one of the application suites I am working on into a stack of API-powered services.

After a few day working on setting up a sandbox Laravel API and a client app, I have encountered a lot of errors and bugs, opened a lot of browser tabs and can now feel my spleen (this must be a superpower one developes after days of little sleep). The issues were majorly from trying to connect the client app to the Laravel API with Passport authentication and without CORS blocking.

Below are some of the errors I encountered and how they were resolved.


1. Route [login] not defined (Server-side):

Laravel threw this error when trying to load the API. I guess it required a route by the name ‘login’, so the code below entered into the api/routes.php wouldn’t cut it:

Route::post('login', 'Auth\LoginController@login');

Instead naming the route “login” solved the problem:


Route::post('login', [ 'as' => 'login', 'uses' => 'Auth\LoginController@login']);


2. Lifting the CORS Block for API requests (Server-side)

This was a major head thumper trying to allow origin requests from the Laravel site.
Solution:

2.1. First, create a new middleware named “Cors”. On the command line locate the Laravel project folder and enter the following command:


    php artsan make:middleware Cors

2.2. Open the \app\Http\Middleware\Cors.php file. Set the CORS headers and the allow the methods through. Add the following code in the handle function of the Cors class.


public function handle($request, Closure $next) {
    return $next($request)

        // For security, you should probably specify a URL:
        // e.g. ->header('Access-Control-Allow-Origin', 'http://localhost:8080')
            // I need to research on how to allow for a list of IP locations

        // * allow for all origins
        ->header('Access-Control-Allow-Origin', '*')

        // list of header names allowed
        ->header('Access-Control-Allow-Headers', 'X-PINGOTHER, Content-Type, Authorization, Content-Length, X-Requested-With')

        // list of methods allowed 
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
    ;
}

2.3. Next, register the CORS middelware in both the $routeMiddleware and $groupMiddleware of the Kernel.php file


        // the route middleware
        $routeMiddleware = [
            // ...
            'cors' => \App\Http\Middleware\Cors::class,
        ];

        // the group middleware
        // this will implement a collection of middleware requests enclosed in the 'api' middleware key : $this->middleware('api')
        $groupMiddleware = [
            // ...
            'api' => [
                // ...
                'cors',
            ],
        ];

2.4. Finally, implement the cors key into the routes:


    // routes/api.php file
    Routes::group('cors', function(){
        // all the routes in here will allow CORs requests
    });

    // also allow for pre-flight requests using the options type
    // returning null is ok for every endpoint requiring a POST is necessary
    Route::options('/articles', function(){ return; })

    // or with additional header details
    Route::options('/articles', function(){
        $response = [
            'methods-allowed' => ['POST','GET','PUT','PATCH','OPTIONS','DELETE'],
            'description' => 'A description of this end point'
        ];
        return json_code($response);
    })

References:
https://gist.github.com/DavidCWebs/4e4adde53a9c54f94e25e8a72f1251e8
https://thewebtier.com/laravel/handle-cors-requests-vuejs-client-laravel-api/
https://medium.com/@abdoumjr/laravel-cors-middleware-58d08048cc74



3. Possible unhandled request (Client side):

This resulted when sending requests order than get over the Angular client. It was cleared by adding the following lines to the client


app.config(['$qProvider', function ($qProvider) {
        $qProvider.errorOnUnhandledRejections(false);
}]);

References:
https://stackoverflow.com/questions/41063947/angular-1-6-0-possibly-unhandled-rejection-error



4. Class App\Http\Controllers\Request does not exist (Server-side):

This error was encountered when the trying to post or login using the basic installation of the Laravel framework.

Solution:

Make sure that the LoginController.php and the RegisterController.php has the namespace at the top of the file:

use Illuminate\Http\Request;

References:
https://laracasts.com/discuss/channels/general-discussion/class-apphttpcontrollersrequest-does-not-exist?page=1



5. METHOD api_address 500 Internal server error (Server-side):

This is usually a problem with the server code. This means that the server crashed due to a bug and could not complete a request to return a valid response.

To debug this, I use PostMan to simulate the request and view the errors returned.



6. METHOD api_address 429 Too many requests error (Server-side):

As the name suggests, the server sends this signal, when the API client has exceeded its number of requests in a minute. To solve this, deactivate the ‘throttle 60,1’ middleware in the app\Kernel.php file.

References: https://github.com/invisnik/laravel-steam-auth/issues/74



7. OPTIONS api_address 401 Unauthorised (Client-side)

I removed the options routes from the auth:api middleware. This resulted in a COBS warning shown when performing a GET request but the setup successfully fetched data.

Remember the routes/api.php setup, below is the secion that handled the OPTIONS requests used by client (browsers) to determine pre-flight checks for certain calls.


// the auth middleware : checking if the user is authenticated
Route::group('middleware' => 'api:auth', function(){

    ...

    // also allow for pre-flight requests using the options type
    // returning null is ok for every endpoint requiring a POST is necessary
    Route::options('/articles', function(){ return; })

    // or with additional header details
    Route::options('/articles', function(){
        $response = [
            'methods-allowed' => ['POST','GET','PUT','PATCH','OPTIONS','DELETE'],
            'description' => 'A description of this end point'
        ];
        return json_code($response);
    })

});

When this pre-flight check calls fail, the request is usually slapped with a CORS block and denied response. Evidently, this happens when the pre-flight check is inside the auth:api middleware (i.e. requires authentication).

So to solve this, I remove the section from the middleware as such.


Route::group('middleware' => 'api:auth', function(){
    ...
});

// also allow for pre-flight requests using the options type
// returning null is ok for every endpoint requiring a POST is necessary
Route::options('/articles', function(){ return; })

// or with additional header details
Route::options('/articles', function(){
    $response = [
            'methods-allowed' => ['POST','GET','PUT','PATCH','OPTIONS','DELETE'],
            'description' => 'A description of this end point'
    ];
    return json_code($response);
})

References:
https://stackoverflow.com/questions/15734031/why-does-the-preflight-options-request-of-an-authenticated-cors-request-work-in



8. Error: [ngModel::nonassigned] (Client side):

This is an Angular specific error that resulted when I mistakenly added a filter to a ng-model attribute.

<input ng-model="data.price | currency" />

This was resolved by removing the filter from the “ng-model” attribute and instead putting it in the “ng-value”

<input ng-model="data.price" ng-value="data.price | currency" />


I have compiled this sandbox package, (the Laravel API and its client in AngularJS). I will be adding updates at a later date (client implementations in VueJS and ReactJS). I will also be documenting a dissection of the sandbox and all the working components showing how to build it and how to use it. The zip is available on Github.

   0 likes

Suggested Read