panva / jose

JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes
MIT License
5.62k stars 315 forks source link

Signature verification works in Node.js but fails in Cloudflare Workers #641

Closed typeofweb closed 8 months ago

typeofweb commented 8 months ago

What happened?

Running the code (below) in Node.js results in a successful validation but a failure in Cloudflare Workers.

Here's a full reproduction: https://github.com/mmiszy/jose-bug-reproduction

Version

5.2.2

Runtime

Other (I will specify below)

Runtime Details

Node.js 20.11.1 and Wrangler 3.28.3

Code to reproduce

import { createLocalJWKSet, flattenedVerify } from "jose";

const signature = "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImlzcyI6Imh0dHBzOi8vbWljaGFsLXN0b3JlLTk3MDIuZXUuc2FsZW9yLmNsb3VkL2dyYXBocWwvIiwia2lkIjoiMm96ZFh6aG5pb1ViUTRxOU9DT3QtTHZmTnFrU2J5QTFuRC10Q1VtY1p2SSIsInR5cCI6IkpXVCJ9..VEhCigg31dl-IQ_xb8-trpPQcXxKpirz5hAo58_0F2IDISnBTMbwB0xV7lLVKwxEry0_sdnjpsLOKU0ri1Ak5LwDrWbh41IBBOyvO-TBmPjKFIdPI3yMeyxe2zM9RL_MMUhz32b4KbZf_ahDWBw4hkBk3xHtmuLFDPOYrL4YuL_OPyCjE2lnr1e4zoZALOv2yxVSDP-WvqVY4rXop1l0ydz1WM9V5JOPczG0FXulgR6FLCoi9ZNS79jXeG_CZn1QqakeT8ks_04-8A_Zz1SaIOyqGbrnQG3GQWvYMGMxZQDcCCQ8Rj_Db-MP7gpyq1KRJV4sq9nHz7_q1GfFxRD4zA";
const text = '{"__typename": "ProductUpdated", "product": {"id": "UHJvZHVjdDoxNTI="}}';

const jwks = {
    keys: [
        {
            kty: "RSA",
            key_ops: ["verify"],
            n: "mv4XerD8Kg1-LHgJmQGsrGkmmA4uWFdVRNSpmiXy2blGWZe5PYQWJU45XQTLgtSZs5vJAg4INWPKXc0Lcl7gXB9EmezZbOYjFKsjZwz1ddlVodr9fUPH8uNNG65nMVhLGvM0PhetAzPFM41R-QYUnnXRUfouThPUYciz7NR0n85_dDwA8J3x7hJgZvp9SGgI2hYXy71hlitXy2hq9EfxmSIR0BPNRt6jQzZA_FKV7Mvrwf7E_21gJ3mvUPDbkaJHikbPa4E_rgUiilK0k70G0XI-9Ir3L1kpOzF_utCRpHlU_K1L1dqOryulertOHLAo432gwjFwSyph5dcMLtUhvw",
            e: "AQAB",
            use: "sig",
            kid: "2ozdXzhnioUbQ4q9OCOt-LvfNqkSbyA1nD-tCUmcZvI",
        },
    ],
};

export const run = async () => {
    const [header, , jwsSignature] = signature.split(".");
    const jws = {
        protected: header,
        payload: text,
        signature: jwsSignature,
    };
    const localJwks = createLocalJWKSet(jwks);
    await flattenedVerify(jws, localJwks);
};

Required

panva commented 8 months ago

That's unfortunately how the different means to base64url decode work. In node the parser is very relaxed, while in the Web based APIs implementation it's more strict.

Anyway, your use of https://www.rfc-editor.org/rfc/rfc7797 is incorrect, if you want to process unencoded payloads then your JOSE Header MUST also include "crit": ["b64"] in order to recognize that extension and in such case the library also accepts your "payload" to be an instance of Uint8Array.