How to protect Ajax server-side processing for jQuery DataTables using API Tokens in Laravel 7
This tutorial assumes you already know how to set up jQuery DataTables for your webpage, and at the least basic Laravel knowledge. The authentication will be token-based, and knowledge of Laravel Passport is not required.
Security concerns: This authentication solution is “easier” to do, compared to authentication using Laravel Passport, but using the latter would be more secure. Laravel’s API Token guard should only be used for trivial authentication, and NOT for authentication involving sensitive user data, etc.
Set up the configuration files
Before we get started, it is important to know that Laravel (or the version of it I’m using, which is Laravel 7.x) automatically handles API authentication through its routes by using the pre-set middleware auth:api
.
To make sure that we use Laravel’s built-in API token authentication driver (or in some cases, people call this the “guard”), we edit config/auth.php
like so:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
Note that for most projects, this is the default setting.
Set up the database
Then, it is crucial to set up the database so it can hold API tokens for authentication. Laravel handles this automatically, and by default looks at the api_token
field in the database. If you use a different field name, then the default guard is not guaranteed to work.
For instance, in my create_users_table.php
migration, I add the field like so:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password'); $table->string('api_token', 80)->unique()
->default(Str::random(80)); $table->rememberToken();
$table->timestamps();
});
}
}
The field is a string, with the name api_token
with length 80 (hence string('api_token', 80)
), is unique (hence unique()
), and lastly, is randomized by default (hence default(Str::random(80)
).
Setting up the routes
First, the page should be in a protected route (like a logged-in user-only route), or else authenticating the API would be pointless in the first place. In this example, my protected page will be ShoppingController@index
.
In your routes/web.php
:
Route::group(['middleware' => ['auth']], function() {
Route::get('/shopping', 'ShoppingController@index')
->name('shopping.index');
});
Second, we make sure that the built-in token guard for Laravel is utilized for the API route. For this example, my API endpoint for my DataTables processing will be ShoppingController@ajax
.
We set the API endpoint in routes/api.php
with the auth:api
middleware like so:
Route::middleware('auth:api')->get('/list, 'ShoppingController@ajax')->name('api.list');
The routes we will be using should now be properly set up.
Passing the API token to the frontend
We will use the default Blade templating engine for the frontend. To get the currently logged-in user’s api_token
, we can call this:
{{ Auth::user()->api_token }}
That being said, we should be able to pass this token to our request with DataTables:
$('#list_table').DataTable({
"processing": true,
"serverSide": true,
"ajax": {
"url": "{{ route('api.list') }}",
"type": "GET",
"data": {"api_token": {{ Auth::user()->api_token }}},
},
"columns": [
... whatever you put here ...
]
});
And that should pass the api_token
parameter in the GET request.
That’s it!
There are many things you could do to improve on approaching this.
- Using Laravel Passport. A little trickier to use, but well worth the security.
- Hashing your API tokens. Storing your tokens in plain text is usually not the way to go.