1
0

Compare commits

...

4 Commits

Author SHA1 Message Date
06c5605bf2 3.1.7 2025-05-26 01:04:33 +02:00
a4edaba6f0 clean up tests 2025-05-26 01:02:50 +02:00
b2a3b4c25f 3.1.6 2025-05-26 00:58:30 +02:00
c691324515 update jwk type and dev deps 2025-05-26 00:57:04 +02:00
6 changed files with 350 additions and 286 deletions

590
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "@tsndr/cloudflare-worker-jwt", "name": "@tsndr/cloudflare-worker-jwt",
"version": "3.1.5", "version": "3.1.7",
"description": "A lightweight JWT implementation with ZERO dependencies for Cloudflare Worker", "description": "A lightweight JWT implementation with ZERO dependencies for Cloudflare Worker",
"type": "module", "type": "module",
"exports": "./index.js", "exports": "./index.js",
@@ -30,10 +30,10 @@
}, },
"homepage": "https://github.com/tsndr/cloudflare-worker-jwt#readme", "homepage": "https://github.com/tsndr/cloudflare-worker-jwt#readme",
"devDependencies": { "devDependencies": {
"@cloudflare/workers-types": "^4.20250313.0", "@cloudflare/workers-types": "^4.20250525.0",
"@edge-runtime/vm": "^5.0.0", "@edge-runtime/vm": "^5.0.0",
"esbuild": "^0.25.1", "esbuild": "^0.25.4",
"typescript": "^5.8.2", "typescript": "^5.8.3",
"vitest": "^3.0.8" "vitest": "^3.1.4"
} }
} }

View File

