Home » Laravel » How to Create Login API in Laravel

How to Create Login API in Laravel

Most Laravel projects require authorization. Some pages and API routes must be protected from unauthorized access and be available only for registered users. In the traditional approach, when PHP code renders a whole page, Laravel can use cookies for authorization. In modern SPA sites, Laravel provides only API. But APIs do not support cookies.

In this article, we will explain how to make login API in Laravel and authenticate users in API using access tokens. You can use the laravel/passport package to achieve it, but this package is very heavy. Laravel supports more lightweight technology. It is called Sanctum, and is enabled in the latest versions of Laravel by default.

How to Create Login API in Laravel

Many of the concepts used in this article are described in the article How to How to Create REST API in Laravel, so look it first.

1. Basic Configuration

First of all, let’s look at how to configure Sanctum to work with your User model. Open app/Models/User.php file and make sure that the HasApiTokens trait has been added to the class:

//.... use HasApiTokens; //....

2. Register Request

Your API should have an endpoint that allows registering a new users. First, create data transfer object for registring data in app/DataTransfers/RegisterData.php:

namespace App\DataTransfers; class RegisterData { public string $email; public string $name; public string $password; }

Next, create a RegisterRequest class that will validate registration data. By default, you can specify name, email and password for the user. In this example I will validate this data. If you want, you can add more columns to the users table. Run this command to create request class:

php artisan make:request RegisterRequest

Change authorize() method to allow all connections:

public function authorize(): bool { return true; }

Then, add rules for validation of the data:

public function rules(): array { return [ "email" => [ "required", "email:rfc,dns", "unique:users,email", "max:255", ], "name" => ["required", "string", "max:255"], "password" => ["required", "string", "max:255", "confirmed"], ]; }

The query should contain a valid email. You can validate it by RFC. It ensures that the email has a @ symbol and domain with a dot. For example, support@example.com. In addition, you should also validate the domain. Use a DNS validator for this. Next, ensure that the email is unique, and the string is not longer than 255 symbols. The password field must be confirmed in another field, so you should add a confirmed rule.

Next, add getRegisterData() method, that will return previously created data transfer with the data:

public function getRegisterData(): RegisterData { $validated = $this->validated(); $data = new RegisterData(); $data->email = $validated["email"]; $data->name = $validated["name"]; $data->password = $validated["password"]; return $data; }

Next, create UserService class, that will create a new user using data from RegisterData DTO:

public function registerUser(RegisterData $data) { $user = new User(); $user->email = $data->email; $user->name = $data->name; $user->password = Hash::make($data->password); $user->save(); return $user; }

Then, create RegisterController class in app/Http/Controllers/Api and add __invoke() method with this code:

public function __invoke(RegisterRequest $request, UserService $service) { $service->registerUser($request->getRegisterData()); return response()->noContent(201); }

Finally, register this route in the routes/api.php file:

Route::post( "auth/register", \App\Http\Controllers\Api\Auth\RegisterController::class )->name("auth.register");

And test it in Postman:

If everything is fine, the server will return a 201 status code and a new user will be created.

3. Login Request

User creation is pretty simple, you just add a new record to the database. In the login request, create a new access token that your clients will use for authenticating their requests and return it in the response with information about the authenticated user. You need two methods in the UserService class.

First, add checkUserCredentials() method that will check the existence of the user with the specified email and password:

public function checkUserCredentials(string $email, string $password): bool { $user = User::query() ->where("email", $email) ->first(); return Hash::check($password, $user->password); }

The Hash facade generates a different hash for the same password on each call, so you can’t just get the user from the database by the email and hash. First, the above method finds the user, then validates the password using the Hash facade.

Second, a method with name createToken() will create a new access token:

public function createAccessToken(string $email): NewAccessToken { $user = User::query() ->where("email", $email) ->first(); return $user->createToken("auth_token"); }

Next, create the LoginRequest class:

php artisan make:request LoginRequest

Fix authorize() method the same way as in RegistrationRequest and add these validation rules in rules() method:

