Skip to content

Instantly share code, notes, and snippets.

@robingustafsson
Created October 14, 2017 20:16
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save robingustafsson/7dd6463d85efdddbb0e4bcd3ecc706e1 to your computer and use it in GitHub Desktop.
Save robingustafsson/7dd6463d85efdddbb0e4bcd3ecc706e1 to your computer and use it in GitHub Desktop.
Simple JWT example for k6 load testing tool
import crypto from "k6/crypto";
import encoding from "k6/encoding";
const algToHash = {
HS256: "sha256",
HS384: "sha384",
HS512: "sha512"
};
function sign(data, hashAlg, secret) {
let hasher = crypto.createHMAC(hashAlg, secret);
hasher.update(data);
// Some manual base64 rawurl encoding as `Hasher.digest(encodingType)`
// doesn't support that encoding type yet.
return hasher.digest("base64").replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
}
function encode(payload, secret, algorithm) {
algorithm = algorithm || "HS256";
let header = encoding.b64encode(JSON.stringify({ typ: "JWT", alg: algorithm }), "rawurl");
payload = encoding.b64encode(JSON.stringify(payload), "rawurl");
let sig = sign(header + "." + payload, algToHash[algorithm], secret);
return [header, payload, sig].join(".");
}
function decode(token, secret, algorithm) {
let parts = token.split('.');
let header = JSON.parse(encoding.b64decode(parts[0], "rawurl"));
let payload = JSON.parse(encoding.b64decode(parts[1], "rawurl"));
algorithm = algorithm || algToHash[header.alg];
if (sign(parts[0] + "." + parts[1], algorithm, secret) != parts[2]) {
throw Error("JWT signature verification failed");
}
return payload;
}
export default function() {
let message = { key2: "value2" };
let token = encode(message, "secret");
console.log("encoded", token);
let payload = decode(token, "secret");
console.log("decoded", JSON.stringify(payload));
}
@mbforbes
Copy link

mbforbes commented Feb 9, 2024

In case anyone's still looking how to do this 7 (!) years later, there seem to have been a few updates to the k6 JS libraries:

  • hasher.digest now supports "base64rawurl"
  • encoding.b64decode now requires a third parameter "s" to return the result as a string and not an ArrayBuffer

Here is the script with those updates, basic type annotations, encode() / decode(), and the test function removed:

import crypto from "k6/crypto";
import encoding from "k6/encoding";

const algToHash = {
  HS256: "sha256",
  HS384: "sha384",
  HS512: "sha512",
};

function sign(data, hashAlg, secret) {
  const hasher = crypto.createHMAC(hashAlg, secret);
  hasher.update(data);
  return hasher.digest("base64rawurl");
}

/**
 *
 * @param {Object} payload
 * @param {string} secret
 * @param {string|undefined} algorithm Default: "HS256"
 * @returns string
 */
export function encode(payload, secret, algorithm) {
  algorithm = algorithm || "HS256";
  const header = encoding.b64encode(
    JSON.stringify({ typ: "JWT", alg: algorithm }),
    "rawurl"
  );
  payload = encoding.b64encode(JSON.stringify(payload), "rawurl");
  const sig = sign(header + "." + payload, algToHash[algorithm], secret);
  return [header, payload, sig].join(".");
}

/**
 *
 * @param {string} token
 * @param {string} secret
 * @param {string|undefined} algorithm
 * @returns
 */
export function decode(token, secret, algorithm) {
  const parts = token.split(".");
  const header = JSON.parse(encoding.b64decode(parts[0], "rawurl", "s"));
  const payload = JSON.parse(encoding.b64decode(parts[1], "rawurl", "s"));
  algorithm = algorithm || algToHash[header.alg];
  if (sign(parts[0] + "." + parts[1], algorithm, secret) != parts[2]) {
    throw Error("JWT signature verification failed");
  }
  return payload;
}

I'm importing the functions like this from my k6 script:

import { encode, decode } from "./jwt.js";

// example code from the original gist:
let message = { key2: "value2" };
let token = encode(message, "secret");
console.log("encoded", token);
let payload = decode(token, "secret");
console.log("decoded", JSON.stringify(payload));

Also of note: k6's documentation points out they have an experimental webcrypto package they recommend using. So future iterations on this might want to check it out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment