JWT y Postman

O, mejor dicho, como probar tus APIs que usen JWT en postman.

Cuando estamos diseñando APIs es muy normal que, lo primero, las diseñemos sin pensar en el tema de la seguridad y, una vez que están funcionando, ya les añadimos los elementos de seguridad para que no puedan ser llamadas por cualquiera en producción.

Una forma, que yo he usado mucho, para fortificar las llamadas es hacerlo mediante un token que se ha obtenido con las credenciales de un usuario concreto, eso supone hacer una llamada previa al sistema del que se obtiene el token y, después, verificarlo en cada llamada. Esto, siendo un token arbitrario, supone sobrecargar el sistema de seguridad con llamadas de comprobación de token, además, tenemos que hacer caducar los tokens periódicamente ya que cualquier filtración del mismo da acceso a los recursos del usuario durante el tiempo que el token esté sin caducar.

Otra manera, ligeramente diferente, es utilizar JWT (JSON Web Tokens) que se diseñaron para poder transmitir credenciales y otra información entre sistemas de una manera más eficiente y segura. Lo único un poco «especial» es que tiene que haber un secreto compartido entre los sistemas que se comunican. Además, se usa ese secreto para firmar la comunicación, así que no es sencillo generar el token JWT de forma manual para las pruebas.

Si usas postman (que es lo que estoy usando yo últimamente) para hacer las pruebas, hay una manera de solventar el problema y que, básicamente, pasa por rellenar el campo pre-request script con este código:

// JWT generation script adapted from
// https://gist.github.com/corbanb/db03150abbe899285d6a86cc480f674d

var jwtSecret = pm.environment.get('jwt_secret') || ''

// Set headers for JWT
var header = {
	'typ': 'JWT',
	'alg': 'HS256'
};

// Prepare timestamp in seconds
var currentTimestamp = Math.floor(Date.now() / 1000)

var data = {
	'iss': pm.environment.get('jwt_iss') || '',
	'ist': pm.environment.get('jwt_ist') || '',
	'iat': currentTimestamp,
	'exp': currentTimestamp + 30, // expiry time is 30 seconds from time of creation
	'jti': 'jwt_nonce'
}


function base64url(source) {
    // Encode in classical base64
    encodedSource = CryptoJS.enc.Base64.stringify(source)
    
    // Remove padding equal characters
    encodedSource = encodedSource.replace(/=+$/, '')
    
    // Replace characters according to base64url specifications
    encodedSource = encodedSource.replace(/\+/g, '-')
    encodedSource = encodedSource.replace(/\//g, '_')
    
    return encodedSource
}

// encode header
var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(header))
var encodedHeader = base64url(stringifiedHeader)

// encode data
var stringifiedData = CryptoJS.enc.Utf8.parse(JSON.stringify(data))
var encodedData = base64url(stringifiedData)

// build token
var token = `${encodedHeader}.${encodedData}`

// sign token
var signature = CryptoJS.HmacSHA256(token, jwtSecret)
signature = base64url(signature)
var signedToken = `${token}.${signature}`

pm.environment.set('jwt_signed', signedToken)
console.log('Signed and encoded JWT', signedToken)

En el mismo vemos que se usan estas variables que deberemos incluir en el entorno o en la petición:

  • jwt_secret : secreto compartido con el otro sistema
  • jwt_iss: issuer (como queramos identificar al emisor)

En cualquier caso también podríamos añadir el campo sub si es parte del payload que recibe nuestro API, o cualquier otro campo adicional que decidamos incluir, ya que el proceso de firma se realiza en ese momento y las fechas de expiración se actualizan justo antes de lanzar la petición al API por lo que no estarán expiradas en las pruebas.

Lo único que quedaría pendiente sería usar el token generado en la cabecera (o donde corresponda) ya que este código genera la variable {{jwt_signed}} esta la podemos usar donde queramos.

Sigo buscndo formas más sencillas para hacer las pruebas sin salir del Visual Studio Codel, si las encuentro las pondré por aquí.

Deja una respuesta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.