Fix 2FA shutdown timer terminating a healthy logged-in session - #376
Open
chapinmark wants to merge 1 commit into
Open
Fix 2FA shutdown timer terminating a healthy logged-in session#376chapinmark wants to merge 1 commit into
chapinmark wants to merge 1 commit into
Conversation
Two independent defects in LoginManager can combine to terminate a
Gateway session that has successfully logged in and is serving the API.
1. restartAfterTime() assigns shutdownAfterTimeTask without cancelling
the ScheduledFuture already held there. setLoginState(LOGGED_IN)
cancels only whatever the field currently references, so any earlier
task is orphaned: still scheduled, but no longer reachable by the
handler meant to cancel it.
Every start arms two of these. The SecondFactorDevice selection
dialog closes first ("Duration since login: 1 seconds") and arms one;
the real 2FA dialog closes a few seconds later and arms a second,
overwriting the field. One orphan therefore survives every session.
2. The re-login scheduled after a 2FA timeout runs unconditionally five
seconds later. TWS/Gateway sometimes closes the 2FA dialog right on
the timeout boundary and then completes the login anyway, so the
session can already be LOGGED_IN when that task fires. Re-initiating
the login takes it back out of LOGGED_IN, and it never returns.
Together they end a working session. Observed with Gateway 1045 and
IBC 3.23.0, SecondFactorAuthenticationExitInterval=3700:
15:38:37.697 device dialog closed, orphan timer armed (deadline 16:40:17)
16:01:24 2FA approved; 16:01:27 LOGGED_IN, API port 4002 open
16:07:15 IB forces "Re-login is required"; new 2FA prompt
16:10:16.759 dialog closed at exactly 180s -> re-login scheduled in 5s
16:10:16.868 session reaches LOGGED_IN on its own, 109ms later
16:10:21 the queued re-login fires anyway, state leaves LOGGED_IN
16:40:17.699 orphan timer fires, state != LOGGED_IN, exit 1111
The Gateway process was alive and serving the API throughout: its log
was still being written at 16:40:17, and the socat helper logged no
connection failures after 16:01:27.
Fix both: cancel any outstanding shutdown task before scheduling a new
one, and guard the deferred re-login on LOGGED_IN, mirroring the guard
restartAfterTime already applies in its own scheduled task. Either
change alone prevents the failure above; both are included because each
is a defect in its own right.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two defects in
LoginManagercan combine to terminate a Gateway session that has logged in successfully and is serving the API.1.
restartAfterTime()orphans the previous shutdown taskIt assigns
shutdownAfterTimeTaskwithout cancelling theScheduledFuturealready held there.setLoginState(LOGGED_IN)cancels only whatever the field currently references, so any earlier task is orphaned — still scheduled, but no longer reachable by the handler meant to cancel it.Every start arms two of these. The
SecondFactorDeviceselection dialog closes first (Duration since login: 1 seconds) and arms one; the real 2FA dialog closes a few seconds later and arms a second, overwriting the field. One orphan therefore survives every session, and firesSecondFactorAuthenticationExitIntervalseconds after login began regardless of what happened since.2. The re-login queued after a 2FA timeout has no
LOGGED_INguardIt runs unconditionally five seconds later. TWS/Gateway sometimes closes the 2FA dialog right on the timeout boundary and then completes the login anyway, so the session can already be
LOGGED_INwhen the task fires. Re-initiating the login takes it back out ofLOGGED_IN, and it never returns.Observed failure
Gateway 1045, IBC 3.23.0,
SecondFactorAuthenticationExitInterval=3700,SecondFactorAuthenticationTimeout=180,ReloginAfterSecondFactorAuthenticationTimeout=yes:The exit is 3700.002s after the orphan was armed. The Gateway process was alive and serving throughout — its own log was still being written at 16:40:17, and the
socathelper forwarding 4002 logged no connection failures after 16:01:27. The container then exited 87 (1111 % 256) and its orchestrator restarted it.The change
shutdownAfterTimeTaskbefore scheduling a replacement, so only one is ever pending andLOGGED_INcan always cancel it.LOGGED_IN, mirroring the guardrestartAfterTimealready applies inside its own scheduled task.Either change alone prevents the failure above; both are included because each is a defect independently. No settings change, no new configuration, and no effect on installs where 2FA is approved promptly — in that case the first dialog's orphan simply finds
LOGGED_INand returns, as it does today.Compiled clean against the full tree with
ant dist(IBC_BINpointing at Gateway 1045 jars) and verified in the resultingIBC.jarbytecode.I appreciate this arrives close to retirement, and I'll understand entirely if you'd rather not take further changes — posting it mainly so the analysis is on record for anyone who hits the same thing.