🔍 refactor: Surface primary OpenID JWT failure reason in auth failure log (#14346)

When OPENID_REUSE_TOKENS is enabled and both the openidJwt strategy and the
HS256 jwt fallback fail, the final 'Authentication failed after all strategies'
warn log reported only the fallback's reason. For an RS256 provider (Keycloak,
Auth0, Okta) that surfaces as 'invalid algorithm', which is the HS256 fallback
rejecting the provider token, not the real reason openidJwt did not
authenticate, and it was previously only visible at debug level.

Include the captured primary (openidJwt) failure reason and error name in the
final warn log so reused-token failures are diagnosable without enabling debug
and are not misattributed to the fallback.

Refs #14311
This commit is contained in:
Danny Avila 2026-07-20 21:05:20 -04:00 committed by GitHub
parent d5e8c5c15e
commit d02867a3e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 13 additions and 0 deletions

View file

@ -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"');
});

View file

@ -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,