Documentación completa de la implementación de una API RESTful en Laravel 12 con sistema de autenticación JWT y control de acceso basado en roles (admin/user).
- Instalación de JWT Auth
- Configuración del Modelo User
- Migración de Base de Datos
- Creación de Middlewares
- Creación de Modelos y Controladores
- Controlador de Autenticación
- Configuración de Middlewares
- Definición de Rutas
- Pruebas con Postman/FlashPost
Ejecuta el siguiente comando para instalar la dependencia de autenticación JWT:
composer require tymon/jwt-authPublica el archivo de configuración del proveedor de servicios:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"Genera la clave secreta que se utilizará para firmar los tokens:
php artisan jwt:secretNota: Estos comandos provienen de la documentación oficial de JWT Auth
Modifica el archivo app/Models/User.php para implementar la interfaz JWTSubject:
<?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 Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}En la migración de usuarios (database/migrations/xxxx_create_users_table.php), agrega el campo role:
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('role', 20);
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}Agrega el campo role al array $fillable en app/Models/User.php:
protected $fillable = [
'name',
'role',
'email',
'password',
];Crea un middleware para verificar que el usuario esté autenticado:
php artisan make:middleware isUserAuthCrea un middleware para verificar que el usuario sea administrador:
php artisan make:middleware isAdminEjecuta el siguiente comando para crear el modelo, migración y controlador en un solo paso:
php artisan make:model Product -mcNota: Con este comando se crean automáticamente el modelo, la migración y el controlador.
php artisan make:controller AuthControllerImplementa toda la lógica de autenticación en app/Http/Controllers/AuthController.php:
class AuthController extends Controller
{
// Registro
public function register(Request $request)
{
$validator = validator::make($request->all(), [
'name' => 'required|string|max:255',
'role' => 'required|string|in:admin,user', // Validación para el rol
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 400);
}
User::create([
'name' => $request->get('name'),
'role' => $request->get('role'), // Se guarda el rol
'email' => $request->get('email'),
'password' => bcrypt($request->get('password')),
]);
return response()->json(['message' => 'User registered successfully'], 201);
}
// Login
function login(Request $request)
{
$validator = validator::make($request->all(), [
'email' => 'required|string|email|max:255',
'password' => 'required|string|min:6',
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 400);
}
$credentials = $request->only('email', 'password');
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'Invalid Credentials'], 401);
}
} catch (JWTException $e) {
return response()->json(['error' => 'Could not create token'], 500);
}
return response()->json([
'message' => 'Login successful',
'token' => $token,
'token_type' => 'bearer',
'expires_in' => config('jwt.ttl') * 60 // El token durará 60 minutos
], 200);
}
// Obtener usuario autenticado
public function getUser(){
$user = Auth::user();
return response()->json($user, 200);
}
// Logout
public function logout(){
JWTAuth::invalidate(JWTAuth::getToken());
return response()->json(['message' => 'User logged out successfully'], 200);
}
}Modifica el archivo app/Http/Middleware/isUserAuth.php:
public function handle(Request $request, Closure $next): Response
{
// Si un usuario está autenticado se aceptan las peticiones, si no se rechazan
if (auth('api')->user()) {
return $next($request);
} else {
return response()->json(['message' => 'Unauthorized'], 401);
}
}Modifica el archivo app/Http/Middleware/isAdmin.php para validar si el usuario es administrador:
public function handle(Request $request, Closure $next): Response
{
$user = auth('api')->user();
if ($user && $user->role === 'admin') {
return $next($request);
} else {
return response()->json(['message' => 'You are not an admin'], 403);
}
}En el archivo bootstrap/app.php, registra los middlewares:
->withMiddleware(function (Middleware $middleware): void {
isUserAuth::class;
isAdmin::class;
})Y agrega las importaciones necesarias:
use App\Http\Middleware\isAdmin;
use App\Http\Middleware\isUserAuth;Configura las rutas públicas y privadas en routes/api.php:
// Rutas públicas
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
// Rutas privadas
Route::middleware([isUserAuth::class])->group(function () {
// Las rutas privadas requieren que el usuario esté autenticado
Route::controller(AuthController::class)->group(function () {
Route::post('logout', 'logout');
Route::get('me', 'getUser');
});
Route::get('products', [ProductController::class, 'index']);
// Rutas exclusivas para administradores
Route::middleware([isAdmin::class])->group(function () {
Route::controller(ProductController::class)->group(function () {
Route::get('products', 'index');
Route::post('products', 'store');
Route::get('/products/{id}', 'show');
Route::put('/products/{id}', 'update');
Route::patch('/products/{id}', 'updatePartial');
Route::delete('/products/{id}', 'destroy');
});
});
});- Ejecuta el proyecto Laravel
- Abre tu cliente de base de datos preferido para visualizar los registros
- Utiliza Postman o FlashPost para probar la API
Endpoint: POST http://127.0.0.1:8000/api/register
Al enviar la petición sin datos, deberías recibir un error indicando que los campos son obligatorios.
{
"name": "Forlan Ordoñez",
"role": "admin",
"email": "foor@gmail.com",
"password": "123456789"
}Este envío solicitará la confirmación de contraseña.
{
"name": "Forlan Ordoñez",
"role": "admin",
"email": "foor@gmail.com",
"password": "123456789",
"password_confirmation": "123456789"
}Respuesta esperada:
{
"message": "User registered successfully"
}El usuario se creará correctamente en la base de datos.
Endpoint: POST http://127.0.0.1:8000/api/login
Body (JSON):
{
"email": "foor@gmail.com",
"password": "123456789"
}Respuesta esperada:
{
"message": "Login successful",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvYXBpL2xvZ2luIiwiaWF0IjoxNzcwMDY0MjgxLCJleHAiOjE3NzAwNjc4ODEsIm5iZiI6MTc3MDA2NDI4MSwianRpIjoiSEtXSWhsSTl6NTZEWkFCVSIsInN1YiI6IjEiLCJwcnYiOiIyM2JkNWM4OTQ5ZjYwMGFkYjM5ZTcwMWM0MDA4NzJkYjdhNTk3NmY3In0.JCh4pzcwdDfU9pNuU_qmgou7loP7OXsRfHRXM88hGh4",
"token_type": "bearer",
"expires_in": 3600
}Importante: Copia el token generado, ya que lo necesitarás para las siguientes peticiones autenticadas. El token tiene una duración de 60 minutos.
Endpoint: GET http://127.0.0.1:8000/api/me
- Abre una nueva pestaña en Postman/FlashPost
- Ve a la sección Auth o Authorization
- Selecciona el tipo Bearer Token
- Pega el token obtenido en el login
- Envía la petición
Respuesta esperada:
{
"id": 1,
"name": "Forlan Ordoñez",
"role": "admin",
"email": "foor@gmail.com",
"email_verified_at": null,
"created_at": "2026-02-02T20:11:42.000000Z",
"updated_at": "2026-02-02T20:11:42.000000Z"
}Endpoint: POST http://127.0.0.1:8000/api/logout
- Ve a la sección Auth o Authorization
- Selecciona el tipo Bearer Token
- Pega el mismo token utilizado anteriormente
- Envía la petición
Respuesta esperada:
{
"message": "User logged out successfully"
}Después del logout, el token será invalidado y no podrá utilizarse nuevamente.
Endpoint: POST http://127.0.0.1:8000/api/products
para crear un producto se necesita authenticacion.
se imicia seccion y se copia el token de la misma con el endpoint products.
Respuesta esperada:
{
3 items
"message":"Error de validación"
"errors"
:
{
3 items
"name"
:
[
1 items
0:"The name field is required."
]
"description"
:
[
1 items
0:"The description field is requ...
]
"price"
:
[
1 items
0:"The price field is required."...
]
}
"status":400
}una vez visto ese mensjaje se procede a crear un producto:
{
"name": "vino 300mil años",
"description": "el vino que tomo cleopatara y tal",
"price": "28.000"
}Importante si eres user no puedes hacer el crud, si deseas hacerlo debes de modicarlo para ello.
- Todos los endpoints privados requieren el token de autenticación en el header
Authorization: Bearer {token} - Los usuarios con rol
admintienen acceso completo al CRUD de productos - Los usuarios con rol
usersolo pueden listar productos - La duración del token es de 60 minutos (configurable en
config/jwt.php)
Desarrollado con ❤️ usando Laravel 12