Laravel API Series: Laravel Sanctum Setup, Sign Up, Login, and Logout
Tags: laravel, webdev, beginners, tutorial
Published: 2022-05-15 00:26:40

## What you'll learn
In this part of the series, you'll learn the following:
- What Sanctum is 
- How to install and use Laravel Sanctum
- Implement the Sign-Up function
- Implement the Login function
- Implement the Logout function
- Restructure the routes to protected and public

## Laravel Sanctum setup
Laravel Sanctum, also commonly known as Sanctum is a lightweight authentication system used to authenticate token-based APIs and SPAs (ReactJs, VueJs, etc). In this section, I will show you how to authenticate users with Sanctum.

### Install Sanctum
   
Due to Laravel's aim to provide a great developer experience, the Laravel project you generated in the first part of the series includes Sanctum, and you can confirm that by going to `composer.json` file, and it should be inside the `require` array like so:

![sanctum installation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmek3vrmsfti6ywv1l71.png)
The green box is the `require` array. If you can't find Sanctum inside the array in your `composer.json` file, run the following command to install it:
```sh
composer require laravel/sanctum
```
The above command will install Sanctum inside your app, and you can confirm by checking the `composer.json` file again.

### Create personal access tokens migration
After confirming Sanctum's installation, the next thing is to create a personal access tokens table in the database, you do that by publishing Sanctum's configurations and migrations file by running the following in your command line:
```sh
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
```
The above command will create a `create_personal_access_tokens_table.php` in your `/database/migrations` folder and a `sanctum.php` file inside the `/config` folder, once you have verified the creation of those two files, the next thing to do is to migrate the new migration file, and you do that with the following command:
```sh
php artisan migrate 
```
The above command will add a new `personal_access_tokens` table to your database, check your database manager to verify:

![personal_access_tokens table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7i5d4io9u1xkij2i1258.png)

Next, go to the `app/Http/Kernel.php` file and replace the `api` array inside the `middlewareGroups` array with the following code:
```php
'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
```
The above code is the middleware that will be used to authenticate our API. 

Next, I'll show you can how to protect routes in Laravel. 

## Protecting Routes
To protect your routes, you need to group the protected routes with a middleware function like so:
```php
// posts routes
Route::group(['middleware' => ['auth:sanctum']], function () {
// protected routes go here
});
```
the above code uses the static `group` function on the `Route` Facade and adds the middleware array that utilizes the `'auth:sanctum'` middleware to protect the routes that you define inside the function. To show you how this works, I'll add all the post routes inside the function like so:
```php
// posts routes
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::resource('posts', PostController::class);
Route::get('/posts/search/{title}', [PostController::class, 'search']);
Route::get('/post/author/{id}', [PostController::class, 'get_author']);
});
```
Now try to get all posts by making a GET request to `localhost:8000/api/posts` and you should get the following result:

![unauthenticated](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnnpb4baih317a9bvoto.png)

The green box is the result you would get from the request, and it reads `"message": "Unauthenticated."`, and that's it! the route has been protected successfully, Now you need to define the steps the user has to take to get authenticated. Next, we will define the `signup` function.

> Note: The above is just an example, I'm going to restructure all the routes later.

Next, I'll show you how to set up a controller for the functions related to authentication.

## AuthController
You learned in the second part of the series that controllers are used to organizing functions in your application, So you'll need to create a controller that will contain all the functions related to authentication.

First, create a controller with artisan, name it AuthController like so:
```sh
php artisan make:controller AuthController
```
>Note: You should not add the `--resource` flag, as we won't be using the `CRUD` functionality here.

That should create a controller file that contains the following code:
```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class AuthController extends Controller
{
    //
}
```
Next, add the dependencies required which in this case will be:
```php
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;
```
Add the code above under the `namespace App\Http\Controllers;` line.
- The `User` is the user model and migration that was created when you generated your Laravel application.
- The `Request` is the object that contains any data the user sends to the server.
- The `Response` is the object that contains any data the server sends back to the user.
- The `Hash` contains `bcrypt` function that will be used to hash the passwords.

Next, I'll show you how to create the Sign-Up function

