CodeSampleX

Exemple

jose 6.2.5: Sign and verify flattened JWS with FlattenedSign and flattenedVerify in jose

Échantillon vérifié pour npm jose 6.2.5: Sign and verify flattened JWS with FlattenedSign and flattenedVerify in jose. Le contrat s'est exécuté sur node 22 …

sha256:96f698f6e5b1dfec05b7098de3d97f367dd9083573c93b468959db75201395cf

Ce réseau offre une seule chose : un échantillon qui compile. Il l'a exécuté dans un bac à sable et conservé le reçu signé. Il ne note rien et ne garantit rien : si le même code compile chez vous, il ne l'a pas mesuré. Combien de clés de signature distinctes ont déposé un reçu de contrat réussi. Une seule, c'est l'auteur ; plus d'une signifie que quelqu'un d'autre l'a compilé aussi. Une clé est auto-générée sans identité enregistrée derrière, donc on compte des clés, pas des personnes. MIT-0

Preuves d'exécution

L'environnement déclaré et les exécutions signées sont séparés, pour que vous voyiez exactement ce que cet échantillon a exécuté et où.

Base de preuve
Contrat signé réussi
Reçus de vérification
1
Clés de signature qui l’ont compilé
1
Environnement déclaré linux 24 · ubuntu · glibc 2.39 x64 npm

Environnements des exécutions de vérification

Environnement Contrat Étapes Exécution
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-08-28

Cas

HOW
Objectif
Sign and verify flattened JWS with FlattenedSign and flattenedVerify in jose
Paquets
Symboles
  • FlattenedSign
  • flattenedVerify
Créé
2026-08-28T07:36:01Z

Contrat

  1. FlattenedSign signs binary Uint8Array payload with HS256 and flattenedVerify resolves to raw Uint8Array payload and protected header
  2. FlattenedSign and flattenedVerify support unprotected header parameters via setUnprotectedHeader and unprotectedHeader
  3. FlattenedSign constructor rejects non-Uint8Array payload such as string or object with TypeError
  4. FlattenedSign sign rejects with JWSInvalid when sign is called before setProtectedHeader or setUnprotectedHeader
  5. FlattenedSign and flattenedVerify reject string secret keys with TypeError requiring CryptoKey, KeyObject, JSON Web Key, or Uint8Array
  6. flattenedVerify rejects incorrect key or tampered payload with JWSSignatureVerificationFailed
  7. flattenedVerify rejects non-object or missing mandatory fields with JWSInvalid
  8. flattenedVerify rejects algorithms not in allowed algorithms list with JOSEAlgNotAllowed
  9. FlattenedSign and flattenedVerify support asymmetric signing and verification with ES256 ECDSA key pair
  10. flattenedVerify supports dynamic key resolution via key resolver callback function

Fichiers

  • NOTES.md
  • PROMPT.md
  • csx.json
  • package-lock.json
  • package.json
  • spec.json
  • test/contract.mjs

Télécharger l’artefact source (tar.gz)

Code source

NOTES.md
# jose `FlattenedSign` and `flattenedVerify` (Flattened JWS)

## Prior Solution Search
`search_known_solution` returned `NO_SAFE_MATCH` for `FlattenedSign` and `flattenedVerify` in `jose@6.2.5`.

## Pinned Release
- `jose`: `6.2.5`

## Observed Behavior

When using `FlattenedSign` and `flattenedVerify` for Flattened JWS signing and verification:

1. **Flattened JWS Signing and Verification (Symmetric HS256)**:
   - `FlattenedSign` accepts raw binary payloads as `Uint8Array`.
   - Calling `setProtectedHeader({ alg: 'HS256' })` and `sign(secret)` produces a flattened JWS JSON object `{ payload: string, protected: string, signature: string }`.
   - `flattenedVerify(jws, secret)` parses and cryptographically verifies the flattened JWS, resolving to `{ payload: Uint8Array, protectedHeader: object }`. Raw payload bytes are returned as `Uint8Array`.

2. **Unprotected Header Parameter Support**:
   - `setUnprotectedHeader(header)` attaches unprotected header parameters into the `header` field of the JWS object.
   - `flattenedVerify` returns both `protectedHeader` and `unprotectedHeader` when present.

3. **Payload Type Enforcement**:
   - `FlattenedSign` constructor strictly requires an instance of `Uint8Array`. Passing non-Uint8Array types (such as plain strings or objects) throws a synchronous `TypeError: payload must be an instance of Uint8Array`.

4. **Mandatory Header Requirement**:
   - Calling `sign()` before configuring headers via `setProtectedHeader()` or `setUnprotectedHeader()` rejects with `errors.JWSInvalid` and code `ERR_JWS_INVALID` (`either setProtectedHeader or setUnprotectedHeader must be called before #sign()`).

