As a part of our generated code, we also generate API Controllers and Routes. By popular demand, in late 2020, we switched their protection from Laravel Passport to more popular Laravel Sanctum. This article will show how to use Sanctum-protected API from your front-end.

First, we generate routes in routes/api.php file, like this:

Route::group(['middleware' => ['auth:sanctum']], function () {
    Route::apiResource('projects', 'ProjectsApiController');
    Route::apiResource('teams', 'TeamApiController');

    // ... Other routes
});

So any endpoint of those Controllers should be used only with Sanctum authentication.

According to official Sanctum documentation, there are three different ways to do that:

  • with API Tokens;
  • in the SPA project;
  • from a mobile application.

In this article, I will assume that you will use API Token Authentication.

So, the process is this:

  • User registers in your system via registration API endpoint;
  • User logs into your system via login API endpoint and gets the generated token back;
  • Then, with every request, user sends that token as a Bearer token.

As a part of QuickAdminPanel, we don’t generate login/register controllers, but it’s easy to implement them.

Step 1. Add HasApiTokens to User Model

Inside of app/Models/User.php, you need to add these bolded fragments:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

Step 2. Registration endpoint

In routes/api.php we add this public API endpoint:

Route::post('register', 'Api\\AuthController@register');

Then, we generate a Controller:

php artisan make:controller Api/AuthController

And then, inside of that Controller, here’s a possible implementation of the registration. You may modify what data you return, instead of a full User object.

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    public function register(Request $request)
    {
        $request->validate([
            'name' => 'required',
            'email' => 'required|email',
            'password' => 'required|min:6'
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password),
        ]);

        $user->roles()->attach(2); // Simple user role

        return response()->json($user);
    }
}

And now we can call that endpoint, as POST http://project.test/api/register

If you launch this in Postman with some invalid data, it will return 422 code with validation errors:

But if all goes well, you will get a User object back:

Notice: please don’t forget to add the Accept: application/json header.

Step 3. Login endpoint

So, we have a user, now they can log in with their credentials, and get the token for all the other upcoming requests.

In routes/api.php, we add this:

Route::post('login', 'Api\\AuthController@login');

And then we create this login() method in the same AuthController:

public function login(Request $request)
{
    $request->validate([
        'email' => 'email|required',
        'password' => 'required'
    ]);

    $credentials = request(['email', 'password']);
    if (!auth()->attempt($credentials)) {
        return response()->json([
            'message' => 'The given data was invalid.',
            'errors' => [
                'password' => [
                    'Invalid credentials'
                ],
            ]
        ], 422);
    }

    $user = User::where('email', $request->email)->first();
    $authToken = $user->createToken('auth-token')->plainTextToken;

    return response()->json([
        'access_token' => $authToken,
    ]);
}

If the email/password is incorrect, we return a validation error with 422 code, but feel free to modify it to 500 code or whatever you want.

But if Auth attempt is successful, we create a new user token (powered by Sanctum) and return it.

Step 4. Getting Data with Token and Middleware

Now, we can use that token when doing any other API requests, and we need to pass it as a Bearer token.

First, I remind that we add a middleware auth:sanctum to any API calls you want to protect.

Route::group(['middleware' => ['auth:sanctum']], function () {
    Route::apiResource('projects', 'ProjectsApiController');
    Route::apiResource('teams', 'TeamApiController');

    // ... Other routes
});

So, if we try to load GET /api/v1/projects without any token, we get “Unauthenticated” error with 401 code:

But if we pass the Bearer token, the one that we got after login…

Ta-daa! We have the data, which is protected by auth:sanctum middleware.