@carecard/jwt-read 3.19.0 → 3.20.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.
Files changed (2) hide show
  1. package/lib/jwtLib.js +36 -18
  2. package/package.json +4 -4
package/lib/jwtLib.js CHANGED
@@ -97,6 +97,7 @@ function validateAndExtractJwtObject(req, publicKey, customErrorFunction, option
97
97
 
98
98
  if (jwtString && isJwtSignatureValid) {
99
99
  _extractJwtObject(req, jwtString, customErrorFunction);
100
+ requireActiveExtractedToken(req, 'jwt', customErrorFunction);
100
101
  validateAndExtractOptionalUserAuthorizationObject(req, options, customErrorFunction);
101
102
  } else {
102
103
  req['jwt'] = null;
@@ -114,6 +115,7 @@ function validateAndExtractWebToken(req, publicKey, headerName, customErrorFunct
114
115
 
115
116
  if (webTokenString && isJwtSignatureValid) {
116
117
  _extractJwtObject(req, webTokenString, customErrorFunction);
118
+ requireActiveExtractedToken(req, 'jwt', customErrorFunction);
117
119
  validateAndExtractOptionalUserAuthorizationObject(req, options, customErrorFunction);
118
120
  } else {
119
121
  req['jwt'] = null;
@@ -463,6 +465,7 @@ function _isLoginRequired(hasRequiredRole, customErrorFunction) {
463
465
  }
464
466
  }
465
467
 
468
+ // Pattern: Fail-Closed Probe - accepts a bearer JWT only when signature and lifetime are valid.
466
469
  function tryValidateAndExtractJwtObject(req, publicKey) {
467
470
  const jwtString = _validateJwtNoThrow(req);
468
471
  if (!jwtString || !isJwtString(jwtString)) {
@@ -476,7 +479,11 @@ function tryValidateAndExtractJwtObject(req, publicKey) {
476
479
  }
477
480
 
478
481
  _extractJwtObjectNoThrow(req, jwtString);
479
- return Boolean(req?.jwt);
482
+ if (!isJwtPayloadWithinLifetime(req?.jwt?.payload)) {
483
+ req.jwt = null;
484
+ return false;
485
+ }
486
+ return true;
480
487
  }
481
488
 
482
489
  function _validateServerAuthToken(req, customErrorFunction) {
@@ -564,6 +571,7 @@ function normalizeSeconds(value) {
564
571
  return value > 1000000000000 ? Math.floor(value / 1000) : Math.floor(value);
565
572
  }
566
573
 
574
+ // Pattern: Policy - validates service identity, audience, and registered token lifetime.
567
575
  function _isServiceJwtFor(payload, expectedIssuer, expectedAudience) {
568
576
  if (!payload) {
569
577
  return false;
@@ -577,16 +585,7 @@ function _isServiceJwtFor(payload, expectedIssuer, expectedAudience) {
577
585
  if (!payloadAudienceMatches(payload.aud, expectedAudience)) {
578
586
  return false;
579
587
  }
580
- if (isJwtPayloadIssuedInFuture(payload)) {
581
- return false;
582
- }
583
- if (isJwtPayloadExpired(payload)) {
584
- return false;
585
- }
586
- if (isJwtPayloadNotYetValid(payload)) {
587
- return false;
588
- }
589
- return true;
588
+ return isJwtPayloadWithinLifetime(payload);
590
589
  }
591
590
 
592
591
  function payloadAudienceMatches(actualAudience, expectedAudience) {
@@ -629,6 +628,27 @@ function isJwtPayloadNotYetValid(payload) {
629
628
  return Math.floor(Date.now() / 1000) < nbf;
630
629
  }
631
630
 
631
+ // Pattern: Specification - centralizes the registered time-claim policy for signed tokens.
632
+ function isJwtPayloadWithinLifetime(payload) {
633
+ return (
634
+ Boolean(payload) &&
635
+ !isJwtPayloadIssuedInFuture(payload) &&
636
+ !isJwtPayloadExpired(payload) &&
637
+ !isJwtPayloadNotYetValid(payload)
638
+ );
639
+ }
640
+
641
+ // Pattern: Guard Clause - clears an extracted token before surfacing an authentication failure.
642
+ function requireActiveExtractedToken(req, propertyName, customErrorFunction) {
643
+ if (isJwtPayloadWithinLifetime(req?.[propertyName]?.payload)) {
644
+ return;
645
+ }
646
+ if (req) {
647
+ req[propertyName] = null;
648
+ }
649
+ throwError(customErrorFunction);
650
+ }
651
+
632
652
  // Pattern: Factory - normalizes direct user authorization options into one config shape.
633
653
  function createUserAuthorizationConfig(publicKey, options) {
634
654
  return normalizeUserAuthorizationConfig({
@@ -705,13 +725,7 @@ function isUserAuthorizationTokenAllowed(token, config) {
705
725
  if (!payload) {
706
726
  return false;
707
727
  }
708
- if (isJwtPayloadIssuedInFuture(payload)) {
709
- return false;
710
- }
711
- if (isJwtPayloadExpired(payload)) {
712
- return false;
713
- }
714
- if (isJwtPayloadNotYetValid(payload)) {
728
+ if (!isJwtPayloadWithinLifetime(payload)) {
715
729
  return false;
716
730
  }
717
731
  if (config.expectedType && payload.typ !== config.expectedType) {
@@ -762,6 +776,7 @@ function _validateGeneric(req, headerName, extractor, customErrorFunction) {
762
776
  return null;
763
777
  }
764
778
 
779
+ // Pattern: Fail-Closed Extraction - optional tokens survive only valid signatures and lifetimes.
765
780
  function _validateAndExtractGenericNoThrow(req, publicKey, validator, extractor, propertyName) {
766
781
  try {
767
782
  const jwtString = validator(req);
@@ -769,6 +784,9 @@ function _validateAndExtractGenericNoThrow(req, publicKey, validator, extractor,
769
784
 
770
785
  if (jwtString && isJwtSignatureValid) {
771
786
  extractor(req, jwtString);
787
+ if (!isJwtPayloadWithinLifetime(req?.[propertyName]?.payload)) {
788
+ req[propertyName] = null;
789
+ }
772
790
  } else {
773
791
  req[propertyName] = null;
774
792
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carecard/jwt-read",
3
- "version": "3.19.0",
3
+ "version": "3.20.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/CareCard-ca/pkg-jwt-read.git"
@@ -47,9 +47,9 @@
47
47
  "typescript": "6.0.3"
48
48
  },
49
49
  "dependencies": {
50
- "@carecard/auth-util": "3.18.0",
51
- "@carecard/common-util": "3.18.0",
52
- "@carecard/validate": "3.18.0"
50
+ "@carecard/auth-util": "3.20.0",
51
+ "@carecard/common-util": "3.20.0",
52
+ "@carecard/validate": "3.20.0"
53
53
  },
54
54
  "overrides": {
55
55
  "diff": "8.0.4",