Important update: At the end of 2020, we switched the API authentication mechanism from Laravel Passport to Laravel Sanctum. So this article is outdated, please read the new instructions: QuickAdminPanel API Builder with Laravel Sanctum
* * * * * * * * * *
In our QuickAdminPanel we had a simple API generator for some time, we even released its open source version as a package. But the most common question from our customers was “how to manage permissions in this API?” “. Today we finally have an answer to that, with a new release.
We thought we should use another Laravel standard for authentication, namely Laravel Passport. So here’s how the new version of the API generator works.
If you prefer video explanations, here is a video version of this article:
First of all, all you need to do, to enable the API for a particular CRUD, is check this box. Generate CRUD API check box in the options. In fact, it’s checked for you by default.
Then you get all the API related files generated:
- routes/api.php – separate line for this CRUD
- app/Http/Controllers/Api/V1/TransactionsApiController.php
- app/Http/Resources/Admin/TransactionResource.php
- app/Providers/RouteServiceProvider.php special method mapApiRoutes();
Simple, huh? But if you try to reach the end point API/v1/[your_crud_name] without any token you get a 500 error:

This error means that Laravel Passport is not installed.
Installing and configuring Laravel Passport
By default, in config/auth.php we generate an API driver like ‘passport’:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
],
Additionally, we have a passport generated in our composer.json deposit:
{
"name": "laravel/laravel",
// ...
"require": {
"php": "^7.1.3",
// ...
"laravel/passport": "^7.3"
},
Now we need to run this command to generate the Passport related database tables and entries:
php artisan passport:install
The result is as follows:
Encryption keys generated successfully. Personal access client created successfully. Client ID: 1 Client secret: KAY4k312yebafievTzWNQWuY4nUEDfvAqFRSQo0b Password grant client created successfully. Client ID: 2 Client secret: 1OSbOGyJsXgA1QlbCzL3cPv7LhJAUZuTcIRSbKmm
What does this actually mean? This, in the database:

Copy it Customer ID And Customer confidentiality somewhere for the password granting client. You’ll need this to retrieve the access token that allows your user to access an API (more on that later).
Now Passport is installed and if we run our API endpoint call we should get another error: 401 Unauthenticated.

Next step – to authenticate some users with Laravel Passport.
Authenticate a user with Laravel Passport
Laravel Passport uses the OAuth2 concept, which may seem complicated, but in our version it’s actually quite simple: we’ll use what are called password grant tokens.
We need to make an API request in two steps:
- Step 1: API request to authenticate the user and obtain an access token with Passport credentials;
- Step 2: Actual API data request, passing this token from step 1.
To recover your access token you must do JOB ask /oauth/token URL, with these body parameters:
'grant_type' => 'password', 'client_id' => 'client-id', // Client ID from passport:install above 'client_secret' => 'client-secret', // Client Secret from passport:install above 'username' => 'admin@admin.com', // username and password are email and password of actual DB user 'password' => 'somepassword', 'scope' => '', // empty in our case
Example taken from the official documentation, with the Guzzle client:
$http = new GuzzleHttp\Client;
$response = $http->post(' [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);
Example with Postman, with exactly the credentials above, and we try to authenticate by default admin@admin.com user:

You can see a successful response here, and the most useful part for us is access_token returned value. We need to copy and paste it to make the actual API endpoint request. Like that:

Of course, Postman allows you to simply paste the token into one of the authentication sections. But if you query without Postman, it’s just a Bearer token, see the CURL example:
curl -H 'Accept: application/json' \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImY3MDQxZjQ1OWQ4NGY3OTBjZGU1MzhlM2RkNDIyY2YxOGFmMDY3NGM0YTA0ZGNiNjI2ZTgxZGEzNTM0YTk4M2VkYjdhZGExOTliY2RmNzkwIn0.eyJhdWQiOiIyIiwianRpIjoiZjcwNDFmNDU5ZDg0Zjc5MGNkZTUzOGUzZGQ0MjJjZjE4YWYwNjc0YzRhMDRkY2I2MjZlODFkYTM1MzRhOTgzZWRiN2FkYTE5OWJjZGY3OTAiLCJpYXQiOjE1NjQ3NDI1OTgsIm5iZiI6MTU2NDc0MjU5OCwiZXhwIjoxNTk2MzY0OTk4LCJzdWIiOiIxIiwic2NvcGVzIjpbIioiXX0.vhjCK7dcRExXuawQnxd7mw3Cf6dD0qY6s61u86OwAuLd86UAmDgFWlj-PehfE5F6wcScKov-oDCOXL3jYba-gvTIMRSuDRkGmpNYTDZCzf72a595RJ7VLibCVfyxrN_kW9_nZBmHHX_DDgYX5wtu9QCAtD_bQS61RAC0h5tw-WkHv2A1XTxFqEefTQkkD1JsQjo2v1iL11O4Uj6zHsZ_SnTLtLWGVvhhenYFq4iElFnD7xevzlxdomFG6AEkXkV6wjM2JUyYYdpSB7gYD5xso4Kvzk3Sa0U1n3GZ-hGCLaJnMwBvaBY3nfFTB7_ppMHgG81eNVCX00jjmnEKRM-V6ZUKAVCcoUXnnAHm4CYz2bHhxxzYpkMc8SybS2Z1jP2OODxFIWqZkEQHX92auUUIq6EMuKTgKCJ6WBWdhBs5RA5rFkGitoy_RrRQjJNcgEQLauIqKzKHIPtx-AsIc5tPK-JmoEuMAoAp8Bt2jtXbe6Lq8IJULt0rS_isqzF_1cG8AQCpvAxkmF-PmTeX8FkQ5P1f7XHu3OM0Sn-8EODxilxB3hvEB3JXAX6UfLyG9tcVUnij34MvkfRmJ9YLmSB-tYlb5nMg56_eY9AnBs8EniblK029TZVzkkqKCA5hIYEmdFzyl137MJeJmDFYjmntALzhbQYoouTcKrhGXqBjo4I' \
That’s it, we authenticated our admin in a single request, then made a specific data request with this token.
But wait, there’s more: you can use our Gate system to get specific permissions. per user.
Endpoint Protection with Authentication Middleware
There are actually two authorization levels for each API request:
- Authentication with user/password – if failed, you get 401 Unauthenticated;
- After that, permission for specific functionality – if failed, you get 403 Not allowed.
So far we’ve covered the first part: authenticating our user. But in our QuickAdminPanel, we have a pretty powerful roles/permissions system, and it’s reused for web and API authentication.
Here’s how TransactionsApiController.php it looks like:
class TransactionsApiController extends Controller
{
public function index()
{
abort_if(Gate::denies('transaction_access'),
Response::HTTP_FORBIDDEN,
'403 Forbidden');
return new TransactionResource(Transaction::all());
}
// ...
Look at this abortive()and verification of the portal by transaction_access name? This comes from the database:


So if you authenticate to a non-admin user who doesn’t have it transaction_access permission, you will get 403 Not allowed error.
How does it all happen under the hood?
First of all, here’s how routes/api.php look at :
Route::group([
'prefix' => 'v1',
'as' => 'api.',
'namespace' => 'Api\V1\Admin',
'middleware' => ['auth:api']
], function () {
Route::apiResource('transactions', 'TransactionsApiController');
});
Next we have a middleware called Authentication gatesto generate all doors for a particular query:
class AuthGates
{
public function handle($request, Closure $next)
{
$user = \Auth::user();
if (!app()->runningInConsole() && $user) {
$roles = Role::with('permissions')->get();
$permissionsArray = [];
foreach ($roles as $role) {
foreach ($role->permissions as $permissions) {
$permissionsArray[$permissions->title][] = $role->id;
}
}
foreach ($permissionsArray as $title => $roles) {
Gate::define($title, function (\App\User $user) use ($roles) {
return count(array_intersect($user->roles->pluck('id')->toArray(), $roles)) > 0;
});
}
}
return $next($request);
}
}
It takes all permissions from the database permission_role DB table and defines them as gates.
This middleware class is added for all requests, both “api” and “web”, in app/Http/Kernel.php:
protected $middlewareGroups = [
'api' => [
'throttle:60,1',
'bindings',
\App\Http\Middleware\AuthGates::class,
],
'web' => [
\App\Http\Middleware\EncryptCookies::class,
// ...
\App\Http\Middleware\AuthGates::class,
],
];
And that’s it. I know this might sound complicated, but it’s probably the most standard and secure way these days to authenticate API requests.
Last thing: if you want withdraw this feature of Laravel Passport, we have a separate article: How to remove Laravel Passport and make the API public