Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@tsndr/cloudflare-worker-jwt

Package Overview
Dependencies
Maintainers
1
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tsndr/cloudflare-worker-jwt - npm Package Compare versions

Comparing version 2.4.5 to 2.4.6

247

index.js

@@ -1,102 +0,155 @@

import { textToArrayBuffer, arrayBufferToBase64Url, base64UrlToArrayBuffer, textToBase64Url, importKey, decodePayload } from "./utils";
if (typeof crypto === 'undefined' || !crypto.subtle)
throw new Error('SubtleCrypto not supported!');
const algorithms = {
ES256: { name: 'ECDSA', namedCurve: 'P-256', hash: { name: 'SHA-256' } },
ES384: { name: 'ECDSA', namedCurve: 'P-384', hash: { name: 'SHA-384' } },
ES512: { name: 'ECDSA', namedCurve: 'P-521', hash: { name: 'SHA-512' } },
HS256: { name: 'HMAC', hash: { name: 'SHA-256' } },
HS384: { name: 'HMAC', hash: { name: 'SHA-384' } },
HS512: { name: 'HMAC', hash: { name: 'SHA-512' } },
RS256: { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-256' } },
RS384: { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-384' } },
RS512: { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-512' } }
// src/utils.ts
function bytesToByteString(bytes) {
let byteStr = "";
for (let i = 0; i < bytes.byteLength; i++) {
byteStr += String.fromCharCode(bytes[i]);
}
return byteStr;
}
function byteStringToBytes(byteStr) {
let bytes = new Uint8Array(byteStr.length);
for (let i = 0; i < byteStr.length; i++) {
bytes[i] = byteStr.charCodeAt(i);
}
return bytes;
}
function arrayBufferToBase64String(arrayBuffer) {
return btoa(bytesToByteString(new Uint8Array(arrayBuffer)));
}
function base64StringToArrayBuffer(b64str) {
return byteStringToBytes(atob(b64str)).buffer;
}
function textToArrayBuffer(str) {
return byteStringToBytes(decodeURI(encodeURIComponent(str)));
}
function arrayBufferToBase64Url(arrayBuffer) {
return arrayBufferToBase64String(arrayBuffer).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}
function base64UrlToArrayBuffer(b64url) {
return base64StringToArrayBuffer(b64url.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""));
}
function textToBase64Url(str) {
const encoder = new TextEncoder();
const charCodes = encoder.encode(str);
const binaryStr = String.fromCharCode(...charCodes);
return btoa(binaryStr).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}
function pemToBinary(pem) {
return base64StringToArrayBuffer(pem.replace(/-+(BEGIN|END).*/g, "").replace(/\s/g, ""));
}
async function importTextSecret(key, algorithm, keyUsages) {
return await crypto.subtle.importKey("raw", textToArrayBuffer(key), algorithm, true, keyUsages);
}
async function importJwk(key, algorithm, keyUsages) {
return await crypto.subtle.importKey("jwk", key, algorithm, true, keyUsages);
}
async function importPublicKey(key, algorithm, keyUsages) {
return await crypto.subtle.importKey("spki", pemToBinary(key), algorithm, true, keyUsages);
}
async function importPrivateKey(key, algorithm, keyUsages) {
return await crypto.subtle.importKey("pkcs8", pemToBinary(key), algorithm, true, keyUsages);
}
async function importKey(key, algorithm, keyUsages) {
if (typeof key === "object")
return importJwk(key, algorithm, keyUsages);
if (typeof key !== "string")
throw new Error("Unsupported key type!");
if (key.includes("PUBLIC"))
return importPublicKey(key, algorithm, keyUsages);
if (key.includes("PRIVATE"))
return importPrivateKey(key, algorithm, keyUsages);
return importTextSecret(key, algorithm, keyUsages);
}
function decodePayload(raw) {
try {
const bytes = Array.from(atob(raw), (char) => char.charCodeAt(0));
const decodedString = new TextDecoder("utf-8").decode(new Uint8Array(bytes));
return JSON.parse(decodedString);
} catch {
return;
}
}
// src/index.ts
if (typeof crypto === "undefined" || !crypto.subtle)
throw new Error("SubtleCrypto not supported!");
var algorithms = {
ES256: { name: "ECDSA", namedCurve: "P-256", hash: { name: "SHA-256" } },
ES384: { name: "ECDSA", namedCurve: "P-384", hash: { name: "SHA-384" } },
ES512: { name: "ECDSA", namedCurve: "P-521", hash: { name: "SHA-512" } },
HS256: { name: "HMAC", hash: { name: "SHA-256" } },
HS384: { name: "HMAC", hash: { name: "SHA-384" } },
HS512: { name: "HMAC", hash: { name: "SHA-512" } },
RS256: { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } },
RS384: { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-384" } },
RS512: { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-512" } }
};
/**
* Signs a payload and returns the token
*
* @param {JwtPayload} payload The payload object. To use `nbf` (Not Before) and/or `exp` (Expiration Time) add `nbf` and/or `exp` to the payload.
* @param {string | JsonWebKey | CryptoKey} secret A string which is used to sign the payload.
* @param {JwtSignOptions | JwtAlgorithm | string} [options={ algorithm: 'HS256', header: { typ: 'JWT' } }] The options object or the algorithm.
* @throws {Error} If there's a validation issue.
* @returns {Promise<string>} Returns token as a `string`.
*/
export async function sign(payload, secret, options = 'HS256') {
if (typeof options === 'string')
options = { algorithm: options };
options = { algorithm: 'HS256', header: { typ: 'JWT' }, ...options };
if (!payload || typeof payload !== 'object')
throw new Error('payload must be an object');
if (!secret || (typeof secret !== 'string' && typeof secret !== 'object'))
throw new Error('secret must be a string, a JWK object or a CryptoKey object');
if (typeof options.algorithm !== 'string')
throw new Error('options.algorithm must be a string');
const algorithm = algorithms[options.algorithm];
if (!algorithm)
throw new Error('algorithm not found');
if (!payload.iat)
payload.iat = Math.floor(Date.now() / 1000);
const partialToken = `${textToBase64Url(JSON.stringify({ ...options.header, alg: options.algorithm }))}.${textToBase64Url(JSON.stringify(payload))}`;
const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm, ['sign']);
const signature = await crypto.subtle.sign(algorithm, key, textToArrayBuffer(partialToken));
return `${partialToken}.${arrayBufferToBase64Url(signature)}`;
async function sign(payload, secret, options = "HS256") {
if (typeof options === "string")
options = { algorithm: options };
options = { algorithm: "HS256", header: { typ: "JWT" }, ...options };
if (!payload || typeof payload !== "object")
throw new Error("payload must be an object");
if (!secret || typeof secret !== "string" && typeof secret !== "object")
throw new Error("secret must be a string, a JWK object or a CryptoKey object");
if (typeof options.algorithm !== "string")
throw new Error("options.algorithm must be a string");
const algorithm = algorithms[options.algorithm];
if (!algorithm)
throw new Error("algorithm not found");
if (!payload.iat)
payload.iat = Math.floor(Date.now() / 1e3);
const partialToken = `${textToBase64Url(JSON.stringify({ ...options.header, alg: options.algorithm }))}.${textToBase64Url(JSON.stringify(payload))}`;
const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm, ["sign"]);
const signature = await crypto.subtle.sign(algorithm, key, textToArrayBuffer(partialToken));
return `${partialToken}.${arrayBufferToBase64Url(signature)}`;
}
/**
* Verifies the integrity of the token and returns a boolean value.
*
* @param {string} token The token string generated by `jwt.sign()`.
* @param {string | JsonWebKey | CryptoKey} secret The string which was used to sign the payload.
* @param {JWTVerifyOptions | JWTAlgorithm} options The options object or the algorithm.
* @throws {Error | string} Throws an error `string` if the token is invalid or an `Error-Object` if there's a validation issue.
* @returns {Promise<boolean>} Returns `true` if signature, `nbf` (if set) and `exp` (if set) are valid, otherwise returns `false`.
*/
export async function verify(token, secret, options = { algorithm: 'HS256', throwError: false }) {
if (typeof options === 'string')
options = { algorithm: options, throwError: false };
options = { algorithm: 'HS256', throwError: false, ...options };
if (typeof token !== 'string')
throw new Error('token must be a string');
if (typeof secret !== 'string' && typeof secret !== 'object')
throw new Error('secret must be a string, a JWK object or a CryptoKey object');
if (typeof options.algorithm !== 'string')
throw new Error('options.algorithm must be a string');
const tokenParts = token.split('.');
if (tokenParts.length !== 3)
throw new Error('token must consist of 3 parts');
const algorithm = algorithms[options.algorithm];
if (!algorithm)
throw new Error('algorithm not found');
const { payload } = decode(token);
try {
if (!payload)
throw new Error('PARSE_ERROR');
if (payload.nbf && payload.nbf > Math.floor(Date.now() / 1000))
throw new Error('NOT_YET_VALID');
if (payload.exp && payload.exp <= Math.floor(Date.now() / 1000))
throw new Error('EXPIRED');
const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm, ['verify']);
return await crypto.subtle.verify(algorithm, key, base64UrlToArrayBuffer(tokenParts[2]), textToArrayBuffer(`${tokenParts[0]}.${tokenParts[1]}`));
}
catch (err) {
if (options.throwError)
throw err;
return false;
}
async function verify(token, secret, options = { algorithm: "HS256", throwError: false }) {
if (typeof options === "string")
options = { algorithm: options, throwError: false };
options = { algorithm: "HS256", throwError: false, ...options };
if (typeof token !== "string")
throw new Error("token must be a string");
if (typeof secret !== "string" && typeof secret !== "object")
throw new Error("secret must be a string, a JWK object or a CryptoKey object");
if (typeof options.algorithm !== "string")
throw new Error("options.algorithm must be a string");
const tokenParts = token.split(".");
if (tokenParts.length !== 3)
throw new Error("token must consist of 3 parts");
const algorithm = algorithms[options.algorithm];
if (!algorithm)
throw new Error("algorithm not found");
const { payload } = decode(token);
try {
if (!payload)
throw new Error("PARSE_ERROR");
if (payload.nbf && payload.nbf > Math.floor(Date.now() / 1e3))
throw new Error("NOT_YET_VALID");
if (payload.exp && payload.exp <= Math.floor(Date.now() / 1e3))
throw new Error("EXPIRED");
const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm, ["verify"]);
return await crypto.subtle.verify(algorithm, key, base64UrlToArrayBuffer(tokenParts[2]), textToArrayBuffer(`${tokenParts[0]}.${tokenParts[1]}`));
} catch (err) {
if (options.throwError)
throw err;
return false;
}
}
/**
* Returns the payload **without** verifying the integrity of the token. Please use `jwt.verify()` first to keep your application secure!
*
* @param {string} token The token string generated by `jwt.sign()`.
* @returns {JwtData} Returns an `object` containing `header` and `payload`.
*/
export function decode(token) {
return {
header: decodePayload(token.split('.')[0].replace(/-/g, '+').replace(/_/g, '/')),
payload: decodePayload(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'))
};
function decode(token) {
return {
header: decodePayload(token.split(".")[0].replace(/-/g, "+").replace(/_/g, "/")),
payload: decodePayload(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))
};
}
export default {
sign,
verify,
decode
var src_default = {
sign,
verify,
decode
};
export {
decode,
src_default as default,
sign,
verify
};
{
"name": "@tsndr/cloudflare-worker-jwt",
"version": "2.4.5",
"version": "2.4.6",
"description": "A lightweight JWT implementation with ZERO dependencies for Cloudflare Worker",

@@ -12,4 +12,4 @@ "type": "module",

"scripts": {
"build": "tsc",
"test": "jest"
"build": "tsc & esbuild --bundle --target=esnext --platform=neutral --outfile=index.js src/index.ts & wait",
"test": "vitest"
},

@@ -34,11 +34,9 @@ "repository": {

"devDependencies": {
"@cloudflare/workers-types": "^4.20231025.0",
"@jest/globals": "^29.7.0",
"@types/jest": "^29.5.8",
"@types/node": "^20.9.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"@cloudflare/workers-types": "^4.20240208.0",
"@edge-runtime/vm": "^3.2.0",
"@types/node": "^20.11.19",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vitest": "^1.3.1"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc