@naturalcycles/nodejs-lib 15.116.1 → 15.116.2

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.
@@ -78,6 +78,10 @@ export interface JWTSignOptions<T extends AnyObject = AnyObject> {
78
78
  * By default `iat` is NOT set (same as legacy JWTService with its `noTimestamp: true` default).
79
79
  */
80
80
  issuedAt?: UnixTimestamp;
81
+ /**
82
+ * Sets the `kid` (key id) header, required by some APIs (e.g Apple App Store Connect).
83
+ */
84
+ kid?: string;
81
85
  /**
82
86
  * Overrides cfg.schema for this call.
83
87
  */
@@ -170,6 +174,14 @@ export declare class JWTService2<T extends AnyObject = AnyObject> {
170
174
  */
171
175
  private normalizeError;
172
176
  }
177
+ /**
178
+ * Decodes a JWT without verifying its signature (no key needed) - never trust the result.
179
+ * Standalone version of JWTService2.decode, for when there's no service instance at hand
180
+ * (no schema validation, no errorData extension).
181
+ *
182
+ * Throws JWTError with code JWT_INVALID if the token cannot be decoded.
183
+ */
184
+ export declare function jwtDecode<T extends AnyObject>(token: JWTString): JWTDecoded<T>;
173
185
  export type JWTErrorCode = 'JWT_EXPIRED' | 'JWT_NOT_YET_VALID' | 'JWT_INVALID';
174
186
  export interface JWTErrorData extends ErrorData {
175
187
  code: JWTErrorCode;
@@ -38,12 +38,17 @@ export class JWTService2 {
38
38
  }
39
39
  async sign(payload, opt) {
40
40
  _assert(this.privateKey, 'JWTService2: privateKey is required to be able to sign, but not provided', this.cfg.errorData);
41
- const { expiresAt, notBefore, issuer, audience, subject, jwtid, issuedAt, schema } = {
41
+ const { expiresAt, notBefore, issuer, audience, subject, jwtid, issuedAt, kid, schema } = {
42
42
  ...this.cfg.signOptions,
43
43
  ...opt,
44
44
  };
45
45
  (schema || this.cfg.schema)?.validate(payload);
46
- const jwt = new SignJWT(payload).setProtectedHeader({ alg: this.cfg.algorithm, typ: 'JWT' });
46
+ // `kid: undefined` is dropped by JSON serialization, keeping the header unchanged when not set
47
+ const jwt = new SignJWT(payload).setProtectedHeader({
48
+ alg: this.cfg.algorithm,
49
+ typ: 'JWT',
50
+ kid,
51
+ });
47
52
  if (expiresAt !== null)
48
53
  jwt.setExpirationTime(expiresAt);
49
54
  if (notBefore !== undefined)
@@ -83,24 +88,18 @@ export class JWTService2 {
83
88
  return data;
84
89
  }
85
90
  decode(token, opt = {}) {
86
- let header;
87
- let payload;
91
+ let decoded;
88
92
  try {
89
- header = decodeProtectedHeader(token);
90
- payload = decodeJwt(token);
93
+ decoded = jwtDecode(token);
91
94
  }
92
95
  catch (err) {
93
- throw new JWTError('invalid token, unable to decode', {
94
- ...this.cfg.errorData,
95
- code: 'JWT_INVALID',
96
- }, { cause: err });
96
+ if (this.cfg.errorData) {
97
+ _errorDataAppend(err, this.cfg.errorData);
98
+ }
99
+ throw err;
97
100
  }
98
- this.validate(payload, opt.schema);
99
- return {
100
- header,
101
- payload,
102
- signature: token.split('.')[2],
103
- };
101
+ this.validate(decoded.payload, opt.schema);
102
+ return decoded;
104
103
  }
105
104
  /**
106
105
  * Schema-validation errors on Verify/Decode are extended with cfg.errorData:
@@ -145,6 +144,29 @@ export class JWTService2 {
145
144
  }, { cause: err });
146
145
  }
147
146
  }
147
+ /**
148
+ * Decodes a JWT without verifying its signature (no key needed) - never trust the result.
149
+ * Standalone version of JWTService2.decode, for when there's no service instance at hand
150
+ * (no schema validation, no errorData extension).
151
+ *
152
+ * Throws JWTError with code JWT_INVALID if the token cannot be decoded.
153
+ */
154
+ export function jwtDecode(token) {
155
+ let header;
156
+ let payload;
157
+ try {
158
+ header = decodeProtectedHeader(token);
159
+ payload = decodeJwt(token);
160
+ }
161
+ catch (err) {
162
+ throw new JWTError('invalid token, unable to decode', { code: 'JWT_INVALID' }, { cause: err });
163
+ }
164
+ return {
165
+ header,
166
+ payload,
167
+ signature: token.split('.')[2],
168
+ };
169
+ }
148
170
  /**
149
171
  * Thrown by JWTService2 on any Verify/Decode failure.
150
172
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.116.1",
4
+ "version": "15.116.2",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@standard-schema/spec": "^1",
@@ -105,6 +105,10 @@ export interface JWTSignOptions<T extends AnyObject = AnyObject> {
105
105
  * By default `iat` is NOT set (same as legacy JWTService with its `noTimestamp: true` default).
106
106
  */
107
107
  issuedAt?: UnixTimestamp
108
+ /**
109
+ * Sets the `kid` (key id) header, required by some APIs (e.g Apple App Store Connect).
110
+ */
111
+ kid?: string
108
112
  /**
109
113
  * Overrides cfg.schema for this call.
110
114
  */
@@ -198,14 +202,19 @@ export class JWTService2<T extends AnyObject = AnyObject> {
198
202
  this.cfg.errorData,
199
203
  )
200
204
 
201
- const { expiresAt, notBefore, issuer, audience, subject, jwtid, issuedAt, schema } = {
205
+ const { expiresAt, notBefore, issuer, audience, subject, jwtid, issuedAt, kid, schema } = {
202
206
  ...this.cfg.signOptions,
203
207
  ...opt,
204
208
  }
205
209
 
206
210
  ;(schema || this.cfg.schema)?.validate(payload)
207
211
 
208
- const jwt = new SignJWT(payload).setProtectedHeader({ alg: this.cfg.algorithm, typ: 'JWT' })
212
+ // `kid: undefined` is dropped by JSON serialization, keeping the header unchanged when not set
213
+ const jwt = new SignJWT(payload).setProtectedHeader({
214
+ alg: this.cfg.algorithm,
215
+ typ: 'JWT',
216
+ kid,
217
+ })
209
218
  if (expiresAt !== null) jwt.setExpirationTime(expiresAt)
210
219
  if (notBefore !== undefined) jwt.setNotBefore(notBefore)
211
220
  if (issuer) jwt.setIssuer(issuer)
@@ -249,30 +258,20 @@ export class JWTService2<T extends AnyObject = AnyObject> {
249
258
  }
250
259
 
251
260
  decode<TT extends T = T>(token: JWTString, opt: JWTDecodeOptions<TT> = {}): JWTDecoded<TT> {
252
- let header: JWTHeader
253
- let payload: TT
261
+ let decoded: JWTDecoded<TT>
254
262
 
255
263
  try {
256
- header = decodeProtectedHeader(token) as JWTHeader
257
- payload = decodeJwt(token) as TT
264
+ decoded = jwtDecode<TT>(token)
258
265
  } catch (err) {
259
- throw new JWTError(
260
- 'invalid token, unable to decode',
261
- {
262
- ...this.cfg.errorData,
263
- code: 'JWT_INVALID',
264
- },
265
- { cause: err },
266
- )
266
+ if (this.cfg.errorData) {
267
+ _errorDataAppend(err, this.cfg.errorData)
268
+ }
269
+ throw err
267
270
  }
268
271
 
269
- this.validate(payload, opt.schema)
272
+ this.validate(decoded.payload, opt.schema)
270
273
 
271
- return {
272
- header,
273
- payload,
274
- signature: token.split('.')[2]!,
275
- }
274
+ return decoded
276
275
  }
277
276
 
278
277
  /**
@@ -321,6 +320,31 @@ export class JWTService2<T extends AnyObject = AnyObject> {
321
320
  }
322
321
  }
323
322
 
323
+ /**
324
+ * Decodes a JWT without verifying its signature (no key needed) - never trust the result.
325
+ * Standalone version of JWTService2.decode, for when there's no service instance at hand
326
+ * (no schema validation, no errorData extension).
327
+ *
328
+ * Throws JWTError with code JWT_INVALID if the token cannot be decoded.
329
+ */
330
+ export function jwtDecode<T extends AnyObject>(token: JWTString): JWTDecoded<T> {
331
+ let header: JWTHeader
332
+ let payload: T
333
+
334
+ try {
335
+ header = decodeProtectedHeader(token) as JWTHeader
336
+ payload = decodeJwt(token) as T
337
+ } catch (err) {
338
+ throw new JWTError('invalid token, unable to decode', { code: 'JWT_INVALID' }, { cause: err })
339
+ }
340
+
341
+ return {
342
+ header,
343
+ payload,
344
+ signature: token.split('.')[2]!,
345
+ }
346
+ }
347
+
324
348
  export type JWTErrorCode = 'JWT_EXPIRED' | 'JWT_NOT_YET_VALID' | 'JWT_INVALID'
325
349
 
326
350
  export interface JWTErrorData extends ErrorData {