Skip to content

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.

Conocer más sobre JWT

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.

bash
$ laravel new webpage

Para implementar JWT en Laravel utilizaremos el módulo llamado tymondesigns/jwt-auth con el siguiente código:

bash
$ composer require tymon/jwt-auth:dev-develop --prefer-source

Luego para generar el archivo de configuración corremos el siguiente código:

bash
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

Procedemos a generar una llave para nuestro proyecto:

bash
$ 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 AUTOINCREMENT
  • name VARCHAR
  • surname VARCHAR
  • email VARCHAR
  • password VARCHAR
  • create_at TIMESTAMP
  • updated_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:

php
...
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
<?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:

php
'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:

bash
$ php artisan make:controller AuthController

En este controlador colocamos el siguiente código:

php
<?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
<?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:

php
// 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.

Prueba de registro con JWT

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.

Prueba de inicio de sesión con JWT

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.

Prueba de cierre de sesión con JWT

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.