[Bug] v5.0.0 Web terminal SSH sessions are disconnected with "Access permission expired" – one-time connection token is expired on use but re-validated during the session
Product Version
v5.0.0
Product Edition
- Community Edition
- Enterprise Edition
- Enterprise Trial Edition
Installation Method
- Online Installation (One-click command installation)
- Offline Package Installation
- All-in-One
- 1Panel
- Kubernetes
- Source Code
Environment Information
OS: Debian13, Docker: 29.8.1, single host, all components in Docker (jms_core, jms_koko, jms_web, ...)
Bug Description
When connecting to an SSH asset via the Web Terminal, the session works normally, but after a short time the browser shows several warnings:
Permission expired: Connection Token Expired: 2026-09-29 09:50:18.268838+02:00After ~4 warnings KoKo terminates the session:
Permission has expired, disconnect
Koko ended the session: Access permission expiredThe asset permission and the user are NOT expired. Server time is NTP-synchronized; host, core and koko containers have identical time and TZ.
The database shows that every one-time connection token is expired ~0.2 s after creation:
select date_created, date_expired, date_expired - date_created as validity, is_reusable, protocol, account
from authentication_connectiontoken order by date_created desc limit 5;
date_created | date_expired | validity | is_reusable | protocol | account
2026-09-29 16:06:27.167795+08 | 2026-09-29 16:06:27.406117+08 | 00:00:00.238322 | f | ssh | @INPUT
2026-09-29 15:50:18.08012+08 | 2026-09-29 15:50:18.268838+08 | 00:00:00.188718 | f | ssh | @INPUT
2026-09-29 15:49:59.75259+08 | 2026-09-29 15:49:59.970414+08 | 00:00:00.217824 | f | ssh | @INPUTThe date_expired of the token matches exactly the timestamp in the "Connection Token Expired" warning.
Analysis
apps/authentication/api/connection_token.py– thesecretaction callstoken.expire()when KoKo consumes the token (unless reusable,expire_now=false, or asset type is k8s/kubernetes).- During the session KoKo periodically re-validates the token; this ends in
ConnectionToken.is_valid()(apps/authentication/models/connection_token.py), which raisesConnection token expired at: {}becauseis_expiredis now true. - The
renewalaction also rejects already-expired tokens, so the token cannot be renewed either.
So the periodic in-session check uses the same validation as the initial connection. For a running session it should check the asset permission (expire_at / permed_account.date_expired), not whether the intentionally consumed one-time token is still valid. There is already an exception for k8s (expire_now = False), but not for SSH/SFTP/Telnet.
Recurrence Steps
- Web Terminal → connect to any SSH asset (manual input account
@INPUT, not tested with saved accounts yet) - Work normally in the session
- After a short time "Connection Token Expired" warnings appear, then the session is terminated with "Access permission expired"
Expected Behavior
An active session should stay connected as long as the user's asset permission is valid. Consuming the one-time token at connect time should not terminate the running session.
Attempted Solutions
Workaround (temporary): extend the k8s exception to SSH/SFTP/Telnet in api/connection_token.py:
if asset_type in ['k8s', 'kubernetes'] or token.protocol in ('ssh', 'sftp', 'telnet'):
expire_now = Falseplus CONNECTION_TOKEN_ONETIME_EXPIRATION=28800 in config.txt. With this the sessions stay connected, but the one-time token remains valid until its natural expiry, which weakens the one-time semantics.
Suggested Fix
The workaround above weakens the one-time token semantics, so it should not be the final fix. Possible proper fixes (we did not inspect the KoKo side, so the exact endpoint used for the periodic check may differ):
- Separate the in-session check from the connect-time validation. For the periodic permission check of an already running session, do not reject the token because
is_expiredis true after consumption. Instead validate what actually matters for a running session: user is valid, asset is active, and the asset permission is still valid (expire_at/permed_account.date_expired). - Alternatively, allow
renewalfor tokens that belong to an active session, so KoKo can keep the session alive without the token being reusable for new connections. - As a stop-gap, the existing k8s exception (
expire_now = False) could be extended to long-lived protocols (ssh/sftp/telnet), but together with a shortCONNECTION_TOKEN_ONETIME_EXPIRATIONthis only moves the disconnect to the token's natural expiry.
Option 1 keeps the one-time token protection for new connections and fixes the disconnects of running sessions.
Expected Behavior
No response
Additional Information
No response
Attempted Solutions
No response
Source: jumpserver/jumpserver