@shlomiatar/nano-jwt
Version:
A tiny, minimalistic HS256/HS512 jwt verifier using WebCryptoAPIs (for browser/bun/cloudflare)
34 lines (33 loc) • 1.49 kB
JavaScript
// src/index.ts
var [dec, enc] = [new TextDecoder, new TextEncoder];
var b64uToU8 = (s) => new Uint8Array(atob(s.replace(/-/g, "+").replace(/_/g, "/")).split("").map((c) => c.charCodeAt(0)));
var b64uToJson = (s) => JSON.parse(dec.decode(b64uToU8(s)));
var jwt = (secret, alg = "HS256", matcher) => {
let _key;
const hash = `SHA-${alg.substring(2)}`;
const matcherFn = matcher ? typeof matcher === "function" ? matcher : (p) => Object.keys(matcher ?? {}).every((k) => p[k] === matcher[k]) : () => true;
return {
verify: async (token) => {
try {
_key = _key ?? await crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash }, false, ["verify"]);
const [enc_alg, enc_payload, enc_sign, ...rest] = token.split(".");
if (!enc_alg || !enc_payload || !enc_sign || rest.length)
return [false, { error: "invalid_format" }];
if (b64uToJson(enc_alg).alg != alg)
return [false, { error: "unsupported_algorithm" }];
if (!await crypto.subtle.verify("HMAC", _key, b64uToU8(enc_sign), enc.encode(`${enc_alg}.${enc_payload}`)))
return [false, { error: "bad_signature" }];
const payload = b64uToJson(enc_payload);
const result = matcherFn(payload);
if (result !== true)
return [false, { error: result || "invalid_payload" }];
return [true, payload];
} catch {
return [false, { error: "verification_failed" }];
}
}
};
};
export {
jwt
};