Laravel 12 API Authentication using Passport Tutorial with Example
Learn how to set up secure Rest API authentication in Laravel 12 using Passport. Step-by-step guide to OAuth2, token management, and protecting routes. Perfect for developers building robust APIs!

APIs are the backbone of modern web applications, and securing them is crucial. Laravel, one of the most popular PHP frameworks, provides a powerful package called Laravel Passport to handle API authentication seamlessly. In this blog post, we’ll walk through how to set up Rest API authentication in Laravel 12 using Passport.
What is Laravel Passport?
Laravel Passport is an OAuth2 server implementation for Laravel. It provides a full OAuth2 server implementation for your Laravel application, allowing you to issue access tokens and authenticate API requests. Passport is built on top of the League OAuth2 Server and is a great choice for securing your APIs.
Prerequisites
Before we dive into the implementation, make sure you have the following:
- Laravel 12 installed on your local machine.
- A database configured in your
.env
file. - Basic knowledge of Laravel (routes, controllers, migrations, etc.).
Step 1: Install Laravel Passport
To get started, you need to install Passport via Composer.
Run the following command in your terminal:
php artisan install:api --passport
Step 2: Run Passport Migrations
Passport requires a database table to store API tokens. Run the following command to create the necessary migration:
php artisan migrate
Step 3: Configure Authentication Guards
In config/auth.php, set the API driver to passport:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Step 4: Configure User Model to Use Passport
Next, you need to update your User model to use the HasApiTokens trait provided by Passport.
Open the app/Models/User.php file and add the following:
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
This trait allows the user model to issue API tokens.
Step 5: Create Routes
Now, let’s create routes for user registration, login, and token-based authentication. Open the routes/api.php file and add the following routes:
<?php
use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\ProductController;
use Illuminate\Support\Facades\Route;
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::middleware('auth:api')->group(function () {
Route::get('user', [AuthController::class, 'user']);
Route::post('logout', [AuthController::class, 'logout']);
Route::prefix('a_products')->group(function () {
Route::get('/', [ProductController::class, 'index']);
Route::post('save', [ProductController::class, 'save']);
Route::get('{id}/show', [ProductController::class, 'show']);
Route::patch('{id}/update', [ProductController::class, 'update']);
Route::delete('{id}/delete', [ProductController::class, 'delete']);
});
});
Here’s what each route does:
- /register: Handles user registration.
- /login: Handles user login and returns an API token.
- /logout: Logs the user out and revokes the token.
- /user: Returns the authenticated user’s details.
- /a_products: Returns a list of all products.
- /a_products/save: Creates a new product with the provided details.
- /a_products/{id}/show: Returns the details of a specific product based on the provided id.
- /a_products/{id}/update: Updates the details of a specific product based on the provided id.
- /a_products/{id}/delete: Deletes a specific product based on the provided id.
Step 6: Create the UserResource File
Next, create a UserResource to format the JSON response for your API. Run the following Artisan command:
php artisan make:resource UserResource
This command will generate a new file at app/Http/Resources/UserResource.php. Open the newly created UserResource.php file located in app/Http/Resources. Update the toArray method to customize the JSON response for the User model. For example:
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'email_verified_at' => $this->email_verified_at,
];
}
Step 7: Create the AuthController
Next, create a controller to handle authentication logic. Run the following command:
php artisan make:controller Api\AuthController
Now, open the app/Http/Controllers/Api/AuthController.php file and add the following methods:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\CreateUserRequest;
use App\Http\Requests\Api\LoginRequest;
use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
public function register(CreateUserRequest $request): JsonResponse
{
$data = $request->validated();
$data['password'] = Hash::make($data['password']);
User::query()->create($data);
return response()->json(['message' => 'User registerd successfully'], 201);
}
public function login(LoginRequest $request): JsonResponse
{
$data = $request->validated();
if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
$user = Auth::user();
$token = $user->createToken('authToken')->accessToken;
return response()->json(['token' => $token], 200);
}
return response()->json(['message' => 'The provided credentials are incorrect.'], 401);
}
public function user(): UserResource
{
$user = Auth::user();
return new UserResource($user);
}
public function logout(Request $request): JsonResponse
{
$request->user()->token()->revoke();
return response()->json(['message' => 'Logged out successfully'], 200);
}
}
Next, Create the Form request file to handle the incoming request. Run following command to create file
php artisan make:request Api\CreateUserRequest
php artisan make:request Api\LoginRequest
Update app/Http/Request/Api/CreateUserRequest.php File:
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
$rules = [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users', 'max:255'],
'password' => ['required', 'string', 'min:8', 'max:255', 'confirmed'],
];
return $rules;
}
Update app/Http/Request/Api/LoginRequest.php File:
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
$rules = [
'email' => ['required', 'email', 'max:255'],
'password' => ['required', 'min:8'],
];
return $rules;
}
Step 8: Create the Product Migration File
Now for handle the product CRUD create the migration file. Run the following command:
php artisan make:migration create_products_table
Update Migration File:
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description');
$table->decimal('price');
$table->string('image');
$table->timestamps();
});
}
Note: Don't forgot to run migration for create product table.
Step 9: Create the Product Model File:
Run following Command for create the Product model file:
php artisan make:model Product
Open the Product model at app/Models/Product.php and update it with the following code:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $guarded = [];
}
$guarded empty array allow all filed fillable
Step 10: Create the ProductResource File
Next, create a ProductResource to format the JSON response for your API. Run the following Artisan command:
php artisan make:resource ProductResource
This command will generate a new file at app/Http/Resources/ProductResource.php. Open the newly created ProductResource.php file located in app/Http/Resources. Update the toArray method to customize the JSON response for the Product model. For example:
public function toArray(Request $request): array
{
return [
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'image' => asset('storage/product/'.$this->image),
];
}
Step 11: Create the ProductController
Next, create a controller to handle Product CRUD logic. Run the following command:
php artisan make:controller Api\ProductController
Now, open the app/Http/Controllers/Api/ProductController.php file and add the following methods:
<?php
namespace App\Http\Controllers\Api;
use App\Helper\CommonHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\CreateProductRequest;
use App\Http\Resources\ProductResource;
use App\Models\Product;
use Illuminate\Http\JsonResponse;
class ProductController extends Controller
{
public function index()
{
$products = Product::query()->paginate(5);
return ProductResource::collection($products);
}
public function save(CreateProductRequest $request): JsonResponse
{
$data = $request->validated();
$data['image'] = CommonHelper::uploadFile($request->file('image'), 'product');
Product::query()->create($data);
return response()->json(['message' => 'Product create successfuly'], 200);
}
public function show($id): JsonResponse|ProductResource
{
$product = Product::query()->find($id);
if (! empty($product)) {
return new ProductResource($product);
}
return response()->json(['message' => 'Product not found'], 404);
}
public function update($id, CreateProductRequest $request): JsonResponse
{
$data = $request->validated();
$product = Product::query()->where('id', $id)->first();
if (! empty($product)) {
if ($request->hasFile('image')) {
$data['image'] = CommonHelper::uploadFile($request->file('image'), 'product', $product->image);
}
$product->update($data);
return response()->json(['message' => 'Product update successfully'], 200);
}
return response()->json(['message' => 'Product not found'], 404);
}
public function delete($id): JsonResponse
{
$product = Product::where('id', $id)->first();
if (! empty($product)) {
CommonHelper::removeOldFile('public/product/'.$product->image);
$product->delete();
return response()->json(['message' => 'Product delete successfully'], 200);
}
return response()->json(['message' => 'Product not found'], 404);
}
}
Next, Create the Form request file to handle the incoming request. Run following command to create file
php artisan make:request Api\CreateProductRequest
Update app/Http/Request/Api/CreateProductRequest.php File:
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
$rules = [
'name' => ['required', 'string', 'max:255'],
'description' => ['required', 'string'],
'price' => ['required', 'numeric'],
'image' => ['required', File::types(['jpeg', 'jpg', 'png', 'webp'])->max(1 * 500)],
];
if (in_array($this->method(), ['PUT', 'PATCH'])) {
$rules['name'][0] = 'sometimes';
$rules['description'][0] = 'sometimes';
$rules['price'][0] = 'sometimes';
$rules['image'] = array_merge(['sometimes'], CommonHelper::getFileValidationRule('image', ['jpeg', 'jpg', 'png', 'webp'], (1 * 500)));
}
return $rules;
}
Step 12: Create CommonHelper File
Create CommonHelper.php file in app\Helper directory.
update the app\Helper\CommonHelper.php file:
<?php
namespace App\Helper;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rules\File;
class CommonHelper
{
public static function uploadFile(UploadedFile $file, $path, $oldFile = ''): string
{
if (! empty($file)) {
// Remove Old file
if (! empty($oldFile)) {
Storage::delete('public/'.$path.'/'.$oldFile); // Delete file from local
}
// Upload image
$path = $file->store('public/'.$path);
$parts = explode('/', $path);
return end($parts);
}
return '';
}
public static function removeOldFile($oldFile): void
{
// Remove Old file
if (! empty($oldFile)) {
Storage::delete($oldFile); // Delete file from local
}
}
public static function getFileValidationRule(string $key, $types, $size = (1 * 500)): array
{
if (request()->hasFile($key)) {
return [File::types($types)->max($size)];
}
return ['string'];
}
}
Step 13: Change File storing path private to public
Change the value of key drive in 'local' array in disk array storage_path('app/private') to storage_path('app').
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'serve' => true,
'throw' => false,
'report' => false,
],
now link storage to public directory.
php artisan storage:link
Step 14: Test Your API
Now that your API is set up, it’s time to test it! You can use tools like Postman or cURL to send requests to your API endpoints and verify their functionality.
- Register a User
- Endpoint: POST /api/register
Body:
{ "name": "Test User", "email": "test@gmail.com", "password": "password", "password_confirmation": "password" }
- Login
- Endpoint: POST /api/login
Body:
{ "email": "test@gmail.com", "password": "password" }
- Access Authenticated Route
- Endpoint: GET /api/user
Header:
Authorization: Bearer your-api-token
- Logout
- Endpoint: POST /api/logout
Header:
Authorization: Bearer your-api-token
Product CRUD Operations
- List All Products
- Endpoint: GET /api/a_products
Header:
Authorization: Bearer your-api-token
- Create a New Product
- Endpoint: POST /api/a_products/save
Header:
Authorization: Bearer your-api-token
Body:
{ "name": "Test Product", "description": "Description of the product without any length limitation.", "price": 200, "image": "anyimage.jpg" }
- Show Product Details
- Endpoint: GET /api/a_products/{id}/show
Header:
Authorization: Bearer your-api-token
- Update a Product
- Endpoint: PATCH /api/a_products/{id}/update
Header:
Authorization: Bearer your-api-token
Body:
{ "name": "Updated Product Name", "description": "Updated description of the product.", "price": 250, "image": "updatedimage.jpg" }
- Note: If the PATCH method doesn’t work in your API call, you can use POST with a hidden input _method='PATCH'.
- Delete a Product
- Endpoint: DELETE /api/a_products/{id}/delete
Header:
Authorization: Bearer your-api-token
Testing Tips
- Use Postman or any API testing tool to send requests to the endpoints.
- Ensure you include the Authorization header with the Bearer token for authenticated routes.
- Verify the responses to confirm that your API is working as expected.
By following these steps, you can thoroughly test your API and ensure that all endpoints are functioning correctly. This will help you identify and fix any issues before deploying your application.
Conclusion
Congratulations! You have successfully set up secure API authentication in Laravel 12 using Laravel Passport. This authentication system ensures that API requests are properly secured, preventing unauthorized access. Laravel Passport makes it simple to implement OAuth2 authentication in your Laravel projects, providing robust security for modern web applications.
authentication.
For more Laravel tutorials and API authentication guides, follow this blog and stay updated. Happy coding! 🚀