Part 1 - API Authentication using Sanctum
Introduction of the Project
This is the first part of our series where we will be building a notes app in Laravel Framework. We will be making the app in Laravel and ReactJS. I will try to follow the best practices and we will have discussions over its architecture along the way.
Creating a Laravel Project
Let's create a new project
composer create-project laravel/laravel notes
Now cd into your 'notes' directory and run the project.
cd notes
php artisan serve
I have got Laravel 10.2.6.
Authentication using Sanctum
We are using Laravel Sanctum for API authentication. You can also use a Laravel passport but I have never used Sanctum before so prefer using it. The good thing is that Sanctum comes pre-installed with Laravel. So let's begin with implementing a register, login, and logout mechanism.
We will start by creating a controller
php artisan make:controller AuthenticationController
It will create AuthenticationController.php
in app\Http\Controllers
. Let's create the functions.
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Exception;
class AuthenticationController extends Controller {
public function register (RegisterUserRequest $request): JsonResponse {
throw new Exception ("Not implemented", 501);
}
public function login (LoginUserRequest $request): JsonResponse {
throw new Exception ("Not implemented", 501);
}
public function logout (Request $request): JsonResponse {
throw new Exception ("Not implemented", 501);
}
}
Oh, we are submitting a form here for register and login requests so we need to validate requests before anything else. Let's create the requests.
php artisan make:request RegisterUserRequest
php artisan make:request LoginUserRequest
Now we will import these at the top of the AuthenticationController.php
use App\Http\Requests\LoginUserRequest;
use App\Http\Requests\RegisterUserRequest;
// remaining code...
Now it is time to implement the user registration. Edit the App\Http\Requests\RegisterUserRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterUserRequest extends FormRequest {
public function authroize (): bool {
// everyone is able to register right?
return true;
}
public function rules (): array {
return [
"name" => "required | string | max:50 | min:3",
"email" => "required | email | max:50 | min:5",
"password" => "required | max:50 | min: 8"
];
}
}
Now we can implement the register
function in AuthenticationController.php
// ... imports
use App\Models\User;
use App\Http\Requests\RegisterUserRequest;
public function register (RegisterUserRequest $request): JsonReponse {
$user = User::create ($request->all());
$token = $user->createToken('token');
return response()->json([
'user' => $user,
'token' => $token->plainTextToken
]);
}
Now we need to add the route only and then we are ready to register users. Open routes/api.php
and add a route for register
.
use App\Http\Controllers\AuthenticationController;
// Auth Routes
Route::post('register', [AuthenticationController::class, 'register']);
Setting up the database
The above flow is complete but we cannot really create any user yet because we have not configured the database. Since this is a tutorial, let's use sqlite. Open your .env file and change the database configuration to
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
Now let's migrate the database. It will ask you to confirm creating a new database so do allow it by answering yes
.
php artisan migrate
Now run the server if not already running using php artisan serve
and make a request to localhost:8000/api/register
And it works! But you know, I am not comfortable with the code. We decided that we would follow best practices. Let's implement login
and logout
methods and then discuss this further.
Implementing Login
Let's implement login
method. We know that Auth Facade in Laravel provides attempt
method that takes the credentials and returns true
if the credentials are matched. So let's use it for login.
First, edit the LoginUserRequest.php
as follows
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LoginUserRequest extends FormRequest
{
public function authorize(): bool {
// anyone can login, right?
return true;
}
public function rules(): array {
return [
"email" => "required | email | max:50 | min:5",
"password" => "required | max:50 | min: 8"
];
}
}
Now let's edit the login
method in AuthenticationController
use App\Http\Requests\LoginUserRequest;
public function login (LoginUserRequest $request): JsonResponse {
if (Auth::attempt ($request->all())) {
$user = User::where('email', $request->email)->first();
$token = $user->createToken('token');
return response()->json([
'user' => $user,
'token' => $token->plainTextToken
]);
} else {
return response()->json([
'message' => 'Invalid credentials'
], 401);
}
}
Now let's add a route for login
in routes/api.php
after register
Route::post('login', [AuthenticationController::class, 'login');
Run the server if not already running, and send a post request to login
Yeah! The code is working as expected. Only the logout
method and then we are done. So let's implement logout
and complete the authentication stuff.
Implementing Logout
Let's implement logout
method in AuthenticationController
public function logout (Request $request): JsonResponse {
$request->user()->currentAccessToken()->delete();
return response()->json([]);
}
Define the route in routes/api.php
. But this time we need an authenticated user, since only those who have logged in can logout, right? So, let's make a middleware group in the file to include routes that require authentication first.
Route::middleware('auth:sanctum')->group(function () {
Route::post('logout', [AuthenticationController::class, 'logout']);
});
Now you can log out.
So that's it. Our authentication module is ready. Let's dive deeper and discuss what's wrong with the code in the next part. Stay tuned till then.