## Sign Up
For users to be able to sign in, you need to create the function. So create a public `sign_up` function like so:
```php
    public function sign_up(Request $request){

    }
```
Next, validate the data coming through the request object like so:
```php
        $data = $request->validate([
            'name' => 'required|string',
            'email' => 'required|string|unique:users,email',
            'password' => 'required|string|confirmed'
        ]);
```
The above code validates the data using the `validate` function.
- The name is a required string.
- The email is a required string and has to be a unique value inside the column in the users table.
- The password is a required string and needs to be confirmed, so the user needs to input it a second time.

Next, create user using the static `create` function on the `User` model like so: 
```php
        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password'])
        ]);
```
The above code uses the `create` function with an array of the previous `data` variable to create a user.

> Note: The `password` in the above array is wrapped in `bcrypt` function, so the password will be hashed before saving the user to the database.

Next, generate an authentication token using the `createToken` function on the `$user` like so:
```php
$token = $user->createToken('apiToken')->plainTextToken;
```
The above code will create a token that will be sent along with every request to a protected route.

Next, create the response that will be sent back once the user has been created successfully:
```php
        $res = [
            'user' => $user,
            'token' => $token
        ];
        return response($res, 201);
```
The above code created a variable named `$res` which is an array that contains the created `user` and the generated `token`, and returns it using the `response` function along with the status code `201` which means that a resource was created, which is the user and the token. Now the `sign_up` function should look like so:
```php
    public function sign_up(Request $request){
        $data = $request->validate([
            'name' => 'required|string',
            'email' => 'required|string|unique:users,email',
            'password' => 'required|string|confirmed'
        ]);

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

        $token = $user->createToken('apiToken')->plainTextToken;

        $res = [
            'user' => $user,
            'token' => $token
        ];
        return response($res, 201);
    }
```
Next, create a signup route for the above function like so:
```php
Route::post('/signup', [AuthController::class, 'sign_up']);
```
> Note: This route will be public.

You can now create a user by sending the required data to the `/signup` route, like so:
![user created successfully](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8d4tmf875tswer31b0m2.png)
- The purple box is the type of request you'll send for this route, which is a `POST` request.
- The yellow box is the URL of the route `localhost:8000/api/signup`.
- The red box is the data I sent to the server in `form-data` format.
- The green box is the result you'll get after sending the request successfully - this will be the user that was created and the generated token.

Next, add the generated token as the `bearer token` and send a GET request to the protected routes you defined earlier:
![authenticate request successful](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqt2ntvngy099bkg9dyu.png)
- The purple box is the type of request you'll send for this route, which is a `GET` request.
- The yellow box is the URL of the route `localhost:8000/api/posts`.
- The orange box is the type of token I sent to the server which is the `bearer token`.
- The blue box is the token I sent to the server which is the token that was generated when I signed up(this is why you get logged in automatically once you sign up on any application).
- The green box is the result you'll get after sending the request successfully - this will be the posts in the database which was unavailable earlier because I was not authenticated.

Next, I'll show you how to create the Sign-In function.

## Sign In
You need to create a login function so users can log in. To do so, create a `login` function like so:
```php
    public function login(Request $request)
    {

    }
```
Next, validate the request data like so:
```php
        $data = $request->validate([
            'email' => 'required|string',
            'password' => 'required|string'
        ]);
```
- The email is a required string.
- The password is a required string.

Next, check if the user is registered like so:
```php
        $user = User::where('email', $data['email'])->first();

if (!$user || !Hash::check($data['password'], $user->password)) {
            return response([
                'msg' => 'incorrect username or password'
            ], 401);
        }
```
The above code does the following:
- Define a `$user` variable that contains the user with the given email.
- Check if the `$user` is registered and return `'msg' => 'incorrect username or password'` with a `401` status code if it isn't.

>Note: 401 status code means the user is unauthorized.

Next, generate a token if the email passes the above check, like so:
```php
$token = $user->createToken('apiToken')->plainTextToken;
```
The above code generates a token that will be used to log in.

