jose 6.0.9 → 6.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -118,10 +118,10 @@ Please note that certain algorithms may not be available depending on the runtim
118
118
 
119
119
  | Version | Security Fixes 🔑 | Other Bug Fixes 🐞 | New Features ⭐ | Runtime and Module type |
120
120
  | ----------------------------------------------- | ----------------- | ------------------ | --------------- | ------------------------------- |
121
- | [v6.x](https://github.com/panva/jose/tree/v6.x) | ✅ | ✅ | ✅ | Universal[^universal] ESM[^cjs] |
122
- | [v5.x](https://github.com/panva/jose/tree/v5.x) | ✅ | ❌ | ❌ | Universal[^universal] CJS + ESM |
123
- | [v4.x](https://github.com/panva/jose/tree/v4.x) | ✅ | ❌ | ❌ | Universal[^universal] CJS + ESM |
124
- | [v2.x](https://github.com/panva/jose/tree/v2.x) | ✅ | ❌ | ❌ | Node.js CJS |
121
+ | [v6.x](https://github.com/panva/jose/tree/v6.x) | [Security Policy] | ✅ | ✅ | Universal[^universal] ESM[^cjs] |
122
+ | [v5.x](https://github.com/panva/jose/tree/v5.x) | [Security Policy] | ❌ | ❌ | Universal[^universal] CJS + ESM |
123
+ | [v4.x](https://github.com/panva/jose/tree/v4.x) | [Security Policy] | ❌ | ❌ | Universal[^universal] CJS + ESM |
124
+ | [v2.x](https://github.com/panva/jose/tree/v2.x) | [Security Policy] | ❌ | ❌ | Node.js CJS |
125
125
 
126
126
  ## Specifications
127
127
 
@@ -145,7 +145,8 @@ The algorithm implementations in `jose` have been tested using test vectors from
145
145
  [sponsor-auth0]: https://a0.to/signup/panva
146
146
  [WebCryptoAPI]: https://w3c.github.io/webcrypto/
147
147
  [Fetch API]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
148
+ [Security Policy]: https://github.com/panva/jose/security/policy
148
149
 
149
- [^cjs]: CJS style `let jose = require('jose')` is possible in Node.js versions where `process.features.require_module` is `true` by default (^20.19.0 || ^22.12.0 || >= 23.0.0) or with the `--experimental-require-module` Node.js CLI flag.
150
+ [^cjs]: CJS style `let jose = require('jose')` is possible in Node.js versions where the `require(esm)` feature is enabled by default (^20.19.0 || ^22.12.0 || >= 23.0.0).
150
151
 
151
152
  [^universal]: Assumes runtime support of [WebCryptoAPI][] and [Fetch API][]
@@ -9,7 +9,7 @@ function isCloudflareWorkers() {
9
9
  let USER_AGENT;
10
10
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
11
11
  const NAME = 'jose';
12
- const VERSION = 'v6.0.9';
12
+ const VERSION = 'v6.0.11';
13
13
  USER_AGENT = `${NAME}/${VERSION}`;
14
14
  }
15
15
  export const customFetch = Symbol();
@@ -1,9 +1,9 @@
1
1
  import { compactDecrypt } from '../jwe/compact/decrypt.js';
2
- import jwtPayload from '../lib/jwt_claims_set.js';
2
+ import { validateClaimsSet } from '../lib/jwt_claims_set.js';
3
3
  import { JWTClaimValidationFailed } from '../util/errors.js';
4
4
  export async function jwtDecrypt(jwt, key, options) {
5
5
  const decrypted = await compactDecrypt(jwt, key, options);
6
- const payload = jwtPayload(decrypted.protectedHeader, decrypted.plaintext, options);
6
+ const payload = validateClaimsSet(decrypted.protectedHeader, decrypted.plaintext, options);
7
7
  const { protectedHeader } = decrypted;
8
8
  if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) {
9
9
  throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', payload, 'iss', 'mismatch');
@@ -1,5 +1,5 @@
1
1
  import { CompactEncrypt } from '../jwe/compact/encrypt.js';
2
- import { ProduceJWT } from './produce.js';
2
+ import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
3
3
  export class EncryptJWT {
4
4
  #cek;
5
5
  #iv;
@@ -10,7 +10,7 @@ export class EncryptJWT {
10
10
  #replicateAudienceAsHeader;
11
11
  #jwt;
12
12
  constructor(payload = {}) {
13
- this.#jwt = new ProduceJWT(payload);
13
+ this.#jwt = new JWTClaimsBuilder(payload);
14
14
  }
15
15
  setIssuer(issuer) {
16
16
  this.#jwt.iss = issuer;
@@ -1,11 +1,11 @@
1
1
  import { CompactSign } from '../jws/compact/sign.js';
2
2
  import { JWTInvalid } from '../util/errors.js';
3
- import { ProduceJWT } from './produce.js';
3
+ import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
4
4
  export class SignJWT {
5
5
  #protectedHeader;
6
6
  #jwt;
7
7
  constructor(payload = {}) {
8
- this.#jwt = new ProduceJWT(payload);
8
+ this.#jwt = new JWTClaimsBuilder(payload);
9
9
  }
10
10
  setIssuer(issuer) {
11
11
  this.#jwt.iss = issuer;
@@ -1,12 +1,11 @@
1
1
  import * as b64u from '../util/base64url.js';
2
2
  import { decoder } from '../lib/buffer_utils.js';
3
3
  import { JWTInvalid } from '../util/errors.js';
4
- import jwtPayload from '../lib/jwt_claims_set.js';
5
- import { ProduceJWT } from './produce.js';
4
+ import { validateClaimsSet, JWTClaimsBuilder } from '../lib/jwt_claims_set.js';
6
5
  export class UnsecuredJWT {
7
6
  #jwt;
8
7
  constructor(payload = {}) {
9
- this.#jwt = new ProduceJWT(payload);
8
+ this.#jwt = new JWTClaimsBuilder(payload);
10
9
  }
11
10
  encode() {
12
11
  const header = b64u.encode(JSON.stringify({ alg: 'none' }));
@@ -58,7 +57,7 @@ export class UnsecuredJWT {
58
57
  catch {
59
58
  throw new JWTInvalid('Invalid Unsecured JWT');
60
59
  }
61
- const payload = jwtPayload(header, b64u.decode(encodedPayload), options);
60
+ const payload = validateClaimsSet(header, b64u.decode(encodedPayload), options);
62
61
  return { payload, header };
63
62
  }
64
63
  }
@@ -1,12 +1,12 @@
1
1
  import { compactVerify } from '../jws/compact/verify.js';
2
- import jwtPayload from '../lib/jwt_claims_set.js';
2
+ import { validateClaimsSet } from '../lib/jwt_claims_set.js';
3
3
  import { JWTInvalid } from '../util/errors.js';
4
4
  export async function jwtVerify(jwt, key, options) {
5
5
  const verified = await compactVerify(jwt, key, options);
6
6
  if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) {
7
7
  throw new JWTInvalid('JWTs MUST NOT use unencoded payload');
8
8
  }
9
- const payload = jwtPayload(verified.protectedHeader, verified.payload, options);
9
+ const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
10
10
  const result = { payload, protectedHeader: verified.protectedHeader };
11
11
  if (typeof key === 'function') {
12
12
  return { ...result, key: verified.key };
@@ -3,7 +3,19 @@ import { decoder } from './buffer_utils.js';
3
3
  import epoch from './epoch.js';
4
4
  import secs from './secs.js';
5
5
  import isObject from './is_object.js';
6
- const normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, '');
6
+ import { encoder } from './buffer_utils.js';
7
+ function validateInput(label, input) {
8
+ if (!Number.isFinite(input)) {
9
+ throw new TypeError(`Invalid ${label} input`);
10
+ }
11
+ return input;
12
+ }
13
+ const normalizeTyp = (value) => {
14
+ if (value.includes('/')) {
15
+ return value.toLowerCase();
16
+ }
17
+ return `application/${value.toLowerCase()}`;
18
+ };
7
19
  const checkAudiencePresence = (audPayload, audOption) => {
8
20
  if (typeof audPayload === 'string') {
9
21
  return audOption.includes(audPayload);
@@ -13,7 +25,7 @@ const checkAudiencePresence = (audPayload, audOption) => {
13
25
  }
14
26
  return false;
15
27
  };
16
- export default (protectedHeader, encodedPayload, options = {}) => {
28
+ export function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
17
29
  let payload;
18
30
  try {
19
31
  payload = JSON.parse(decoder.decode(encodedPayload));
@@ -101,4 +113,73 @@ export default (protectedHeader, encodedPayload, options = {}) => {
101
113
  }
102
114
  }
103
115
  return payload;
104
- };
116
+ }
117
+ export class JWTClaimsBuilder {
118
+ #payload;
119
+ constructor(payload) {
120
+ if (!isObject(payload)) {
121
+ throw new TypeError('JWT Claims Set MUST be an object');
122
+ }
123
+ this.#payload = structuredClone(payload);
124
+ }
125
+ data() {
126
+ return encoder.encode(JSON.stringify(this.#payload));
127
+ }
128
+ get iss() {
129
+ return this.#payload.iss;
130
+ }
131
+ set iss(value) {
132
+ this.#payload.iss = value;
133
+ }
134
+ get sub() {
135
+ return this.#payload.sub;
136
+ }
137
+ set sub(value) {
138
+ this.#payload.sub = value;
139
+ }
140
+ get aud() {
141
+ return this.#payload.aud;
142
+ }
143
+ set aud(value) {
144
+ this.#payload.aud = value;
145
+ }
146
+ set jti(value) {
147
+ this.#payload.jti = value;
148
+ }
149
+ set nbf(value) {
150
+ if (typeof value === 'number') {
151
+ this.#payload.nbf = validateInput('setNotBefore', value);
152
+ }
153
+ else if (value instanceof Date) {
154
+ this.#payload.nbf = validateInput('setNotBefore', epoch(value));
155
+ }
156
+ else {
157
+ this.#payload.nbf = epoch(new Date()) + secs(value);
158
+ }
159
+ }
160
+ set exp(value) {
161
+ if (typeof value === 'number') {
162
+ this.#payload.exp = validateInput('setExpirationTime', value);
163
+ }
164
+ else if (value instanceof Date) {
165
+ this.#payload.exp = validateInput('setExpirationTime', epoch(value));
166
+ }
167
+ else {
168
+ this.#payload.exp = epoch(new Date()) + secs(value);
169
+ }
170
+ }
171
+ set iat(value) {
172
+ if (typeof value === 'undefined') {
173
+ this.#payload.iat = epoch(new Date());
174
+ }
175
+ else if (value instanceof Date) {
176
+ this.#payload.iat = validateInput('setIssuedAt', epoch(value));
177
+ }
178
+ else if (typeof value === 'string') {
179
+ this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));
180
+ }
181
+ else {
182
+ this.#payload.iat = validateInput('setIssuedAt', value);
183
+ }
184
+ }
185
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jose",
3
- "version": "6.0.9",
3
+ "version": "6.0.11",
4
4
  "description": "JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes",
5
5
  "keywords": [
6
6
  "browser",
@@ -1,20 +0,0 @@
1
- import type * as types from '../types.d.ts';
2
- export declare class ProduceJWT {
3
- #private;
4
- constructor(payload: types.JWTPayload);
5
- data(): Uint8Array;
6
- get iss(): string | undefined;
7
- set iss(value: string);
8
- get sub(): string | undefined;
9
- set sub(value: string);
10
- get aud(): string | string[] | undefined;
11
- set aud(value: string | string[]);
12
- get jti(): string | undefined;
13
- set jti(value: string);
14
- get nbf(): number | undefined;
15
- set nbf(value: number | string | Date);
16
- get exp(): number | undefined;
17
- set exp(value: number | string | Date);
18
- get iat(): number | undefined;
19
- set iat(value: number | string | Date | undefined);
20
- }
@@ -1,91 +0,0 @@
1
- import epoch from '../lib/epoch.js';
2
- import secs from '../lib/secs.js';
3
- import isObject from '../lib/is_object.js';
4
- import { encoder } from '../lib/buffer_utils.js';
5
- function validateInput(label, input) {
6
- if (!Number.isFinite(input)) {
7
- throw new TypeError(`Invalid ${label} input`);
8
- }
9
- return input;
10
- }
11
- export class ProduceJWT {
12
- #payload;
13
- constructor(payload) {
14
- if (!isObject(payload)) {
15
- throw new TypeError('JWT Claims Set MUST be an object');
16
- }
17
- this.#payload = structuredClone(payload);
18
- }
19
- data() {
20
- return encoder.encode(JSON.stringify(this.#payload));
21
- }
22
- get iss() {
23
- return this.#payload.iss;
24
- }
25
- set iss(value) {
26
- this.#payload.iss = value;
27
- }
28
- get sub() {
29
- return this.#payload.sub;
30
- }
31
- set sub(value) {
32
- this.#payload.sub = value;
33
- }
34
- get aud() {
35
- return this.#payload.aud;
36
- }
37
- set aud(value) {
38
- this.#payload.aud = value;
39
- }
40
- get jti() {
41
- return this.#payload.jti;
42
- }
43
- set jti(value) {
44
- this.#payload.jti = value;
45
- }
46
- get nbf() {
47
- return this.#payload.nbf;
48
- }
49
- set nbf(value) {
50
- if (typeof value === 'number') {
51
- this.#payload.nbf = validateInput('setNotBefore', value);
52
- }
53
- else if (value instanceof Date) {
54
- this.#payload.nbf = validateInput('setNotBefore', epoch(value));
55
- }
56
- else {
57
- this.#payload.nbf = epoch(new Date()) + secs(value);
58
- }
59
- }
60
- get exp() {
61
- return this.#payload.exp;
62
- }
63
- set exp(value) {
64
- if (typeof value === 'number') {
65
- this.#payload.exp = validateInput('setExpirationTime', value);
66
- }
67
- else if (value instanceof Date) {
68
- this.#payload.exp = validateInput('setExpirationTime', epoch(value));
69
- }
70
- else {
71
- this.#payload.exp = epoch(new Date()) + secs(value);
72
- }
73
- }
74
- get iat() {
75
- return this.#payload.iat;
76
- }
77
- set iat(value) {
78
- if (typeof value === 'undefined') {
79
- this.#payload.iat = epoch(new Date());
80
- }
81
- else if (value instanceof Date) {
82
- this.#payload.iat = validateInput('setIssuedAt', epoch(value));
83
- }
84
- else if (typeof value === 'string') {
85
- this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));
86
- }
87
- else {
88
- this.#payload.iat = validateInput('setIssuedAt', value);
89
- }
90
- }
91
- }