2.6 KiB
none algorithm
Purpose
RFC 7518 §3.6 "none" algorithm. The none value declares "no integrity protection," which is virtually never what a verifier wants but is occasionally needed for non-security-critical contexts (signed-by-policy local development, deliberately-unsigned debugging payloads). This implementation exists only so the registry is complete; the verify pipeline rejects none by default and requires BOTH algorithms: ["none"] AND acceptNone: true to opt in, neutralising the classic "alg: none" downgrade attack.
Features
verifyreturnstrueif-and-only-if the signature segment is empty (zero bytes after base64url-decode).signalways emits empty signature bytes.- Default rejection: pipeline throws
JWS.ALG_NONE_DISABLEDunless both flags are set.
Usage: Basic Example
import { compactSign, compactVerify } from "@suaveplan/jws";
const token = await compactSign("hello", undefined, "none");
// token has the form "<header>.<payload>." — empty signature segment.
await compactVerify(token, undefined, {
algorithms: ["none"],
acceptNone: true,
});
Usage: Advanced Example
import { compactVerify, JwsError } from "@suaveplan/jws";
const noneToken = "eyJhbGciOiJub25lIn0.eyJzdWIiOjF9.";
// Default: rejected.
try {
await compactVerify(noneToken, undefined, { algorithms: ["HS256"] });
} catch (err) {
if (err instanceof JwsError && err.code === "JWS.ALG_NONE_DISABLED") {
// CVE-class defence in depth.
}
}
// Allowlisted but no acceptNone — also rejected.
try {
await compactVerify(noneToken, undefined, { algorithms: ["none"] });
} catch (err) {
if (err instanceof JwsError && err.code === "JWS.ALG_NONE_DISABLED") {
// Two flags required.
}
}
API Reference
none: AlgorithmImpl<undefined>— verify(input, sig)returnsPromise<boolean>(trueiffsig.length === 0); sign(input, _)returnsPromise<Uint8Array>of length 0.
Implementation Notes
The two-flag opt-in design is a deliberate defence against the historic JWT-CVE class where a caller passes algorithms: ["HS256"] AND a public key, then an attacker mints a token with alg: "none" and the library accepts it because the allowlist is parsed permissively. Here, even a deliberate algorithms: ["none"] requires the second flag acceptNone: true — the second flag is intentionally separate so a single typo or merged configuration cannot accidentally accept unsigned tokens. The implementation also requires the signature segment to be empty, not "not present" — a token like "<hdr>.<payload>.deadbeef" with alg: "none" is rejected because the signature bytes are non-zero.