Usar JSON Web Tokens (JWT) con Laravel para Crear APIs Seguras
Utilizar JWT para autenticar usuarios a través de APIs es especialmente útil pues no se necesita implementar sesiones en el servidor y el JWT en sí mismo contiene la información correspondiente al usuario que realiza la petición por lo que sirve para validar la autenticidad del mismo.
Primero que nada necesitamos instalar Laravel y crear un proyecto. Instrucciones para instalar Laravel 5.8. Luego de tener instalado Laravel procedemos a crear un proyecto.
$ laravel new webpage
Para implementar JWT en Laravel utilizaremos el módulo llamado tymondesigns/jwt-auth con el siguiente código:
$ composer require tymon/jwt-auth:dev-develop --prefer-source
Luego para generar el archivo de configuración corremos el siguiente código:
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
Procedemos a generar una llave para nuestro proyecto:
$ php artisan jwt:secret
Para que Laravel se conecte automáticamente a nuestra base de datos y la utiliza como método de validación de usuarios. Debemos crear una tabla users
con las siguientes columnas:
id
PRIMARY KEY AUTOINCREMENTname
VARCHARsurname
VARCHARemail
VARCHARpassword
VARCHARcreate_at
TIMESTAMPupdated_at
TIMESTAMP
El modelo User
utilizará por defecto la tabla users
. Si se quiere especificar otro nombre para la tabla, se le puede definir el nombre a usar en app\User.php
con el siguiente código:
...
protected $table = ''; // colocamos el nombre de la tabla
...
Los pasos que siguen a continuación son a nivel de código. Necesitamos implementar Tymon\JWTAuth\Contracts\JWTSubject
en el modelo de User
. En este modelo debemos implementar dos métodos: getJWTIdentifier()
y getJWTCustomClaims()
.
El modelo de User
en app\User.php
debería quedar similar a este:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject {
use Notifiable;
protected $fillable = [
'name', 'surname', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
public function getJWTIdentifier() {
return $this->getKey();
}
public function getJWTCustomClaims() {
return [];
}
}
Ahora para que Laravel utilice JWT como método de autenticación, abrimos el archivo config/auth.php
y modificamos los siguientes datos:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
...
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
Ahora procedemos a agregar el controlador de autenticación. Lo llamaremos AuthController
. Podemos crearlo manualmente o con el siguiente comando:
$ php artisan make:controller AuthController
En este controlador colocamos el siguiente código:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\RegisterAuthRequest;
use App\User;
use Illuminate\Http\Request;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthController extends Controller {
public $loginAfterSignUp = true;
public function register(Request $request) {
$user = new User();
$user->name = $request->name;
$user->surname = $request->surname;
$user->email = $request->email;
$user->password = bcrypt($request->password);
$user->save();
if ($this->loginAfterSignUp) {
return $this->login($request);
}
return response()->json([
'status' => 'ok',
'data' => $user
], 200);
}
public function login(Request $request) {
$input = $request->only('email', 'password');
$jwt_token = null;
if (!$jwt_token = JWTAuth::attempt($input)) {
return response()->json([
'status' => 'invalid_credentials',
'message' => 'Correo o contraseña no válidos.',
], 401);
}
return response()->json([
'status' => 'ok',
'token' => $jwt_token,
]);
}
public function logout(Request $request) {
$this->validate($request, [
'token' => 'required'
]);
try {
JWTAuth::invalidate($request->token);
return response()->json([
'status' => 'ok',
'message' => 'Cierre de sesión exitoso.'
]);
} catch (JWTException $exception) {
return response()->json([
'status' => 'unknown_error',
'message' => 'Al usuario no se le pudo cerrar la sesión.'
], 500);
}
}
public function getAuthUser(Request $request) {
$this->validate($request, [
'token' => 'required'
]);
$user = JWTAuth::authenticate($request->token);
return response()->json(['user' => $user]);
}
}
Ahora creamos las rutas que accederan a estos métdos en routes\api.php
:
<?php
use Illuminate\Http\Request;
// estas rutas se pueden acceder sin proveer de un token válido.
Route::post('/login', 'AuthController@login');
Route::post('/register', 'AuthController@register');
// estas rutas requiren de un token válido para poder accederse.
Route::group(['middleware' => 'auth.jwt'], function () {
Route::post('/logout', 'AuthController@logout');
});
Adicionalmente podemos añadir al comienzo del public\index.php
el siguiente código para evitar error de CORS durante nuestras pruebas:
// permite peticiones desde cualquier origen
header('Access-Control-Allow-Origin: *');
// permite peticiones con métodos GET, PUT, POST, DELETE y OPTIONS
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
// permite los headers Content-Type y Authorization
header('Access-Control-Allow-Headers: Content-Type, Authorization');
Esto concluye la parte de la cofiguración de Laravel para utilizar JSON Web Tokens. Ahora procedemos a hacer algunas peticiones de prueba utilizando Insomnia para comprobar que nuestras rutas están funcionando.
Realizamos una petición a la ruta /api/register
y confirmamos que recibimos un status
: ok
y un token
.
Esto quiere decir que en la base de datos se registró este usuario y se generó un token de forma automática que debemos enviar con cada petición a rutas protegidas.
Podemos también probar /api/login
.
El token lo podemos enviar en cada petición de distintas formas.
- Podemos hacerlo por url en el caso de usar
GET
por ejemplo.../api/products?token=eyJ0eXAiOiJ...
. - Como una propiedad de un JSON enviado en un
POST
. - O como parte del header
Authorization: Bearer eyJ0eXAiOiJ...
. Aprende más.
En el siguiente ejemplo lo enviamos como una propiedad en un JSON para desloguear al usuario usando api/logout
.
De la misma forma podríamos continuar creando más rutas protegidas las cuales requerirían de un token válido para ser accedidas. Como siempre cualquier duda la pueden dejar en los comentarios. Saludos.