Abe Estrada

AWS Lambda + JWT

En caso de necesitar validar un JWT (JSON Web Token) con AWS Lambda para alguna aplicación “serverless”, se puede realizar de la siguiente manera.

Primero hay que crear un folder, en este caso yo lo llamé jsonwebtoken y ejecute los siguientes comandos:

npm init -y
npm install jsonwebtoken --save

El archivo package.json quedó de la siguiente manera:

{
    "dependencies": {
        "jsonwebtoken": "^8.5.1"
    }
}

Luego hay que crear el paquete para poder subirlo a Lambda Layers.

zip -r jsonwebtoken-8.5.1.zip ./*

Me gusta agregar la versión de la librería que estoy utilizando en la descripción para ser más claro y llevar un mejor control.

Una vez creada esta capa (Layer), hay que agregarla a la función que vamos a utilizar para que pueda ser importada.

En este caso vamos a utilizar un JWT asimétrico RS256 por lo que es necesario crear un par de llaves, una pública y una privada

Para crear las llaves, se puede utilizar lo siguiente:

ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
# No agregar passphrase
openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub

Luego hay que guardar de forma seguro la llave pública (jwtRS256.key.pub) en Systems Manager / Parameter Store.

Ya listos los pasos anteriores, este es el código mínimo para poder verificar un JWT.

const AWS = require("aws-sdk");
AWS.config.update({ region: "us-east-1" });
const ssm = new AWS.SSM();

// From Lambda Layers
const jwt = require("/opt/node_modules/jsonwebtoken");

const getParameter = (param) => {
    return new Promise((resolve, reject) => {
        ssm.getParameter(param, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
};

exports.handler = async (event) => {
    // Retrieve the encrypted parameter
    const publicKey = await getParameter({
        Name: "pubkey",
        WithDecryption: true,
    });
    // Verify JWT
    const body = jwt.verify(event.token, publicKey.Parameter.Value, (err, decoded) => {
        if (err) {
            return err;
        }
        return decoded;
    });
    const response = {
        statusCode: 200,
        body: JSON.stringify(body),
    };
    return response;
};

Lo que hace esa función es obtener la llave pública encriptada del Systems Manager, luego utilizando la librería jsonwebtoken verifica que el parámetro token (en este caso, se puede utilizar cualquier otro método para obtener el JWT) sea un JWT válido.

La misma librería (jsonwebtoken) puede ser utilizada para generar un JWT para probar que funcione la función creada, solo hay que pasar el token generado.

const fs = require("fs");
const jwt = require("jsonwebtoken");

const privateKey = fs.readFileSync("./jwtRS256.key");

jwt.sign({ hello: "world" }, privateKey, { algorithm: "RS256", expiresIn: "1h" }, (err, token) => {
    console.log(token);
});