Skip to content

fix(token-providers): fromSso 30s refresh throttle leaks across unrelated SSO sessions - #8267

Open
Adityaj0 wants to merge 1 commit into
aws:mainfrom
Adityaj0:fix-fromsso-cross-session-throttle
Open

fix(token-providers): fromSso 30s refresh throttle leaks across unrelated SSO sessions#8267
Adityaj0 wants to merge 1 commit into
aws:mainfrom
Adityaj0:fix-fromsso-cross-session-throttle

Conversation

@Adityaj0

@Adityaj0 Adityaj0 commented Aug 15, 2026

Copy link
Copy Markdown

Motivation and Context

fixes #8266

fromSso's 30-second "don't hammer the OIDC refresh endpoint" throttle uses a single Date declared at module scope:

const lastRefreshAttemptTime = new Date(0);
...
if (Date.now() - lastRefreshAttemptTime.getTime() < 30 * 1000) {
  validateTokenExpiry(existingToken);
  return existingToken;
}
...
lastRefreshAttemptTime.setTime(Date.now());

This timestamp is shared by every fromSso provider in the process, regardless of profile/sso_session. Refreshing profile A's expired token sets it; if profile B — an unrelated profile on a different sso_session, also independently expired — is resolved within the next 30 seconds, its refresh is silently skipped and it falls through to validateTokenExpiry() on its own still-stale token, throwing Token is expired even though B was never actually rate-limited.

Description

Replaced the single shared Date with a Map<string, number> keyed by ssoSessionName:

const lastRefreshAttemptTime = new Map<string, number>();
...
const lastRefreshAttempt = lastRefreshAttemptTime.get(ssoSessionName) ?? 0;
if (Date.now() - lastRefreshAttempt < 30 * 1000) {
  validateTokenExpiry(existingToken);
  return existingToken;
}
...
lastRefreshAttemptTime.set(ssoSessionName, Date.now());

This preserves the original intent (don't hammer refresh calls for the same session within 30 seconds — including across multiple fromSso() instances pointed at the same session, which is correct and desirable) while no longer blocking unrelated sessions from refreshing in the same window.

Testing

Added does not skip refresh for a different profile/sso_session within the same 30 seconds to fromSso.spec.ts, exercising two profiles (different sso_sessions, both independently expired) against a single imported fromSso module (no vi.resetModules() between the two calls, matching real single-process usage). Confirmed it fails against the pre-fix code (git stash the fix, rerun — the second profile incorrectly returns its stale unrefreshed token instead of a real refresh) and passes with the fix.

$ yarn g:vitest run src/fromSso.spec.ts
Test Files  1 passed (1)
     Tests  23 passed (23)

Also independently verified against the real, byte-for-byte source (only import specifiers redirected to stub dependency files) with a standalone driver simulating two profiles/sessions:

=== before fix ===
profileB THREW: Token is expired. ...
getNewSsoOidcToken calls so far: [ 'tokenA' ]

=== after fix ===
profileB result: { token: 'tokenB-REFRESHED', ... }
getNewSsoOidcToken calls so far: [ 'tokenA', 'tokenB' ]

And confirmed the original same-session throttle behavior is unchanged (a second call for the same session within 30s still returns the existing token / throws if it's expired, without triggering a duplicate refresh call).

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • My code follows the code style of this project
  • My change requires a change to the documentation
  • I have read the CONTRIBUTING document
  • I have added tests to cover my changes
  • All new and existing tests passed

…ated SSO sessions

lastRefreshAttemptTime was a single Date at module scope, shared by every
fromSso() provider instance in the process regardless of which profile or
sso_session it targets. Refreshing one profile's expired token set this
global timestamp, so refreshing a completely unrelated, independently
expired profile/session within the next 30 seconds was silently skipped
-- the code fell through to validateTokenExpiry() on the still-stale
token and threw "Token is expired", even though that session had never
actually been rate-limited and no refresh attempt had been made for it.

Key the throttle by sso_session name instead of using one shared
timestamp, so it still protects a single session from being hammered
with refresh calls but no longer blocks unrelated sessions from
refreshing in the same window.
@Adityaj0
Adityaj0 requested a review from a team as a code owner August 15, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

token-providers: fromSso's 30-second refresh throttle is global, causing spurious "Token is expired" errors across unrelated SSO profiles

1 participant