diff --git a/LICENSE b/LICENSE index b23e82a..49d9831 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 cross-org +Copyright (c) 2024-2026 cross-org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index b2370f1..f99e67f 100644 --- a/README.md +++ b/README.md @@ -120,12 +120,10 @@ const { privateKey, publicKey } = await generateKeyPair("RS512"); const key = await generateKeyPair({ algorithm: "RS512" }); ``` -- **`exportPEMKey(key: CryptoKey, filePathOrOptions?: string | ExportPEMKeyOptions): Promise`** (Experimental) -- **`importPEMKey(keyDataOrPath: string, algorithm: SupportedKeyPairAlgorithms): Promise`** (Experimental) +- **`exportPEMKey(key: CryptoKey, filePathOrOptions?: string | ExportPEMKeyOptions): Promise`** +- **`importPEMKey(keyDataOrPath: string, algorithm: SupportedKeyPairAlgorithms): Promise`** ```javascript -// Experimental. - // Generate and export RS512 keys in PEM-format. (filePath and write mode can be supplied as optional second parameter at export) const { privateKey, publicKey } = await generateKeyPair("RS512"); await exportPEMKey(privateKey, "./private_key_RS512.pem"); @@ -431,13 +429,13 @@ const insecureString = "shortString"; const key = await generateKey(insecureString, keyOptions); ``` -Export/import a key pair to and from local files. (Experimental) +Export/import a key pair to and from local files. ```javascript // Generate and export RS512 keys in PEM-format. const { privateKey, publicKey } = await generateKeyPair("RS512"); -await exportPEMKey(privateKey, "./private_key_RS512a.pem"); -await exportPEMKey(publicKey, "./public_key_RS512a.pem"); +await exportPEMKey(privateKey, "./private_key_RS512.pem"); +await exportPEMKey(publicKey, "./public_key_RS512.pem"); // Import RS512 keys from PEM-format. const importedPrivateKey = await importPEMKey("./private_key_RS512.pem", "RS512"); diff --git a/deno.jsonc b/deno.jsonc index 11d458b..4226bd3 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,6 +1,6 @@ { "name": "@cross/jwt", - "version": "0.6.0", + "version": "1.0.0", "exports": "./mod.ts", "tasks": { diff --git a/src/core/validate.ts b/src/core/validate.ts index 902dcff..df9832d 100644 --- a/src/core/validate.ts +++ b/src/core/validate.ts @@ -92,7 +92,12 @@ export async function validateJWT( } } - const payload = JSON.parse(textDecode(decodeBase64Url(jwtParts[1]))); + let payload: JWTPayload; + try { + payload = JSON.parse(textDecode(decodeBase64Url(jwtParts[1]))); + } catch (_error) { + throw new JWTParseError("Invalid JWT payload: malformed JSON"); + } validateClaims(payload, options); @@ -251,12 +256,8 @@ export function unsafeParseJWT(jwt: string): JWTPayload { const jwtParts = validateParts(jwt); const payload = JSON.parse(textDecode(decodeBase64Url(jwtParts[1]))); return payload; - } catch (error) { - if (error instanceof Error) { - throw new JWTParseError(error.message); - } else { - throw new JWTParseError("An unknown error occurred while parsing the JWT."); - } + } catch (_error) { + throw new JWTParseError("Invalid JWT format or malformed payload"); } } @@ -272,11 +273,7 @@ export function unsafeParseJOSEHeader(jwt: string): JOSEHeader { const jwtParts = validateParts(jwt); const payload = JSON.parse(textDecode(decodeBase64Url(jwtParts[0]))); return payload; - } catch (error) { - if (error instanceof Error) { - throw new JWTParseError(error.message); - } else { - throw new JWTParseError("An unknown error occurred while parsing the JOSE header."); - } + } catch (_error) { + throw new JWTParseError("Invalid JWT format or malformed header"); } } diff --git a/src/crypto/sign-verify/rsapss.ts b/src/crypto/sign-verify/rsapss.ts index 29ae0da..ceec22c 100644 --- a/src/crypto/sign-verify/rsapss.ts +++ b/src/crypto/sign-verify/rsapss.ts @@ -12,9 +12,12 @@ import type { JWTOptions } from "../../types/options.ts"; * @returns {Promise} A promise resolving to the base64url-encoded RSA-PSS signature. */ export async function signWithRSAPSS(key: CryptoKey, data: string, options?: JWTOptions) { - (key.algorithm as RsaPssParams).saltLength = options?.saltLength || 32; + const algorithm = { + name: key.algorithm.name, + saltLength: options?.saltLength || 32, + }; const signature = await crypto.subtle.sign( - key.algorithm, + algorithm, key, textEncode(data) as BufferSource, ); @@ -32,9 +35,12 @@ export async function signWithRSAPSS(key: CryptoKey, data: string, options?: JWT * @returns {Promise} A promise resolving to `true` if the signature is valid, `false` otherwise. */ export async function verifyWithRSAPSS(key: CryptoKey, data: string, signature: string, options?: JWTOptions) { - (key.algorithm as RsaPssParams).saltLength = options?.saltLength || 32; + const algorithm = { + name: key.algorithm.name, + saltLength: options?.saltLength || 32, + }; const isValid = await crypto.subtle.verify( - key.algorithm, + algorithm, key, decodeBase64Url(signature), textEncode(data) as BufferSource,