@naturalcycles/nodejs-lib 15.117.0 → 15.118.0

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.
@@ -175,6 +175,22 @@ export declare class JWTService2<T extends AnyObject = AnyObject> {
175
175
  constructor(cfg: JWTService2Cfg<T>);
176
176
  sign<TT extends T = T>(payload: TT, opt: JWTSignOptions<TT>): Promise<JWTString>;
177
177
  verify<TT extends T = T>(token: JWTString, opt?: JWTVerifyOptions<TT>): Promise<TT>;
178
+ /**
179
+ * Tries to Verify the token, falling back to unverified Decode on failure,
180
+ * so the caller can "peek" into the token's content even when it doesn't verify.
181
+ *
182
+ * Returns an [error, payload] tuple - ALWAYS check the error first:
183
+ * - [null, payload] - the token verified, payload can be trusted
184
+ * - [error, payload] - verification failed, but the token could still be decoded.
185
+ * When the error is JWTExpiredError/JWTNotYetValidError, the signature was already
186
+ * verified and only the time-claim check failed, so the payload is trustworthy
187
+ * (just expired/not yet valid). For any other error the payload is completely
188
+ * UNVERIFIED - never trust it, only peek (e.g to log/report the claimed identity).
189
+ * - [error, null] - the token could not even be decoded
190
+ *
191
+ * The decode fallback is raw: opt.schema/cfg.schema are only applied on the Verify path.
192
+ */
193
+ tryToVerifyOrDecode<TT extends T = T>(token: JWTString, opt?: JWTVerifyOptions<TT>): Promise<[err: null, payload: TT] | [err: Error, payload: TT | null]>;
178
194
  decode<TT extends T = T>(token: JWTString, opt?: JWTDecodeOptions<TT>): JWTDecoded<TT>;
179
195
  /**
180
196
  * Schema-validation errors on Verify/Decode are extended with cfg.errorData:
@@ -88,6 +88,40 @@ export class JWTService2 {
88
88
  this.validate(data, schema);
89
89
  return data;
90
90
  }
91
+ /**
92
+ * Tries to Verify the token, falling back to unverified Decode on failure,
93
+ * so the caller can "peek" into the token's content even when it doesn't verify.
94
+ *
95
+ * Returns an [error, payload] tuple - ALWAYS check the error first:
96
+ * - [null, payload] - the token verified, payload can be trusted
97
+ * - [error, payload] - verification failed, but the token could still be decoded.
98
+ * When the error is JWTExpiredError/JWTNotYetValidError, the signature was already
99
+ * verified and only the time-claim check failed, so the payload is trustworthy
100
+ * (just expired/not yet valid). For any other error the payload is completely
101
+ * UNVERIFIED - never trust it, only peek (e.g to log/report the claimed identity).
102
+ * - [error, null] - the token could not even be decoded
103
+ *
104
+ * The decode fallback is raw: opt.schema/cfg.schema are only applied on the Verify path.
105
+ */
106
+ async tryToVerifyOrDecode(token, opt = {}) {
107
+ let verifyError;
108
+ try {
109
+ return [null, await this.verify(token, opt)];
110
+ }
111
+ catch (err) {
112
+ verifyError = err;
113
+ }
114
+ // Expired/not-yet-valid errors already carry the (signature-verified) payload
115
+ if (verifyError instanceof JWTExpiredError || verifyError instanceof JWTNotYetValidError) {
116
+ return [verifyError, verifyError.payload];
117
+ }
118
+ try {
119
+ return [verifyError, jwtDecode(token).payload];
120
+ }
121
+ catch {
122
+ return [verifyError, null];
123
+ }
124
+ }
91
125
  decode(token, opt = {}) {
92
126
  let decoded;
93
127
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.117.0",
4
+ "version": "15.118.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@standard-schema/spec": "^1",
@@ -274,6 +274,45 @@ export class JWTService2<T extends AnyObject = AnyObject> {
274
274
  return data
275
275
  }
276
276
 
277
+ /**
278
+ * Tries to Verify the token, falling back to unverified Decode on failure,
279
+ * so the caller can "peek" into the token's content even when it doesn't verify.
280
+ *
281
+ * Returns an [error, payload] tuple - ALWAYS check the error first:
282
+ * - [null, payload] - the token verified, payload can be trusted
283
+ * - [error, payload] - verification failed, but the token could still be decoded.
284
+ * When the error is JWTExpiredError/JWTNotYetValidError, the signature was already
285
+ * verified and only the time-claim check failed, so the payload is trustworthy
286
+ * (just expired/not yet valid). For any other error the payload is completely
287
+ * UNVERIFIED - never trust it, only peek (e.g to log/report the claimed identity).
288
+ * - [error, null] - the token could not even be decoded
289
+ *
290
+ * The decode fallback is raw: opt.schema/cfg.schema are only applied on the Verify path.
291
+ */
292
+ async tryToVerifyOrDecode<TT extends T = T>(
293
+ token: JWTString,
294
+ opt: JWTVerifyOptions<TT> = {},
295
+ ): Promise<[err: null, payload: TT] | [err: Error, payload: TT | null]> {
296
+ let verifyError: Error
297
+
298
+ try {
299
+ return [null, await this.verify<TT>(token, opt)]
300
+ } catch (err) {
301
+ verifyError = err as Error
302
+ }
303
+
304
+ // Expired/not-yet-valid errors already carry the (signature-verified) payload
305
+ if (verifyError instanceof JWTExpiredError || verifyError instanceof JWTNotYetValidError) {
306
+ return [verifyError, verifyError.payload as TT]
307
+ }
308
+
309
+ try {
310
+ return [verifyError, jwtDecode<TT>(token).payload]
311
+ } catch {
312
+ return [verifyError, null]
313
+ }
314
+ }
315
+
277
316
  decode<TT extends T = T>(token: JWTString, opt: JWTDecodeOptions<TT> = {}): JWTDecoded<TT> {
278
317
  let decoded: JWTDecoded<TT>
279
318