Skip to content

fix(codegen): #927 — jwt.verify returns parsed object, not JSON-stringified string - #936

Merged
proggeramlug merged 1 commit into
mainfrom
fix/issue-927-jwt-verify-returns-string
May 17, 2026
Merged

fix(codegen): #927 — jwt.verify returns parsed object, not JSON-stringified string#936
proggeramlug merged 1 commit into
mainfrom
fix/issue-927-jwt-verify-returns-string

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • cargo fmt --all clean
  • cargo build --release -p perry-runtime -p perry-stdlib -p perry succeeds
  • ./target/release/perry test-files/test_issue_927_jwt_verify_returns_object.ts -o /tmp/test_927 && /tmp/test_927 prints decoded typeof: object, decoded.sub: u-001, decoded.acc: a-001, issue 927 jwt.verify: ok
  • Existing Native server SIGSEGV at jwt.sign(...) (jsonwebtoken) after resumed async-step body — follow-up to #859 #915 regression test_issue_915_jwt_sign.ts still passes
  • Bad-secret path (verify with wrong secret) returns null without throwing/aborting
  • jwt.decode(token) also returns object (mirror change)

…gified string

The `jsonwebtoken.verify` (and `decode`) entries in NATIVE_MODULE_TABLE
used `NR_STR`, which NaN-boxed the runtime's JSON-text `*mut StringHeader`
return as a string. User code per the jsonwebtoken README expects an
object — `decoded.sub` read `undefined`, auth middleware broke on every
authenticated request after a successful signup.

Add `NativeRetKind::ObjFromJsonStr` (alias `NR_OBJ_FROM_JSON_STR`) that
automatically pipes the runtime's JSON-text result through a JSON-parse
on the way out — symmetric counterpart of #915's `NA_JSON` on the
argument side.

Failure path (bad signature → null `*mut StringHeader`) routes through a
new `js_json_parse_or_null` shim in `crates/perry-runtime/src/json.rs`
so user code sees `null` instead of an uncaught
"Unexpected end of JSON input" exception that aborts the process.

Regression fixture exercises round-trip HS256 sign+verify and asserts
`typeof decoded === "object"` plus `decoded.sub` indexability.
@proggeramlug
proggeramlug merged commit 68b989b into main May 17, 2026
7 of 9 checks passed
@proggeramlug
proggeramlug deleted the fix/issue-927-jwt-verify-returns-string branch May 17, 2026 16:30
proggeramlug added a commit that referenced this pull request May 18, 2026
…s private PEM (#1025)

The previous #927 fix (#936) made `jwt.verify` return the parsed
claims object instead of the JSON text, but ES256 / RS256 tokens
were still failing verification silently — the generic NativeModSig
table routed every algorithm through `js_jwt_verify`, which was
hardcoded to HS256. The token was signed correctly with ES256, then
`Validation::new(Algorithm::HS256)` rejected it with
`InvalidAlgorithm`, the runtime returned a null pointer, and
`js_json_parse_or_null` surfaced it as `null` — breaking the
shop-admin auth middleware on the very first authenticated request
after a successful signup.

Fix mirrors the `sign` side's algorithm-aware dispatch:

- Add `js_jwt_verify_es256` / `js_jwt_verify_rs256` runtime
  functions in `perry-stdlib`. Both auto-derive the public key from
  a private PEM (PKCS#8 *or* SEC1 for EC, PKCS#8 *or* PKCS#1 for
  RSA) so callers can reuse the same PEM they passed to `sign` —
  matches Node `jsonwebtoken`'s ergonomics. Direct public-key PEMs
  (SPKI) also accepted unchanged.

- Replace the generic NativeModSig `verify` row with
  `lower_jsonwebtoken_verify` in `lower_call/native.rs`. Routes on
  the canonical `algorithms: ['…']` array option as well as the
  singular `algorithm: '…'`. Falls back to HS256 when the option is
  absent or unparseable. Return path still pipes through
  `js_json_parse_or_null` so user code sees an object on success
  and `null` on failure (same contract as before).

- Bonus: `js_jwt_sign_es256` now accepts SEC1 (`BEGIN EC PRIVATE
  KEY`) PEMs in addition to PKCS#8, which is what `openssl ecparam
  -genkey` emits by default. Symmetric with the new verify
  ergonomics.

New `perry-stdlib` deps gated on `bundled-jsonwebtoken`: `p256`
(EC public-key derivation), `rsa` (RSA public-key derivation),
`spki` (PEM serialization).

Verified with a 9-line standalone repro (sign + verify a token
with `algorithms: ['ES256']`) — previously returned `null`, now
returns the parsed claims object with `decoded.sub` accessible.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jwt.verify returns JSON-stringified payload (string) instead of parsed object — auth middleware breaks

1 participant