@@ -143,7 +143,7 @@ const algorithms: JwtAlgorithms = {
* @throws If there"s a validation issue. * @throws If there"s a validation issue.
* @returns Returns token as a `string`. * @returns Returns token as a `string`.
*/ */
export async function sign<Payload = {}, Header = {}>(payload: JwtPayload<Payload>, secret: string | JsonWebKey | CryptoKey, options: JwtSignOptions<Header> | JwtAlgorithm = "HS256"): Promise<string> { export async function sign<Payload = {}, Header = {}>(payload: JwtPayload<Payload>, secret: string | JsonWebKeyWithKid | CryptoKey, options: JwtSignOptions<Header> | JwtAlgorithm = "HS256"): Promise<string> {
if (typeof options === "string") if (typeof options === "string")
options = { algorithm: options } options = { algorithm: options }
@@ -183,7 +183,7 @@ export async function sign<Payload = {}, Header = {}>(payload: JwtPayload<Payloa
* @throws Throws integration errors and if `options.throwError` is set to `true` also throws `NOT_YET_VALID`, `EXPIRED` or `INVALID_SIGNATURE`. * @throws Throws integration errors and if `options.throwError` is set to `true` also throws `NOT_YET_VALID`, `EXPIRED` or `INVALID_SIGNATURE`.
* @returns Returns the decoded token or `undefined`. * @returns Returns the decoded token or `undefined`.
*/ */
export async function verify<Payload = {}, Header = {}>(token: string, secret: string | JsonWebKey | CryptoKey, options: JwtVerifyOptions | JwtAlgorithm = "HS256"): Promise<JwtData<Payload, Header> | undefined> { export async function verify<Payload = {}, Header = {}>(token: string, secret: string | JsonWebKeyWithKid | CryptoKey, options: JwtVerifyOptions | JwtAlgorithm = "HS256"): Promise<JwtData<Payload, Header> | undefined> {
if (typeof options === "string") if (typeof options === "string")
options = { algorithm: options } options = { algorithm: options }
options = { algorithm: "HS256", clockTolerance: 0, throwError: false, ...options } options = { algorithm: "HS256", clockTolerance: 0, throwError: false, ...options }

View File

@@ -56,7 +56,7 @@ export async function importTextSecret(key: string, algorithm: SubtleCryptoImpor
return await crypto.subtle.importKey("raw", textToUint8Array(key), algorithm, true, keyUsages) return await crypto.subtle.importKey("raw", textToUint8Array(key), algorithm, true, keyUsages)
} }
export async function importJwk(key: JsonWebKey, algorithm: SubtleCryptoImportKeyAlgorithm, keyUsages: KeyUsages[]): Promise<CryptoKey> { export async function importJwk(key: JsonWebKeyWithKid, algorithm: SubtleCryptoImportKeyAlgorithm, keyUsages: KeyUsages[]): Promise<CryptoKey> {
return await crypto.subtle.importKey("jwk", key, algorithm, true, keyUsages) return await crypto.subtle.importKey("jwk", key, algorithm, true, keyUsages)
} }
@@ -68,7 +68,7 @@ export async function importPrivateKey(key: string, algorithm: SubtleCryptoImpor
return await crypto.subtle.importKey("pkcs8", pemToBinary(key), algorithm, true, keyUsages) return await crypto.subtle.importKey("pkcs8", pemToBinary(key), algorithm, true, keyUsages)
} }
export async function importKey(key: string | JsonWebKey, algorithm: SubtleCryptoImportKeyAlgorithm, keyUsages: KeyUsages[]): Promise<CryptoKey> { export async function importKey(key: string | JsonWebKeyWithKid, algorithm: SubtleCryptoImportKeyAlgorithm, keyUsages: KeyUsages[]): Promise<CryptoKey> {
if (typeof key === "object") if (typeof key === "object")
return importJwk(key, algorithm, keyUsages) return importJwk(key, algorithm, keyUsages)

View File

@@ -13,27 +13,27 @@ describe("Verify", async () => {
const notYetValidToken = await jwt.sign({ sub: "me", nbf: now + offset }, secret) const notYetValidToken = await jwt.sign({ sub: "me", nbf: now + offset }, secret)
const expiredToken = await jwt.sign({ sub: "me", exp: now - offset }, secret) const expiredToken = await jwt.sign({ sub: "me", exp: now - offset }, secret)
test("Valid", () => { test("Valid", async () => {
expect(jwt.verify(validToken, secret, { throwError: true })).resolves.toBeTruthy() await expect(jwt.verify(validToken, secret, { throwError: true })).resolves.toBeTruthy()
}) })
test("Not yet expired", () => { test("Not yet expired", async () => {
expect(jwt.verify(notYetExpired, secret, { throwError: true })).resolves.toBeTruthy() await expect(jwt.verify(notYetExpired, secret, { throwError: true })).resolves.toBeTruthy()
}) })
test("Not yet valid", () => { test("Not yet valid", async () => {
expect(jwt.verify(notYetValidToken, secret, { throwError: true })).rejects.toThrowError("NOT_YET_VALID") await expect(jwt.verify(notYetValidToken, secret, { throwError: true })).rejects.toThrowError("NOT_YET_VALID")
}) })
test("Expired", () => { test("Expired", async () => {
expect(jwt.verify(expiredToken, secret, { throwError: true })).rejects.toThrowError("EXPIRED") await expect(jwt.verify(expiredToken, secret, { throwError: true })).rejects.toThrowError("EXPIRED")
}) })
test("Clock offset", () => { test("Clock offset", async () => {
expect(jwt.verify(notYetValidToken, secret, { clockTolerance: offset, throwError: true })).resolves.toBeTruthy() await expect(jwt.verify(notYetValidToken, secret, { clockTolerance: offset, throwError: true })).resolves.toBeTruthy()
expect(jwt.verify(expiredToken, secret, { clockTolerance: offset, throwError: true })).resolves.toBeTruthy() await expect(jwt.verify(expiredToken, secret, { clockTolerance: offset, throwError: true })).resolves.toBeTruthy()
expect(jwt.verify(notYetValidToken, secret, { clockTolerance: offset - 1, throwError: true })).rejects.toThrowError("NOT_YET_VALID") await expect(jwt.verify(notYetValidToken, secret, { clockTolerance: offset - 1, throwError: true })).rejects.toThrowError("NOT_YET_VALID")
expect(jwt.verify(expiredToken, secret, { clockTolerance: offset - 1, throwError: true })).rejects.toThrowError("EXPIRED") await expect(jwt.verify(expiredToken, secret, { clockTolerance: offset - 1, throwError: true })).rejects.toThrowError("EXPIRED")
}) })
}) })

View File

@@ -67,7 +67,7 @@ describe("Imports", () => {
const testAlgorithm = { name: "HMAC", hash: { name: "SHA-256" } } const testAlgorithm = { name: "HMAC", hash: { name: "SHA-256" } }
const testCryptoKey = { type: "secret", extractable: true, algorithm: { ...testAlgorithm, length: 168 }, usages: ["verify", "sign"] } const testCryptoKey = { type: "secret", extractable: true, algorithm: { ...testAlgorithm, length: 168 }, usages: ["verify", "sign"] }
expect(await importTextSecret(testKey, testAlgorithm, ["verify", "sign"])).toMatchObject(testCryptoKey) await expect(importTextSecret(testKey, testAlgorithm, ["verify", "sign"])).resolves.toMatchObject(testCryptoKey)
}) })
test.todo("importJwk") test.todo("importJwk")