Ejemplo
jose 6.2.9: Sign and verify compact JWS with CompactSign and compactVerify in jose
Muestra verificada para npm jose 6.2.9: Sign and verify compact JWS with CompactSign and compactVerify in jose. El contrato se ejecutó en node 22 · linux…
sha256:f0055e6757de9ed8a97ad948b61220493c19f7ec36e064e313d91c41476c8dfc
Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido.
Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas.
MIT-0
Evidencia de ejecución
El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.
- Base de evidencia
- Contrato firmado aprobado
- Recibos de verificación
- 2
- Claves de firma que lo compilaron
- 1
Entorno declarado
linux 24 · ubuntu · glibc 2.39 x64 npm
Entornos de las ejecuciones de verificación
| Entorno | Contrato | Etapas | Ejecución |
|---|---|---|---|
| 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-26 |
| node 22 · linux debian/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1node:22@sha256:8a34c4ab3ea2… |
2026-09-07 |
Caso
HOW- Objetivo
- Sign and verify compact JWS with CompactSign and compactVerify in jose
- Paquetes
- Símbolos
-
- CompactSign
- compactVerify
- Creado
- 2026-08-26T07:46:56Z
Contrato
- CompactSign signs binary Uint8Array payload with HS256 and compactVerify resolves to raw Uint8Array payload and protected header
- CompactSign constructor rejects non-Uint8Array payload such as string or object with TypeError
- CompactSign sign rejects with JWSInvalid when sign is called before setProtectedHeader
- CompactSign and compactVerify reject string secret keys with TypeError requiring CryptoKey, KeyObject, JSON Web Key, or Uint8Array
- compactVerify rejects incorrect key or tampered payload with JWSSignatureVerificationFailed
- compactVerify rejects malformed compact JWS strings with JWSInvalid
- compactVerify rejects algorithms not in allowed algorithms list with JOSEAlgNotAllowed
- CompactSign and compactVerify support asymmetric signing and verification with ES256 ECDSA key pair
- compactVerify supports dynamic key resolution via key resolver callback function
Archivos
- NOTES.md
- PROMPT.md
- csx.json
- package-lock.json
- package.json
- spec.json
- test/contract.mjs
Código fuente
# jose `CompactSign` and `compactVerify` (Compact JWS)
## Prior Solution Search
`search_known_solution` returned `NO_SAFE_MATCH` for `CompactSign` and `compactVerify` in `jose@6.2.9`.
## Pinned Release
- `jose`: `6.2.9`
## Observed Behavior
When using `CompactSign` and `compactVerify` for Compact JWS signing and verification:
1. **Compact JWS Signing and Verification (Symmetric HS256)**:
- `CompactSign` constructor accepts raw binary payloads as `Uint8Array`.
- Calling `setProtectedHeader({ alg: 'HS256' })` and `sign(secret)` produces a 3-part dot-separated base64url encoded JWS string (`header.payload.signature`).
- `compactVerify(jws, secret)` parses and cryptographically verifies the compact JWS, resolving to `{ payload: Uint8Array, protectedHeader: object }`. Unlike `jwtVerify`, `payload` is returned as raw `Uint8Array` bytes rather than parsed JSON claims.
2. **Payload Type Enforcement**:
- `CompactSign` 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`.
3. **Mandatory Protected Header**:
- Calling `sign()` before configuring the header via `setProtectedHeader()` rejects with `errors.JWSInvalid` and code `ERR_JWS_INVALID` (`either setProtectedHeader or setUnprotectedHeader must be called before #sign()`).
4. **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.`).
5. **Signature Verification Failure**:
- Verifying with an incorrect secret key or a tampered payload/signature portion rejects with `errors.JWSSignatureVerificationFailed` (`ERR_JWS_SIGNATURE_VERIFICATION_FAILED`).
6. **Malformed Serialization Rejection**:
- Malformed compact JWS strings (e.g. invalid dot separation) reject with `errors.JWSInvalid` (`ERR_JWS_INVALID`, `'Invalid Compact JWS'`).
7. **Algorithm Restrictions**:
- Specifying `algorithms` in `compactVerify` options restricts accepted algorithms. Tokens with unlisted algorithms reject with `errors.JOSEAlgNotAllowed` (`ERR_JOSE_ALG_NOT_ALLOWED`).
8. **Asymmetric Key Support (ES256)**:
- Supports signing with asymmetric private keys (e.g. ECDSA `ES256`) and verifying with corresponding public keys (`CryptoKey` or `KeyObject`).
9. **Dynamic Key Resolver Support**:
- `compactVerify` accepts a key resolver callback function `(protectedHeader, token) => KeyLike | Uint8Array` to dynamically resolve keys based on header parameters such as `kid` or `alg`.
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 compact JWS with CompactSign and compactVerify in jose
Kind: HOW
Use EXACTLY these public packages and versions:
- pkg:npm/jose@6.2.9
Demonstrate these symbols/APIs:
- CompactSign
- compactVerify
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.
{"case":{"caseId":"case:sha256:e1a12b3a91420d9f7cc8ade90fd12b56e94d72bd5fe41c8d63999d114836e891","contract":["CompactSign signs binary Uint8Array payload with HS256 and compactVerify resolves to raw Uint8Array payload and protected header","CompactSign constructor rejects non-Uint8Array payload such as string or object with TypeError","CompactSign sign rejects with JWSInvalid when sign is called before setProtectedHeader","CompactSign and compactVerify reject string secret keys with TypeError requiring CryptoKey, KeyObject, JSON Web Key, or Uint8Array","compactVerify rejects incorrect key or tampered payload with JWSSignatureVerificationFailed","compactVerify rejects malformed compact JWS strings with JWSInvalid","compactVerify rejects algorithms not in allowed algorithms list with JOSEAlgNotAllowed","CompactSign and compactVerify support asymmetric signing and verification with ES256 ECDSA key pair","compactVerify supports dynamic key resolution via key resolver callback function"],"goal":"Sign and verify compact JWS with CompactSign and compactVerify in jose","kind":"HOW","packages":["pkg:npm/jose@6.2.9"],"schemaVersion":1,"symbols":["CompactSign","compactVerify"]},"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.9"],"schemaVersion":1,"subject":"pkg:npm/jose@6.2.9","symbols":["CompactSign","compactVerify"],"verifierAdapter":"node-typescript@1"}
{
"name": "jose-compact-sign-verify",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jose-compact-sign-verify",
"version": "1.0.0",
"dependencies": {
"jose": "6.2.9"
}
},
"node_modules/jose": {
"version": "6.2.9",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.9.tgz",
"integrity": "sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
}
}
}
{
"name": "jose-compact-sign-verify",
"version": "1.0.0",
"type": "module",
"dependencies": {
"jose": "6.2.9"
}
}
{
"schemaVersion": 1,
"goal": "Sign and verify compact JWS with CompactSign and compactVerify in jose",
"kind": "HOW",
"packages": [
"pkg:npm/jose@6.2.9"
],
"symbols": [
"CompactSign",
"compactVerify"
]
}
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. Compact JWS Signing and Verification (Symmetric HS256)
// CompactSign serializes arbitrary Uint8Array payload into a compact 3-part JWS string.
// compactVerify returns { payload: Uint8Array, protectedHeader: object }.
const jws = await new jose.CompactSign(payloadBytes)
.setProtectedHeader({ alg: 'HS256' })
.sign(secret);
assert.equal(typeof jws, 'string');
assert.equal(jws.split('.').length, 3);
const { payload, protectedHeader } = await jose.compactVerify(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. Non-Uint8Array payload validation
// CompactSign constructor strictly requires Uint8Array. Passing strings or objects throws TypeError.
assert.throws(
() => new jose.CompactSign('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.CompactSign({ key: 'val' }),
(err) => {
assert(err instanceof TypeError);
assert.equal(err.message, 'payload must be an instance of Uint8Array');
return true;
}
);
// 3. Mandatory Protected Header
// Calling sign() without calling setProtectedHeader() throws JWSInvalid (ERR_JWS_INVALID).
await assert.rejects(
async () => new jose.CompactSign(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;
}
);
// 4. 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.CompactSign(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.compactVerify(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;
}
);
// 5. Signature verification failure with wrong key or tampered payload
const wrongSecret = encoder.encode('another-secret-key-32-bytes-long');
await assert.rejects(
async () => jose.compactVerify(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 parts = jws.split('.');
const tamperedJws = `${parts[0]}.${jose.base64url.encode(encoder.encode(JSON.stringify({ user: 'mallory', role: 'admin' })))}.${parts[2]}`;
await assert.rejects(
async () => jose.compactVerify(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;
}
);
// 6. Malformed compact JWS structure
// Invalid string formats (e.g. 2 parts or 4 parts) throw JWSInvalid (ERR_JWS_INVALID).
await assert.rejects(
async () => jose.compactVerify('invalid.compact.jws.structure', secret),
(err) => {
assert(err instanceof jose.errors.JWSInvalid);
assert.equal(err.code, 'ERR_JWS_INVALID');
assert.equal(err.message, 'Invalid Compact JWS');
return true;
}
);
// 7. 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.compactVerify(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;
}
);
// 8. Asymmetric signing and verification (ES256 ECDSA key pair)
const { privateKey, publicKey } = await jose.generateKeyPair('ES256');
const asymmetricJws = await new jose.CompactSign(payloadBytes)
.setProtectedHeader({ alg: 'ES256', kid: 'key-1' })
.sign(privateKey);
const asymmetricResult = await jose.compactVerify(asymmetricJws, publicKey);
assert.deepEqual(asymmetricResult.payload, payloadBytes);
assert.equal(asymmetricResult.protectedHeader.alg, 'ES256');
assert.equal(asymmetricResult.protectedHeader.kid, 'key-1');
// 9. Key resolver callback function support
// compactVerify accepts a key resolver callback (protectedHeader, token) => KeyLike | Uint8Array
const keyMap = new Map([
['key-1', publicKey],
]);
const keyResolver = async (header) => {
const key = keyMap.get(header.kid);
if (!key) {
throw new Error('Key not found');
}
return key;
};
const resolvedResult = await jose.compactVerify(asymmetricJws, keyResolver);
assert.deepEqual(resolvedResult.payload, payloadBytes);
assert.equal(resolvedResult.protectedHeader.kid, 'key-1');
// Resolver throwing or missing key causes rejection
const jwsWithUnknownKid = await new jose.CompactSign(payloadBytes)
.setProtectedHeader({ alg: 'ES256', kid: 'unknown-key' })
.sign(privateKey);
await assert.rejects(
async () => jose.compactVerify(jwsWithUnknownKid, keyResolver),
(err) => {
assert.equal(err.message, 'Key not found');
return true;
}
);
}
run();
Seeder de origen
anónimo