Introducción a JWT: Cómo funciona y cómo integrarlo en tus proyectos

27/08/2024 | PHP, Seguridad | 0 comentarios

Introducción a JWT: qué es, para qué sirve y cómo usarlo en tus proyectos. Incluye ejemplos en PHP y consejos de seguridad

Descargar archivos


En el desarrollo de aplicaciones web, la autenticación y autorización de usuarios son aspectos importantes para garantizar la seguridad. En este artículo, exploraremos qué es JWT, para qué sirve, y cómo puedes empezar a integrarlo en tus proyectos.

JSON Web Token (JWT)

JSON Web Token (JWT) es un estándar que define una forma compacta de transmitir información de manera segura como un objeto JSON. Este token es autoverificable y puede contener información del usuario, los permisos y más.

Funcionamiento de JTW

Después de que un usuario se autentica en un servicio, el servidor genera un JWT y lo envía al cliente. El cliente almacena este token y lo envía en cada solicitud posterior en el encabezado HTTP Authorization como un Bearer Token. Esto permite que el servidor verifique la autenticidad del usuario sin necesidad de almacenar el estado del usuario en el servidor.

Estructura de JWT

Un JWT está compuesto por tres partes separadas por puntos (.), y codificadas en base64:


eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MjE2MDYxOTcsIm5hbWUiOiJMb3Vpcy
BBcm1zdHJvbmciLCJ1c2VyIjoiMzQ5NDIifQ.mWRX8cHElqpwSYPDxfON26CH90mPpJMrKjOYZWyneJU

Header

El encabezado contiene: typ con el tipo de token y alg para el algoritmo de la firma.


{
    "typ": "JWT",
    "alg": "HS256"
}

Payload

El payload contiene las claims que son declaraciones de una entidad y datos adicionales.

  • Registered claims: Son afirmaciones predefinidas y opcionales, como: iss (emisor), exp (fecha de expiración), sub (asunto) y aud (audiencia).
  • Public claims: Afirmaciones definidas por los usuarios y que deben ser únicas.
  • Private claims: Afirmaciones personalizadas para compartir información entre partes.

{
    "sub": "1234567890",
    "name": "John Doe",
    "admin": true
}

Signature

La firma del token se genera codificando en base64 el header y payload, luego se concatena separándolos por un punto y finalmente se aplica el algoritmo especificado en el header.


HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

Creando JWT con PHP

Ahora que sabemos como crear un token JWT, vamos a usar PHP para crear un token, para ello necesitamos declarar el header, el payload y una clave secreta para firmar el token.


<?php
function base64_url_encode($text):String{
    return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($text));
}

$secret = 'ZU83yaBbeMkcsYQFCSXK5H';
$header = [
    "typ" => "JWT",
    "alg"=> "HS256"
];

$payload = [
    'iat' => time(),
    'nbf' => time() + 3600,
    'name' => 'Louis Armstrong',
    'user' => 34942,
];

$header_encode = base64_url_encode(json_encode($header));
$payload_encode = base64_url_encode(json_encode($payload));
$signature = base64_url_encode(hash_hmac('sha256',"$header_encode.$payload_encode", $secret, true));

// create jwt token
$jwt = $header_encoded. '.' . $payload_encoded . '.' . $signature;
echo json_encode(['token' => $jwt]);

Creando JWT con php-jwt

Existen varias librerías que implementan la creación y validación de token JWT de manera sencilla, para nuestro caso usaremos firebase/php-jwt. Para utilizarla necesitamos instalarla usando Composer.

composer require firebase/php-jwt

Luego podemos crear un token de manera sencilla:


<?php
require "vendor/autoload.php";
use Firebase\JWT\JWT;

$secret = 'ZU83yaBbeMkcsYQFCSXK5H';
$payload = [
    'iat' => time(),
    'nbf' => time() + 3600,
    'name' => 'Louis Armstrong',
    'user' => 34942,
];

$jwt = JWT::encode($payload, $secret, 'HS256');
echo json_encode(['token' => $jwt]);

Ejemplo Completo

Como ejemplo vamos a crear un microservicio con tres archivos:

  • security.php: implementa la función para validar tokens que vienen en el header.
  • auth.php: valida el usuario y clave y devuelve el token JWT.
  • profile.php: valida el token JWT y si es correcto devuelve los datos del usuario.

Implementamos una función que se encargue de leer todos los headers con getallheaders, luego extraemos el header con nombre Authorization para finalmente validar si es un token válido con JWT::decode.


<?php
// file: security.php
require "vendor/autoload.php";

const JWT_SECRET = 'ZU83yaBbeMkcsYQFCSXK5H';  // declare secret key

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

function validateJWT() {
    $headers = getallheaders();
    $authHeader = $headers['Authorization'] ?? '';

    if (preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
        $jwt = $matches[1];

        try {
            // decode token 
            $decoded = JWT::decode($jwt, new Key(JWT_SECRET, 'HS256'));
            return $decoded;
        } catch (Exception $e) {
            http_response_code(403);
            echo json_encode(['message' => 'Acceso prohibido']);
            exit;
        }
    } else {
        http_response_code(401);
        echo json_encode(['message' => 'Token no proporcionado']);
        exit;
    }
}

Lo siguiente es implementar el login de usuarios, como ejemplo sólo validamos el usuario y clave y si es correcto generamos el token con JWT::encode y lo enviamos al usuario:


<?php
// file: auth.php
require "vendor/autoload.php";
require "security.php";

use Firebase\JWT\JWT;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    $username = $input['username'] ?? '';
    $password = $input['password'] ?? '';

    if ($username === 'admin' && $password === 'password') {
        $payload = [
            'iat' => time(),
            'name' => 'Louis Armstrong',
            'user' => '34942'
        ];

        $jwt = JWT::encode($payload, JWT_SECRET, 'HS256');
        echo json_encode(['token' => $jwt]);
    } else {
        http_response_code(401);
        echo json_encode(['message' => 'Credenciales inválidas']);
    }
    exit;
}

Finalmente, si el usuario quiere acceder a los datos de su perfil, primero validamos el token con la función validateJWT y si es correcto devolvemos los datos al usuario.


<?php
// file: profile.php
require "security.php";

$decoded = validateJWT();

// query user data
$profile = array(
    'id' => $decoded->user,
    'name' => 'Louis Armstrong',
    'email' => 'louis@gmail.com',
    'phone' => '998888888',
    'admin' => true
);
echo json_encode($profile);

Listo, lo que sigue es incluir esta función al inicio de cada endpoint para validar el token y continuar con los demás procesos.

Conclusión

JWT es una herramienta poderosa y versátil para la autenticación y autorización en aplicaciones. Su estructura compacta y autónoma lo hace ideal para transmitir información de manera segura y eficiente.

Referencias

Envíar Comentario

En este sitio los comentarios se publican previa aprobación del equipo de Kodetop. Evita los comentarios ofensivos, obscenos o publicitarios. Si deseas publicar código fuente puedes hacerlo entre las etiquedas <pre></pre>