Next, create the response that will be sent back to the user like so:
```php
        $res = [
            'user' => $user,
            'token' => $token
        ];

        return response($res, 201);
```
The above code created a variable named `$res` which is an array that contains the created `user` and the generated `token`, and returns it using the `response` function along with the status code `201` which means that a resource was created, in this case the token. Now the `login` function should look like so:
```php
    public function login(Request $request)
    {
        $data = $request->validate([
            'email' => 'required|string',
            'password' => 'required|string'
        ]);

        $user = User::where('email', $data['email'])->first();

if (!$user || !Hash::check($data['password'], $user->password)) {
            return response([
                'msg' => 'incorrect username or password'
            ], 401);
        }

        $token = $user->createToken('apiToken')->plainTextToken;

        $res = [
            'user' => $user,
            'token' => $token
        ];

        return response($res, 201);
    }
```
Next, create a login route for the above function like so:
```php
Route::post('/login', [AuthController::class, 'login']);
```
> Note: This route will be public.

You can now log in by sending the email and password of a registered user to the `/login` route, like so:
![login successful](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55og5z2trlhmgcelc2n9.png)
- The purple box is the type of request you'll send for this route, which is a `POST` request.
- The yellow box is the URL of the route: `localhost:8000/api/login`.
- The red box is the data I sent to the server in `form-data` format.
- The green box is the result you'll get after sending the request successfully - this will be the logged-in user and the generated token. Next, add the generated token as the bearer token, and viola! you are now authenticated and can visit protected routes.

Next, I'll show you how to create the Logout function

## Logout
The login function is the simplest of all the `AuthController` functions in our case.

First, create a public `logout` function like so:
```php
    public function logout(Request $request)
    {

    }
```
Next, you need to delete the user's valid token, and you do that like so:
```php
        auth()->user()->tokens()->delete();
        return [
            'message' => 'user logged out'
        ];
```
The above function deletes the token for a logged-in user, which means the bearer token will no longer work and the user will be unauthenticated, and returns `'message' => 'user logged out'`.

Now, create a route for the logout function like so:
```php
   Route::post('/logout', [AuthController::class, 'logout']);
```
![logged out successfully](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9p2xe8ezt9kxjjim1qvf.png)
- The purple box is the type of request you'll send for this route, which is a `POST` request.
- The yellow box is the URL of the route `localhost:8000/api/logout`.
- The orange box is the type of token I sent to the server which is the `bearer token`.
- The blue box is the token I sent to the server which is the token that was generated when I logged in.
- The green box is the result you'll get after sending the request successfully - this will be the message that was returned from the `logout` function.

## Restructuring the routes
After the Signup, Login, and Logout functions have been implemented successfully, the next thing is to separate the protected routes and public routes. 

In this case it will be required that you remove the `resource` method that you used to group the `CRUD` routes earlier because the `create`, `update` and `delete` routes will now be protected because an unauthenticated user should not be able to create,  update or delete posts.

So the `api.php` file will look like so now:
```php
// signup and login
Route::post('/signup', [AuthController::class, 'sign_up']);
Route::post('/login', [AuthController::class, 'login']);

// public post routes
Route::get('/posts/search/{title}', [PostController::class, 'search']);
Route::get('/post/author/{id}', [PostController::class, 'get_author']);

// public author routes
Route::get('/authors/search/{name}', [AuthorController::class, 'search']);
Route::get('/author/posts/{id}', [AuthorController::class, 'get_posts']);

// private posts and authors routes
Route::group(['middleware' => ['auth:sanctum']], function () {
// private post routes
Route::post('/posts', [PostController::class, 'store']);
Route::put('/posts/{id}', [PostController::class, 'update']);
Route::delete('/posts/{id}', [PostController::class, 'destroy']);
// private author routes
Route::post('/authors', [AuthorController::class, 'store']);
Route::put('/authors/{id}', [AuthorController::class, 'update']);
Route::delete('/authors/{id}', [AuthorController::class, 'destroy']); 
// logout 
Route::post('/logout', [AuthController::class, 'logout']);
});

```
And that's it!, you have successfully implemented the authentication part of the API. In the next part, I will show you how to test APIs in Laravel.

Please use the comment section for suggestions and feedback, I would really appreciate that. I hope you enjoyed the article!

All the code for this series can be found [here](https://github.com/multi-Adams/laravel-api-series)