diff --git a/api/server/middleware/__tests__/requireJwtAuth.spec.js b/api/server/middleware/__tests__/requireJwtAuth.spec.js index b70f371a94..64e00e4aa4 100644 --- a/api/server/middleware/__tests__/requireJwtAuth.spec.js +++ b/api/server/middleware/__tests__/requireJwtAuth.spec.js @@ -564,12 +564,17 @@ describe('requireJwtAuth tenant context chaining', () => { fallback_strategy: 'jwt', fallback_attempted: true, fallback_succeeded: false, + // The real openidJwt failure is surfaced alongside the fallback's reason so a + // reused-token failure is not misattributed to the `jwt` fallback's error (#14311). + primary_failure_reason: 'jwt expired', + primary_failure_error_name: 'TokenExpiredError', reason: 'invalid signature', error_name: 'JsonWebTokenError', status: 401, }), ); expect(logger.warn.mock.calls[0][0]).toContain('"reason":"invalid signature"'); + expect(logger.warn.mock.calls[0][0]).toContain('"primary_failure_reason":"jwt expired"'); expect(logger.warn.mock.calls[0][0]).toContain('"path":"/api/ask"'); }); diff --git a/api/server/middleware/requireJwtAuth.js b/api/server/middleware/requireJwtAuth.js index 9c4d1ca47c..b4877bcdc5 100644 --- a/api/server/middleware/requireJwtAuth.js +++ b/api/server/middleware/requireJwtAuth.js @@ -121,6 +121,14 @@ const requireJwtAuth = (req, res, next) => { fallback_succeeded: false, attempted_strategies: strategies, final_strategy: strategy, + // Surface the primary (openidJwt) failure alongside the final strategy's reason so a + // reused-token failure is not misattributed to the HS256 `jwt` fallback's "invalid + // algorithm" error, which is the fallback rejecting an RS256 provider token, not the + // real reason openidJwt did not authenticate. + ...(fallbackAttempted && { + primary_failure_reason: primaryFailureReason, + primary_failure_error_name: primaryFailureErrorName, + }), reason: getAuthFailureReason(err, info), error_name: getAuthFailureErrorName(err, info), status: status || 401,