public function rules(): array { return [ "email" => ["required", "email:rfc,dns", "max:255"], "password" => ["required", "string", "max:255"], ]; }

There you should validate that email and password present in the request, string length does not exceed 255 characters and ensure that the email is valid. Also, ensure in the Request class that the password is correct for the user with this email. I recommend using withValidator() method, that will be executed after data validation using the rules and allowing to perform some custom validations:

public function withValidator(Validator $validator) { $validator->after(function (Validator $validator) { if ($validator->errors()->count() > 0) { return; } $data = $validator->validated(); $email = $data["email"]; $password = $data["password"]; $userService = $this->container->get(UserService::class); $result = $userService->checkUserCredentials($email, $password); if (false === $result) { $validator ->errors() ->add("password", "Email or Password is incorrect"); return; } }); }

Also, you need a method that allows you to get the validated email. Let’s create it:

public function getEmail(): string { $validated = $this->validated(); return $validated["email"]; }

Next, create a LoginResource class that will display information about the user and created token as plain text:

php artisan make:resource LoginResource

Paste this code to toArray() method:

return [ "email" => $this->resource->accessToken->tokenable->email, "name" => $this->resource->accessToken->tokenable->name, "token" => $this->resource->plainTextToken, ];

After this, you can create LoginController in app/Http/Controllers/Api/Auth with this code:

use App\Http\Controllers\Controller; use App\Http\Requests\LoginRequest; use App\Http\Resources\LoginResource; use App\Services\UserService; class LoginController extends Controller { public function __invoke(LoginRequest $request, UserService $service) { $token = $service->createAccessToken($request->getEmail()); return new LoginResource($token); } }

Next, register this controller in the api/routes.php file:

Route::post( "auth/login", \App\Http\Controllers\Api\Auth\LoginController::class )->name("auth.login");

And finally, you can check the request in Postman. Enter email and the password as a JSON body and you will get info about the user and access token:

If the email and the password are invalid, the server will return an error with 422 status code:

4. Authorized Request

Let’s create a sample request that will need authorization and will return the identifier of the authenticated user. For this example, you can register it directly in routes/api.php. Simply add the auth:sanctum middleware. Laravel will do all the work for you:

Route::get("example", function (Request $request) { return response($request->user()->id); })->middleware("auth:sanctum");

Then, in Postman or another client, set header Authorization with a value Bearer your_access_token. For example:

Without the access token you will get this error and 401 status code.

4. Log Out Request

In the log out request, just delete the current authentication token. There, you don’t need Request or Response classes. Add the deleteToken() method to the UserService:

public function deleteAccessToken(PersonalAccessToken $token) { $token->delete(); }

Then, create a LogOutController class in app/Http/Controllers/Api/Auth with this code:

public function __invoke(Request $request, UserService $userService) { $userService->deleteAccessToken($request->user()->currentAccessToken()); return response()->noContent(); }

After this, register the controller in the routes/api.php file:

Route::delete( "auth/logout", \App\Http\Controllers\Api\Auth\LogOutController::class ) ->middleware("auth:sanctum") ->name("auth.logout");

The code above adds authentication middleware auth:sanctum because the request needs the current user and current token. Now, test the request using Postman, don’t forget to set the Authentication header:

After this you will get access denied exception with 401 status code when trying to authorize using this token.

If you want to finish all user sessions, just delete all access tokens owned by the current user. Add deleteAllTokens() method to the UserService and use it:

public function deleteAllAccessTokens(User $user) { foreach ($user->tokens() as $token) { $token->delete(); } }

Wrapping Up

In this article we have explained how to create a Login API in Laravel using Sanctum. As you can see it is pretty simple. Now, you can issue tokens for your users and their devices and manage them. If you want to restrict access to any route for all, except authenticated users, just add the middleware. Laravel Sanctum does all the work for you. What authentication do you use in your projects? What do you prefer? Sanctum or Passport? Why? Tell us in the comment form below.

Your Reaction

Leave a Comment