Laravel passport authentication provides the authentication token to authorize the HTTP requests. It is most important to check the authorized requests in any API. Laravel passport is one of the secure API authentication packages. It uses Client Id and Client Secret to generate the token. Then using the token, you can validate the authorized requests. I already posted tutorials on the Laravel Passport Authentication package. Here, I will be creating the RESTful APIs for the Todo App using Laravel 8 Passport. In addition, I will show you the route login not defined exception handling. The Laravel 8 Todo API will contain the basic API for creating and managing todo.
Prerequisites
So, I am assuming you are aware on how to create a Laravel 8 Application. Also, you have the basic idea of HTTP requests. Here, I will create a new project in Laravel 8. So, I am sure that you have the below configuration for the system.
- PHP >= 7.3
- MySQL (version > 5)
- Apache/Nginx Server
- VS Code Editor
- Composer
- Postman
For the API testing, I will be using Postman. You can use other tools also like Insomnia as per your ease.
Create Project For Laravel 8 Passport Auth
Before creating the new project, let me discuss the project that we will create here. Here, in the Todo app, mainly, I will work on two modules.
- User and
- Task
In the User module, I will implement the following functionalities-
- Register
- Login
- User Detail
- Logout
For the Todo module, we will manage the following functionalities-
- Create Todo
- Todos Listing
- Show Todo
- Update and
- Delete Todo
For creating this project, I will be using composer. You can use Laravel installer too.
composer create-project --prefer-dist laravel/laravel todo-api
The above command will create the project and will start installing the laravel 8 inside the folder.
After creating the project, we will require to install the passport package.
How to Implement Invisible reCAPTCHA in Laravel 8
Install Passport Auth Package in Laravel 8
Open the terminal, navigate to the project directory. Then hit the below command-
composer require laravel/passport
The above command will install the passport package in the project. It will take a couple of minutes to install this auth package.
In the next step, we will create and configure the database for this project.
Create and Configure Database
I am creating a new database in MySQL. For creating a database, just enter the below command in MySQL command prompt.
create database laravel_todo;
After creating the database, let’s connect it with our application.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_todo
DB_USERNAME=root
DB_PASSWORD=root
Here, you set your database credentials. So, the database is configured correctly. Now, we will move to the next step.
Create Authentication in Laravel 8 Using Laravel Breeze
Create Model and Migrations
We have the default Model and migration for the Users. So, here the todo application will require one more Model and migration for the Todos.
php artisan make:model Todo -m
After creating the above model and migration, let’s put some fields in both migrations. Firstly, start with user migration file.
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('first_name');
$table->string('last_name');
$table->string('full_name');
$table->string('email')->unique();
$table->string('phone');
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
Now, similarly, put the fields in todos migration file.
public function up()
{
Schema::create('todos', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->boolean('completed')->default(false);
$table->boolean('active')->default(true);
$table->timestamps();
});
}
User Authentication in Laravel 8 Using UI Auth Package
Mass Assignment For User Model
For this Laravel 8 passport auth todo app, we created the models and migration. In the User.php (model), we will have to use a namespace called HasApiTokens. This namespace will be called through the passport package.
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'first_name',
'last_name',
'full_name',
'email',
'phone',
'password'
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* Has Many Relation - Eloquent Relation for Todos
*
* @return void
*/
public function todos() {
return $this->hasMany(Todo::class);
}
}
So, I have added the mass assignment fields in the fillable array. Lastly, I have defined the Eloquent Relation (hasMany) with the Todos table.
Mass Assignment For Todo Model
Here, a user can have multiple todos. Similarly, the inverse of hasMany() relation will need to define inside the Todo model. So, let’s do that.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Todo extends Model
{
use HasFactory;
protected $fillable = [
'title',
'description',
'user_id',
'completed',
'active'
];
/**
* Belongs To Relation - Eloquent Relation For Todo user
*
* @return void
*/
public function user() {
return $this->belongsTo(User::class);
}
}
In the above snippet of Todo.php (model), I have set the mass assignment in the fillable data. Then, I created the function for defining the relation for the inverse of hasMany() from the User.php. Here, belongsTo() relation is inverse of hasMany(). You can go through the official document of Laravel.
Remove Uploaded Image in Laravel 8 From Public Folder
Migrate Tables For Laravel 8 Passport Auth Todo
For migrating the tables, we will be using php artisan command. Here, the below command will migrate all the tables.
php artisan migrate
After the successful migration you will see some extra tables are added also apart from users and todos table.
These tables are added by the Laravel passport auth package. Here, these tables will help to generate access token using the client id and client secret.
In the next step, we will have to set auth guard for the HTTP requests.
How to Implement CKEditor 5 in Laravel 8 From Scratch
Add Auth Guard in Auth Service Provider
However, for authenticating the API requests, we will have to set a guard. This guard will filter the API requests using passport authentication. But, before setting the auth guard, you will have to set the auth service provider. You will find the AuthServiceProvider.php file inside the app/Providers in the project directory.
Firstly, import the below namespace at the top.
use Laravel\Passport\Passport;
Then in the boot() method, simply add the below line. It determins the routes will be authenticated through the Laravel passport auth system.
Passport::routes();
So, overall the code will look like this.
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
// passport routes
Passport::routes();
}
}
Now, we will set the auth guards.
Add Auth Guards For Routes
You can navigate to the config/auth.php file and in the Authentication guards section, replace the API driver with passport as showing below.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
],
That’s it for the authentication guards. In the next step, for generating the access token.
How to Upload Image in CKEditor 5 Using Laravel 8
Install Passport Auth Key
Here, we will install the passport auth key for the OAuth tables. It will install an auth key for validating the users. Laravel passport provides the OAuthClients and OAuthPersonalAccessClient for the encryption key. This encryption key will take the responsibility to validate the API request of the authenticated user.
php artisan passport:install
The above command will create two clients with the ID and the Secret.
That’s it for the passport key configuration. Now, we can proceed with the functional part for creating RESTful APIs for the Todo App.
How to Create and Use Database Seeder in Laravel 8
Create Controllers For Laravel 8 Passport Authentication
As per the todo project, here we will require two controllers. One for the User and another for the Todo. Hence, let’s create the controllers one by one.
php artisan make:controller UserController
AND
php artisan make:controller TodoController --resource
For the TodoController, I have used the — resource flag. This will generate the CRUD methods by default. After creating the controllers, let’s put down some codes there. Firstly, start with UserController.php file.
Create User Authentication For Laravel 8 Passport
Add the below snippet in the UserController.php file then I will explain each functions step by step.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class UserController extends Controller
{
/**
* Register User
*
* @param Request $request
* @return void
*/
public function register(Request $request) {
$validator = Validator::make($request->all(), [
"first_name" => "required",
"last_name" => "required",
"email" => "required|email|unique:users,email",
"password" => "required|min:3",
"phone" => "required|min:10"
]);
if($validator->fails()) {
return $this->validationErrors($validator->errors());
}
$user = User::create([
'first_name' => $request->first_name,
'last_name' => $request->last_name,
'full_name' => $request->first_name . " ".$request->last_name,
'email' => $request->email,
'phone' => $request->phone,
'password' => Hash::make($request->password)
]);
return response()->json(["status" => "success", "error" => false, "message" => "Success! User registered."], 201);
}
/**
* User Login
*
* @param Request $request
* @return void
*/
public function login(Request $request) {
$validator = Validator::make($request->all(), [
"email" => "required|email",
"password" => "required|min:3"
]);
if($validator->fails()) {
return $this->validationErrors($validator->errors());
}
try {
if(Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
$user = Auth::user();
$token = $user->createToken('token')->accessToken;
return response()->json(
[
"status" => "success",
"error" => false,
"message" => "Success! you are logged in.",
"token" => $token,
]
);
}
return response()->json(["status" => "failed", "message" => "Failed! invalid credentials."], 404);
}
catch(Exception $e) {
return response()->json(["status" => "failed", "message" => $e->getMessage()], 404);
}
}
/**
* Logged User Data Using Auth Token
*
* @return void
*/
public function user() {
try {
$user = Auth::user();
return response()->json(["status" => "success", "error" => false, "data" => $user], 200);
}
catch(NotFoundHttpException $exception) {
return response()->json(["status" => "failed", "error" => $exception], 401);
}
}
/**
* Logout Auth User
*
* @param Request $request
* @return void
*/
public function logout() {
if(Auth::check()) {
Auth::user()->token()->revoke();
return response()->json(["status" => "success", "error" => false, "message" => "Success! You are logged out."], 200);
}
return response()->json(["status" => "failed", "error" => true, "message" => "Failed! You are already logged out."], 403);
}
}
Let’s understand what the above code will do.
Explanation of UserController
- The register() in the UserController is defined to create the user. Here, I have set the basic validation of the inputs. When the validation will fail, it will through the validation errors message. So, for throwing the validation error message, I have defined a function in the Controller.php file. The function is added in the below snippet. If the validation is passed then it will create the user and then will return the response in the JSON.
- The next function is for the login. Just like the register function, I have set the inputs validation in the login function too. Here, the email and password are used for the login. That’s why I used the validation for both fields. Similarly, if the validation fails, it will return the error message. But, if the validation passed, then it will go to the next block of code. Here, using the Auth::attempt() function, I checked the inputs are correct. If it is then it will set the logged user data in the Auth() and it will create a token for the current logged user. After that, I returned the response of the login with the auth token.
- For getting the logged user detail, I created the user() function. This function will return the logged user detail through the auth token that will be passed through the header. (I will show you next).
- The last function is for the user logout. Here, the logout will revoke the auth token that is generated after the successful login.
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
/**
* Gloabel Validation Errors -
* It will get the object of validation errors and return in JSON
*/
public function validationErrors($validationErrors) {
return response()->json(["status" => "error", "validation_errors" => $validationErrors]);
}
}
So, that’s it for the UserController.php file.
How to Configure PHPMailer in Laravel 8 For Sending Email
Create Functionalities For Todo
In the TodoController.php add the below code.
<?php
namespace App\Http\Controllers;
use App\Models\Todo;
use Exception;
use Facade\FlareClient\Http\Exceptions\NotFound;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class TodoController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$todos = Auth::user()->todos;
return response()->json(["status" => "success", "error" => false, "count" => count($todos), "data" => $todos],200);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
"title" => "required|min:3|unique:todos,title",
"description" => "required"
]);
if($validator->fails()) {
return $this->validationErrors($validator->errors());
}
try {
$todo = Todo::create([
"title" => $request->title,
"description" => $request->description,
"user_id" => Auth::user()->id
]);
return response()->json(["status" => "success", "error" => false, "message" => "Success! todo created."], 201);
}
catch(Exception $exception) {
return response()->json(["status" => "failed", "error" => $exception->getMessage()], 404);
}
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$todo = Auth::user()->todos->find($id);
if($todo) {
return response()->json(["status" => "success", "error" => false, "data" => $todo], 200);
}
return response()->json(["status" => "failed", "error" => true, "message" => "Failed! no todo found."], 404);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$todo = Auth::user()->todos->find($id);
if($todo) {
$validator = Validator::make($request->all(), [
'title' => 'required',
'description' => 'required'
]);
if($validator->fails()) {
return $this->validationErrors($validator->errors());
}
$todo['title'] = $request->title;
$todo['description'] = $request->description;
// if has active
if($request->active) {
$todo['active'] = $request->active;
}
// if has completed
if($request->completed) {
$todo['completed'] = $request->completed;
}
$todo->save();
return response()->json(["status" => "success", "error" => false, "message" => "Success! todo updated."], 201);
}
return response()->json(["status" => "failed", "error" => true, "message" => "Failed no todo found."], 404);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$todo = Auth::user()->todos->find($id);
if($todo) {
$todo->delete();
return response()->json(["status" => "success", "error" => false, "message" => "Success! todo deleted."], 200);
}
return response()->json(["status" => "failed", "error" => true, "message" => "Failed no todo found."], 404);
}
}
Let me explain what I did in the above controller.
Explanation of TodoController
- The index() function is to list out all the todos created by the logged user. Here, the logged user means no other user can see the todo of the different users. The user will see only their own todo(s). Hence, I used Auth::user() to get the todo of the authenticated user. This will take the logged user token in the authorization.
- The store() function will insert the todo in the table. So, firstly, I have put a basic validation rule to check the title and description of the todo. Once the validation will pass, it will insert the todo with the logged user id.
- In the show() function, the logged user can see only their own todo with the todo id. It will return only one todo on the basis of todo id.
- The update() function will update the particular todo. So, it will accept the inputs and the todo id. Also, it will require the auth token.
- Lastly, the destroy() method will delete the todo on the basis of the todo id. So, overall to manage the todo, the user must be authenticated with the auth token.
How to Create Pagination in Laravel 8 with Eloquent
Create Routes For Laravel Passport Todo
Here, we are creating the API for the todo app using Laravel passport. Hence, we will have to add the routes in the api.php file.
Route::prefix('user')->group(function () {
Route::post('register', [UserController::class, 'register']);
Route::post('login', [UserController::class, 'login']);
// passport auth api
Route::middleware(['auth:api'])->group(function () {
Route::get('/', [UserController::class, 'user']);
Route::get('logout', [UserController::class, 'logout']);
// todos resource route
Route::resource('todos', TodoController::class);
});
});
In the above routes, I have used prefix with the user. Next, for the register and login, we no need any authorization. But, for the todo management and user profile detail, we required the authorized user.
So, our todo app api is ready to test. So, Let’s check by running the endpoints.
Dynamic Email Configuration in Laravel 8 For Sending Email
Result of Laravel Passport Todo API
Here, we will run the API endpoints one by one. And for testing the APIs, I will use the Postman tool. Firstly, start with user register.
User Register
The endpoint of the user register will be-
Endpoint: http://localhost:8000/api/register
Request Type: POST
Data: Body->form-data
I already explained you about the functionalities. Here, you will see the validation errors first if you hit the endpoint without the required parameters.
But, if you hit the API with the required fields inputs, you will get the success response as showing below.
Laravel 8 Client Side Form Validation Using jQuery
User Login API
Simiarly, in the user login, API validation is added. Also, when you enter the wrong credentials then it will show the below message.
Endpoint: http://localhost:8000/api/login
Request Type: POST
Data: Body->form-data
If the user credentials are correct then you will be able to login. You will get the success response
Here, in the response, I got the token. Now, I can use this token for the next API request to get the current logged user detail and for managing todo. So, just copy this token, we will have to pass it in the next endpoint.
User Detail API Using Auth Token
Endpoint: http://localhost:8000/api/user
Request Type: GET
Authorization: Bearer Token
From this endpoint, we will have to pass the Bearer Token in the authorization. So, just go to the Authorization Tab as showing below. Choose the Type as Bearer Token and simply paste the above token.
Hit the above endpoint to see the result. In the response, I got the below detail.
In case, if you don’t pass the auth token, the passport package will throw the route login not defined exception. Here, take a look at this exception.
So, we will have to handle this exception. This will occur when you set the route in the auth middleware and you don’t pass the auth token.
Laravel Passport Route Login Not Defined Exception
To handle this exception, you will need to navigate to the Exceptions/Handler.php class. Here, you will have the register() method and in that method, simply paste the below code.
$this->renderable(function (Exception $e, $request) {
return response()->json(["status" => "Unauthorized access", "message" => "You are not allowed to access the API."], 401);
});
Here, after putting the above code, the Handler.php file will become as showing below.
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->renderable(function (Exception $e, $request) {
return response()->json(["status" => "Unauthorized access", "message" => "You are not allowed to access the API."], 401);
});
}
}
In the above code, I have handled the exception using renderable() method. Here simply
Now, hit again the above endpoint (http://localhost:8000/api/user).
So, in the next step, we will run the Todos API.
Create Todo
For creating todo, we required the auth token because only logged in user will able to create todo.
Endpoint: http://localhost:8000/api/user/todos
Request Type: POST
Data: Body->form-data
Authorization: Bearer Token
Hit the above endpoint with the required inputs with authorization token.
Here, todo is created, you can seen in the below snapshot, I have entered the Authorization as Bearer Token.
List All Todos
Endpoint: http://localhost:8000/api/user/todos
Request Type: GET
Authorization: Bearer Token
Here, after passing the auth token, hit the endpoint. In the result, you will see the created todos by current user.
Create RESTful APIs in Laravel 8 Using Sanctum Auth
Todo Detail (Single Todo)
For getting single todo, you will have to pass the todo id along with the auth token.
Endpoint: http://localhost:8000/api/user/todos/{id}
Request Type: GET
Authorization: Bearer Token
Here, I passed the authorization token and then hit the endpoint with the todo id. As a response, I got the todo of that id and here the user can get only own todo followed by todo id.
Update Todo
For updating Todo, we will require the todo id along with the new updated values. Also, it will require the auth token.
Endpoint: http://localhost:8000/api/user/todos/{id}
Request Type: PUT
Data: Body->x-www-form-urlencoded
Authorization: Bearer Token
The above endpoint is for updating the todo.
Hence, after passing the authorization token, you will require to pass the data in the body as showing below.
Delete Todo
For deleting a todo, we will require the todo id. Here, the logged user can delete only own todo and that will require the todo id.
Endpoint: http://localhost:8000/api/user/todos{id}
Request Type: DELETE
Authorization: Bearer Token
Therefore, here, I entered the authorization token and todo id in the endpoint.
User Logout
Lastly, we can do logout. Here, after the logout the auth token will be revoked of that user. And then that token will no longer be available to use.
Endpoint: http://localhost:8000/api/user/logout
Request Type: GET
Authorization: Bearer Token
As a result, you can see the auth token is revoked successfully. Here, the user has been logged out. Once the auth token is revoked for the user, that user will not able to access the same auth token.
Conclusion
Finally, here, we created the Todo API using Laravel 8 Passport Authentication. Laravel passport provides the client id and client secret to authenticate the HTTP request. In this application, we created the Todo API that is fully managed by the auth token. In the next tutorial, I will be implementing this Todo API to create a Todo application in React JS. So, stay connected here.
Vijay says
Nice article, I have followed all the steps.
I was testing on Postman, till “Laravel Passport Route Login Not Defined Exception” is fine.
When tried “Create Todo” I am getting
{
“status”: “Unauthorized access”,
“message”: “You are not allowed to access the API.”
}
I checked the complete tutorial three times but same error. Can you please check and let me know what could be error.
Umesh Rana says
Hey Vijay,
Go to the Exceptions/Handler.php class and in the register() method, there is exception handled for the route login not defined. If in the todos API you will not pass the token in the header then it will show this-
{
“status”: “Unauthorized access”,
“message”: “You are not allowed to access the API.”
}
So, just pass the auth token that you are getting in the response of login for all todos operations (Create, View, Edit, and Delete).