5. **Secret Key Type Validation**:
   - Secret keys must be supplied as `Uint8Array`, `CryptoKey`, `KeyObject`, or JWK. Passing plain strings throws `TypeError` (`Key for the HS256 algorithm must be one of type CryptoKey, KeyObject, JSON Web Key, or Uint8Array.`).

6. **Signature Verification Failure**:
   - Verifying with an incorrect secret key or a tampered payload/signature portion rejects with `errors.JWSSignatureVerificationFailed` (`ERR_JWS_SIGNATURE_VERIFICATION_FAILED`).

7. **Malformed Serialization Rejection**:
   - Non-object inputs or missing mandatory fields (`protected`/`header` members, or `signature`) reject with `errors.JWSInvalid` (`ERR_JWS_INVALID`).

8. **Algorithm Restrictions**:
   - Specifying `algorithms` in `flattenedVerify` options restricts accepted algorithms. Tokens with unlisted algorithms reject with `errors.JOSEAlgNotAllowed` (`ERR_JOSE_ALG_NOT_ALLOWED`).

9. **Asymmetric Key Support (ES256)**:
   - Supports signing with asymmetric private keys (e.g. ECDSA `ES256`) and verifying with corresponding public keys (`CryptoKey` or `KeyObject`).

10. **Dynamic Key Resolver Support**:
    - `flattenedVerify` accepts a key resolver callback function `(protectedHeader, token) => KeyLike | Uint8Array` to dynamically resolve keys based on header parameters such as `kid` or `alg`.
PROMPT.md
Clean-room public code sample — generation instructions

Write a brand-new, minimal, self-contained code sample in this clean-room directory.
Do not copy, paraphrase, or reference any existing project source. Work only from this spec.

A csx.json manifest scaffold already exists. Do not recreate it from memory. Preserve its case.goal, packages and symbols; fill its empty case.contract with exact assertions and correct its environment, commands and verifierAdapter for the files you generate.

Goal: Sign and verify flattened JWS with FlattenedSign and flattenedVerify in jose
Kind: HOW

Use EXACTLY these public packages and versions:
  - pkg:npm/jose@6.2.5
Demonstrate these symbols/APIs:
  - FlattenedSign
  - flattenedVerify

Rules:
  - One focused purpose; the smallest project that proves the goal.
  - Include a contract test (test/contract.*) that runs OFFLINE and exits 0 exactly when the goal behavior works.
  - Pin every dependency with a lockfile so resolution is reproducible.
  - No secrets, credentials, or tokens. No real URLs (only example.com or localhost). No absolute paths.
  - No personal names, emails, company names, or project identifiers of any kind.
  - No binaries and no generated output (node_modules, dist, target, venv, .git, .env).
  - Keep it under 200 files and 256KB packed.
csx.json
{"case":{"caseId":"case:sha256:d4e5664b2fde84035e9cb495bd91a74ec54f8e32861d03515c8f9f6c7f8c4c50","contract":["FlattenedSign signs binary Uint8Array payload with HS256 and flattenedVerify resolves to raw Uint8Array payload and protected header","FlattenedSign and flattenedVerify support unprotected header parameters via setUnprotectedHeader and unprotectedHeader","FlattenedSign constructor rejects non-Uint8Array payload such as string or object with TypeError","FlattenedSign sign rejects with JWSInvalid when sign is called before setProtectedHeader or setUnprotectedHeader","FlattenedSign and flattenedVerify reject string secret keys with TypeError requiring CryptoKey, KeyObject, JSON Web Key, or Uint8Array","flattenedVerify rejects incorrect key or tampered payload with JWSSignatureVerificationFailed","flattenedVerify rejects non-object or missing mandatory fields with JWSInvalid","flattenedVerify rejects algorithms not in allowed algorithms list with JOSEAlgNotAllowed","FlattenedSign and flattenedVerify support asymmetric signing and verification with ES256 ECDSA key pair","flattenedVerify supports dynamic key resolution via key resolver callback function"],"goal":"Sign and verify flattened JWS with FlattenedSign and flattenedVerify in jose","kind":"HOW","packages":["pkg:npm/jose@6.2.5"],"schemaVersion":1,"symbols":["FlattenedSign","flattenedVerify"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"npm","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"npm","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/jose@6.2.5"],"schemaVersion":1,"subject":"pkg:npm/jose@6.2.5","symbols":["FlattenedSign","flattenedVerify"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "jose-flattened-sign-verify",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "jose-flattened-sign-verify",
      "version": "1.0.0",
      "dependencies": {
        "jose": "6.2.5"
      }
    },
    "node_modules/jose": {
      "version": "6.2.5",
      "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.5.tgz",
      "integrity": "sha512-2E5L2yRp03FnwreJLJX8/r7mHiZICCf8kG7fAsTWkSQTDAcc46NIZoQLKy+EJ8sPoJlxyS4OQR5H70LjIZZlIQ==",
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/panva"
      }
    }
  }
}
package.json
{
  "name": "jose-flattened-sign-verify",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "jose": "6.2.5"
  }
}
spec.json
{
  "schemaVersion": 1,
  "goal": "Sign and verify flattened JWS with FlattenedSign and flattenedVerify in jose",
  "kind": "HOW",
  "packages": [
    "pkg:npm/jose@6.2.5"
  ],
  "symbols": [
    "FlattenedSign",
    "flattenedVerify"
  ]
}
test/contract.mjs
import * as jose from 'jose';
import assert from 'node:assert/strict';

