Thursday 20 June 2024

Creating a basic authentication API using Laravel 11

I have used this blog post to create the API, but with a couple of tweaks and some additional explanations. So, thanks Hendrik!

Pre-requisites

I'll assume that you have Laravel set up to use laravel commands.

First, create your application

Run:
laravel new sanctum-example

To question 'Would you like to install a starter kit?', select 'No starter kit'.
To question 'Which testing framework do you prefer?',  select 'Pest'.
To question 'Would you like to initialize a Git repository?', select 'yes'.
To question 'Which database will your application use?', select 'MySQL'.
To question 'Would you like to run the default database migrations?', select 'no'.

Run:
cd sanctum-example

Run:
php artisan install:api

At this stage, the API script reports: "INFO  API scaffolding installed. Please add the [Laravel\Sanctum\HasApiTokens] trait to your User model".
So, let's do that first.
Open app/Models/User.php and change the line:
use HasFactory, Notifiable;
to:
use HasApiTokens, HasFactory, Notifiable;
This will also require you to add the namespace:
use Laravel\Sanctum\HasApiTokens;

Now that's done:
Delete the resources directory
Delete the file routes/web.php
Delete the line web: __DIR__.'/../routes/web.php', from bootstrap/app.php

At this stage I like to create the database so that migrations can be added.
Run:
php artisan serve
Now open your favourite MySQL editor and create the database, i.e.
CREATE DATABASE `sanctum-example`;
Now we can close the server using Ctrl-c.
We should now edit our database settings in the .env file to something similar to this:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=sanctum-example
DB_USERNAME=root
DB_PASSWORD=<yourpassword>


We're now in a good place to do our migrations:
php artisan migrate

At this stage Sanctum doesn't have a configuration published. For this we have to run:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

Create an Authentication Controller

Our application is API only, so we don't need to create a separate directory to hold API code. So, run:
php artisan make:controller AuthController
We should now have a file called app/Http/Controllers/AuthController.php
To this controller, let's add a method to handle registration:
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
    'name'      => 'required|string|max:255',
    'email'     => 'required|string|max:255|unique:users',
    'password'  => 'required|string'
]);

if ($validator->fails()) {
    return response()->json($validator->errors());
}

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

$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
    'data'          => $user,
    'access_token'  => $token,
    'token_type'    => 'Bearer'
]);
}
A method to handle login:
public function login(Request $request)
{
        $validator = Validator::make($request->all(), [
            'email'     => 'required|string|max:255',
            'password'  => 'required|string'
        ]);
        if ($validator->fails()) {
            return response()->json($validator->errors());
        }

        $credentials    =   $request->only('email', 'password');

        if (!Auth::attempt($credentials)) {
            return response()->json([
                'message' => 'User not found'
            ], 401);
        }

        $user   = User::where('email', $request->email)->firstOrFail();
        $token  = $user->createToken('auth_token')->plainTextToken;

        return response()->json([
            'message'       => 'Login success',
            'access_token'  => $token,
            'token_type'    => 'Bearer'
        ]);
}
Finally, a method to handle logout:
public function logout()
{
        Auth::user()->tokens()->delete();
        return response()->json([
            'message' => 'Logout successfull'
        ]);
}
In order to support these methods, you need to have the following namespaces at the top of your class:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

Create Routes

Next we need to edit the file  routes/api.php
We'll begin by adding our new namespace to the top of the file:
use App\Http\Controllers\AuthController;

Now we can add the routes to support the work we did in the AuthController.
A route for /user should already exist. Don't remove/override that, we'll need it later. Add:
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);

Route::middleware('auth:sanctum')->group(function () {
    Route::post('/logout', [AuthController::class, 'logout']);
});

Testing our work

Let's start the server again:
php artisan serve
I use Postman for this, but essentially, below are the values you need to perform in each test.

Register

Method: POST
URL: http://127.0.0.1:8000/api/register
Header fields: Accept: application/json
Body fields: name,email,password
Make up your own name, email address and password

Login

Method: POST
URL: http://127.0.0.1:8000/api/login
Headers fields: Accept: application/json
Body fields: email, password

Get User

Method: GET
URL: http://127.0.0.1:8000/api/login
Headers: Accept : application/json
Authorization: <Bearer token>
Make use of the token returned when you used the login or register API.

Logout

Method: POST
URL: http://127.0.0.1:8000/api/logout
Headers: Accept: application/json
Authorization: <Bearer token>
Make use of the token returned when you used the login or register API.

No comments:

Post a Comment