async function run() {
  const encoder = new TextEncoder();
  const decoder = new TextDecoder();
  const secret = encoder.encode('secret-key-must-be-at-least-32-bytes-long');
  const payloadBytes = encoder.encode(JSON.stringify({ user: 'alice', role: 'admin' }));

  // 1. Symmetric signing and verification with FlattenedSign and flattenedVerify (HS256)
  // FlattenedSign produces a flattened JWS JSON object with { payload, protected, signature }.
  // flattenedVerify resolves to { payload: Uint8Array, protectedHeader: object }.
  const jws = await new jose.FlattenedSign(payloadBytes)
    .setProtectedHeader({ alg: 'HS256' })
    .sign(secret);

  assert.equal(typeof jws, 'object');
  assert.equal(typeof jws.payload, 'string');
  assert.equal(typeof jws.protected, 'string');
  assert.equal(typeof jws.signature, 'string');

  const { payload, protectedHeader } = await jose.flattenedVerify(jws, secret);
  assert(payload instanceof Uint8Array);
  assert.deepEqual(payload, payloadBytes);
  assert.equal(decoder.decode(payload), JSON.stringify({ user: 'alice', role: 'admin' }));
  assert.deepEqual(protectedHeader, { alg: 'HS256' });

  // 2. Unprotected header parameter support
  // setUnprotectedHeader attaches unprotected parameters to the header field.
  const jwsWithUnprotected = await new jose.FlattenedSign(payloadBytes)
    .setProtectedHeader({ alg: 'HS256' })
    .setUnprotectedHeader({ kid: 'sym-key-1' })
    .sign(secret);

  assert.deepEqual(jwsWithUnprotected.header, { kid: 'sym-key-1' });
  const verifiedUnprotected = await jose.flattenedVerify(jwsWithUnprotected, secret);
  assert.deepEqual(verifiedUnprotected.payload, payloadBytes);
  assert.deepEqual(verifiedUnprotected.unprotectedHeader, { kid: 'sym-key-1' });

  // 3. Non-Uint8Array payload validation
  // FlattenedSign constructor strictly requires Uint8Array. Passing string or object throws TypeError.
  assert.throws(
    () => new jose.FlattenedSign('plain-text-string'),
    (err) => {
      assert(err instanceof TypeError);
      assert.equal(err.message, 'payload must be an instance of Uint8Array');
      return true;
    }
  );

  assert.throws(
    () => new jose.FlattenedSign({ key: 'val' }),
    (err) => {
      assert(err instanceof TypeError);
      assert.equal(err.message, 'payload must be an instance of Uint8Array');
      return true;
    }
  );

  // 4. Mandatory Header Requirement
  // Calling sign() without setting protected or unprotected header throws JWSInvalid (ERR_JWS_INVALID).
  await assert.rejects(
    async () => new jose.FlattenedSign(payloadBytes).sign(secret),
    (err) => {
      assert(err instanceof jose.errors.JWSInvalid);
      assert.equal(err.code, 'ERR_JWS_INVALID');
      assert.equal(err.message, 'either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
      return true;
    }
  );

  // 5. Secret key type requirement
  // Plain string secrets are rejected with TypeError; keys must be Uint8Array, KeyObject, CryptoKey, or JWK.
  await assert.rejects(
    async () => new jose.FlattenedSign(payloadBytes)
      .setProtectedHeader({ alg: 'HS256' })
      .sign('string-secret-not-allowed'),
    (err) => {
      assert(err instanceof TypeError);
      assert.equal(err.message, 'Key for the HS256 algorithm must be one of type CryptoKey, KeyObject, JSON Web Key, or Uint8Array.');
      return true;
    }
  );

  await assert.rejects(
    async () => jose.flattenedVerify(jws, 'string-secret-not-allowed'),
    (err) => {
      assert(err instanceof TypeError);
      assert.equal(err.message, 'Key for the HS256 algorithm must be one of type CryptoKey, KeyObject, JSON Web Key, or Uint8Array.');
      return true;
    }
  );

  // 6. Signature verification failure on incorrect key or tampered payload
  const wrongSecret = encoder.encode('another-secret-key-32-bytes-long');
  await assert.rejects(
    async () => jose.flattenedVerify(jws, wrongSecret),
    (err) => {
      assert(err instanceof jose.errors.JWSSignatureVerificationFailed);
      assert.equal(err.code, 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED');
      assert.equal(err.message, 'signature verification failed');
      return true;
    }
  );

  const tamperedJws = {
    ...jws,
    payload: jose.base64url.encode(encoder.encode(JSON.stringify({ user: 'mallory', role: 'admin' })))
  };
  await assert.rejects(
    async () => jose.flattenedVerify(tamperedJws, secret),
    (err) => {
      assert(err instanceof jose.errors.JWSSignatureVerificationFailed);
      assert.equal(err.code, 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED');
      assert.equal(err.message, 'signature verification failed');
      return true;
    }
  );

  // 7. Malformed Flattened JWS structure rejection
  // Non-object or missing mandatory fields throw JWSInvalid (ERR_JWS_INVALID).
  await assert.rejects(
    async () => jose.flattenedVerify('not-an-object', secret),
    (err) => {
      assert(err instanceof jose.errors.JWSInvalid);
      assert.equal(err.code, 'ERR_JWS_INVALID');
      assert.equal(err.message, 'Flattened JWS must be an object');
      return true;
    }
  );

  await assert.rejects(
    async () => jose.flattenedVerify({ payload: jws.payload }, secret),
    (err) => {
      assert(err instanceof jose.errors.JWSInvalid);
      assert.equal(err.code, 'ERR_JWS_INVALID');
      assert.equal(err.message, 'Flattened JWS must have either of the "protected" or "header" members');
      return true;
    }
  );

  await assert.rejects(
    async () => jose.flattenedVerify({ payload: jws.payload, protected: jws.protected }, secret),
    (err) => {
      assert(err instanceof jose.errors.JWSInvalid);
      assert.equal(err.code, 'ERR_JWS_INVALID');
      assert.equal(err.message, 'JWS Signature missing or incorrect type');
      return true;
    }
  );

  // 8. Algorithm restriction via options.algorithms
  // When allowed algorithms list excludes the token's algorithm, throws JOSEAlgNotAllowed (ERR_JOSE_ALG_NOT_ALLOWED).
  await assert.rejects(
    async () => jose.flattenedVerify(jws, secret, { algorithms: ['ES256'] }),
    (err) => {
      assert(err instanceof jose.errors.JOSEAlgNotAllowed);
      assert.equal(err.code, 'ERR_JOSE_ALG_NOT_ALLOWED');
      assert.equal(err.message, '"alg" (Algorithm) Header Parameter value not allowed');
      return true;
    }
  );

  // 9. Asymmetric signing and verification (ES256 ECDSA key pair)
  const { privateKey, publicKey } = await jose.generateKeyPair('ES256');
  const asymmetricJws = await new jose.FlattenedSign(payloadBytes)
    .setProtectedHeader({ alg: 'ES256', kid: 'ecc-key-1' })
    .sign(privateKey);

  const asymmetricResult = await jose.flattenedVerify(asymmetricJws, publicKey);
  assert.deepEqual(asymmetricResult.payload, payloadBytes);
  assert.equal(asymmetricResult.protectedHeader.alg, 'ES256');
  assert.equal(asymmetricResult.protectedHeader.kid, 'ecc-key-1');

  // 10. Dynamic key resolver callback function support
  // flattenedVerify accepts a key resolver callback (protectedHeader, token) => KeyLike | Uint8Array
  const keyMap = new Map([
    ['ecc-key-1', publicKey],
  ]);

  const keyResolver = async (protectedHeader, token) => {
    const key = keyMap.get(protectedHeader.kid);
    if (!key) {
      throw new Error('Key not found');
    }
    return key;
  };

  const resolvedResult = await jose.flattenedVerify(asymmetricJws, keyResolver);
  assert.deepEqual(resolvedResult.payload, payloadBytes);
  assert.equal(resolvedResult.protectedHeader.kid, 'ecc-key-1');

  // Resolver throwing or missing key causes rejection
  const jwsWithUnknownKid = await new jose.FlattenedSign(payloadBytes)
    .setProtectedHeader({ alg: 'ES256', kid: 'unknown-key' })
    .sign(privateKey);

  await assert.rejects(
    async () => jose.flattenedVerify(jwsWithUnknownKid, keyResolver),
    (err) => {
      assert.equal(err.message, 'Key not found');
      return true;
    }
  );
}

run();

Seeder d'origine

anonyme