Server Tokens
This article explains how to recognize service disruptions caused by auth token desync between Kasm components, as well as how to fix potential issues in a deployed system.
Background
Starting with version 1.18 of Kasm, a new token refresh workflow has been introduced to enhance security and simplify maintenance.
Under normal conditions tokens automatically refresh without user intervention; however, some scenarios might prevent components from refreshing their tokens in time—such as extended maintenance windows or infrastructure powered down when not in use.
Kasm settings include a grace period option that allows a limited time after token expiration for exchanging it with a new token. During this leeway period the expired token cannot authorize service calls; it can only be exchanged for a new token.
When a token expires the client can re-establish trust with the Admin API by providing the same registration token used during the initial deployment. In a non-Kubernetes environment this registration token should be placed in a file named registration_token.txt
which is automatically deleted after use. In contrast, in a Kubernetes deployment the registration token can be set as an environment variable from a Secret which allows the service to be fully self-healing.
The following flowchart illustrates the component logic when refreshing a token.
Note that in the case where the client does not have an existing token at all, the service will need to be redeployed as if it is a new component.
Recognizing authentication token issues
Administrators can determine whether Kasm services have lost authentication with the Admin API by looking either at the Kasm administrative logs or by examining console output from the component in question.
Some messages you might see when a component has an expired token include The JWT token has expired
and Expired JWT utilized on register_component
.
The following screenshot shows a message indicating the RDP Proxy component of a single server installation has lost authentication:
The following console output comes from that same RDP Proxy which was turned off long enough to miss the token refresh window:
Jul 23 18:47:04 INFO (7/7) -- ReDemPtion 12.0.11 starting
Jul 23 18:47:04 INFO (7/7) -- create_ip_dual_stack_server: binding socket 4 on [::]:3389
Jul 23 18:47:04 INFO (7/7) -- create_server: listening on socket 4
Jul 23 18:47:04 INFO (7/7) -- Reading font file /usr/local/share/rdpproxy/dejavu_14.rbf2
Jul 23 18:47:04 INFO (7/7) -- Font: version: 1 name: 'DejaVuSans,NotoSansCJK-Regular' size: 14 style: 1 max_ascent: 14 max_descent: 4 nbglyph: 59914 unicode_max = 195102 total_data: 1138296
2025-07-23 18:47:05,188 [INFO] __main__.handler: Refreshing auth token which expires 2025-07-23 18:45:47
2025-07-23 18:47:05,188 [INFO] __main__.handler: Current list of API servers (['proxy', 'dev-kasm'])
2025-07-23 18:47:05,226 [DEBUG] __main__.handler: Health check return: {'ok': True}
2025-07-23 18:47:05,274 [ERROR] __main__.handler: Error from Kasm server for api: https://proxy:443/api/admin/refresh_token status: 403 error: No response
2025-07-23 18:47:05,275 [ERROR] __main__.handler: Failed to refresh auth token
2025-07-23 18:47:05,276 [INFO] __main__.handler: Registering rdp-gateway with configuration: {'target_component': {'type': 'connection_proxy', 'connection_proxy_type': 'RDP-GATEWAY', 'server_address': 'proxy', 'server_port': 443, 'zone_name': 'default', 'proxy_port': 3389, 'status': 'running', 'id': 'd7c9927661a948a692634a96cc323811'}, 'token': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb25uZWN0aW9uX3Byb3h5X2lkIjoiZDdjOTkyNzYtNjFhOS00OGE2LTkyNjMtNGE5NmNjMzIzODExIiwiZXhwIjoxNzUzMjk2Mzc3LCJhdXRob3JpemF0aW9ucyI6WzkwXX0.oy9VFBn5ObL2POWD9oC4td5e09hLpsqTqnfR72GaxgOJV0-hmU-ftr4Mxk_Q3vSfWgvg6BVUM_y0raDkhGWYMwoZUwYDhVSgPgvwgXIwucaspLSwlsTT2FJjKQCZknv_MqO9Rk9iobksUWJJAuUWrDRlU8lMQbN_W3SZcc_n8szUMnFGBsgJXfADy0SlEtL9Cuni1tT5n3hh9Cr1zhwnW1ygIfaZuRKODz8wLy0Ev_a93Jm15Ryk2s6dUZwqM3u8RJOMrYKtLmQgcsljMQFmf4DEjMr3x96xk1dHYW975_A8JzgsMqaX6G3aU-vNr51T0BnPBQ8IL5zHqVCBouuGrQ'}
2025-07-23 18:47:05,277 [INFO] __main__.handler: Attempt 1 /10 attempts
2025-07-23 18:47:05,277 [INFO] __main__.handler: Current list of API servers (['proxy', 'dev-kasm'])
2025-07-23 18:47:05,309 [DEBUG] __main__.handler: Health check return: {'ok': True}
2025-07-23 18:47:05,344 [ERROR] __main__.handler: Error from Kasm server for api: https://proxy:443/api/admin/register_component status: 200 error: The JWT token has expired.
2025-07-23 18:47:05,344 [WARNING] __main__.handler: Failed to register component on attempt: 1
2025-07-23 18:47:08,345 [INFO] __main__.handler: Attempt 2 /10 attempts
2025-07-23 18:47:08,346 [INFO] __main__.handler: Current list of API servers (['proxy', 'dev-kasm'])
2025-07-23 18:47:08,377 [DEBUG] __main__.handler: Health check return: {'ok': True}
2025-07-23 18:47:08,418 [ERROR] __main__.handler: Error from Kasm server for api: https://proxy:443/api/admin/register_component status: 200 error: The JWT token has expired.
2025-07-23 18:47:08,420 [WARNING] __main__.handler: Failed to register component on attempt: 2
2025-07-23 18:47:08,420 [INFO] __main__.handler: Attempt 3 /10 attempts
2025-07-23 18:47:08,421 [INFO] __main__.handler: Current list of API servers (['proxy', 'dev-kasm'])
2025-07-23 18:47:08,457 [DEBUG] __main__.handler: Health check return: {'ok': True}
2025-07-23 18:47:08,492 [ERROR] __main__.handler: Error from Kasm server for api: https://proxy:443/api/admin/register_component status: 200 error: The JWT token has expired.
Recovering a component
When a component has an expired token it can be re-registered by placing a text file named registration_token.txt
into the appropriate directory with contents of the current Component Registration Token.
Here is an example of what the text file should contain (trailing newlines are automatically ignored and should not cause issues):
MjAyNzk0MmUtZjQzMS00NDZlLWFkODMtOGU2OWVmMzk3MGQx
Each component uses a different named file to store configuration, but the registration_token.txt
file MUST have that exact name, and then be placed alongside the correct config file.
The following list shows standard config file paths for each component on the host machine (mounted into the service containers based on a standard single-server installation):
- Agent:
/opt/kasm/current/conf/app/agent/agent.app.config.yaml
- Guac:
/opt/kasm/current/conf/app/guac/kasmguac.app.config.yaml
- RDP Gateway:
/opt/kasm/current/conf/app/rdp_gateway/passthrough.app.config.yaml
- RDP HTTPS Gateway:
/opt/kasm/current/conf/app/rdp_https_gateway/rdp_https_gateway.app.config.yaml
- Windows Server:
C:\Program Files\Kasm\config.yaml
In this configuration, you would recover the RDP Gateway by creating the registration token file at /opt/kasm/current/conf/app/rdp_gateway/registration_token.txt
.
The service does not need to be restarted, as it should automatically detect this new file when it makes another attempt to refresh the expired token (typically within 30 seconds, based upon the default configured heartbeat interval).
When the token is successfully refreshed, you will see the following output:
2025-07-23 20:09:26,773 [INFO] __main__.handler: Refreshing auth token which expires 2025-07-23 18:45:47
2025-07-23 20:09:26,774 [DEBUG] __main__.handler: Loaded registration token from /usr/local/etc/rdpproxy/conf/registration_token.txt
2025-07-23 20:09:26,775 [INFO] __main__.handler: Current list of API servers (['proxy', 'dev-kasm'])
2025-07-23 20:09:26,809 [DEBUG] __main__.handler: Health check return: {'ok': True}
2025-07-23 20:09:26,886 [INFO] __main__.handler: New auth token expires 2025-07-23 20:11:27
2025-07-23 20:09:26,890 [DEBUG] __main__.handler: Saved configuration changes.
2025-07-23 20:09:26,891 [INFO] __main__.handler: Successfully removed registration token file at /usr/local/etc/rdpproxy/conf/registration_token.txt
2025-07-23 20:09:26,892 [INFO] __main__.handler: Registering rdp-gateway with configuration: {'target_component': {'type': 'connection_proxy', 'connection_proxy_type': 'RDP-GATEWAY', 'server_address': 'proxy', 'server_port': 443, 'zone_name': 'default', 'proxy_port': 3389, 'status': 'running', 'id': 'd7c9927661a948a692634a96cc323811'}, 'token': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb25uZWN0aW9uX3Byb3h5X2lkIjoiZDdjOTkyNzYtNjFhOS00OGE2LTkyNjMtNGE5NmNjMzIzODExIiwiZXhwIjoxNzUzMzAxNDg2LCJhdXRob3JpemF0aW9ucyI6WzkwXX0.ZSflAx1_8At2DnJYtbHcClu2CUI4QveJK3h_zzwSIFyGT51BfB7WGiRqDVv_VBkcI4xGd9IHVCUJ4_SCoBIDGMlvjbpxfF7Em7immwqhRxIlyAyhHpjZpMNthLf-OuQJjgebzXjMA3hYlCNOUWK5PWRcR159UYJPVTzVCTTWBfm_QxoFejkgRXHXAYXHQEMBaXjFmBtTK3Hr0LDlkjJ_axKrraRWwyyhOTmmWcpIiiD06TYkDT3HB0UDbz21QEOG0Fw71L3HPb5VZYr4VYnW-Fhswr4pJ3VbILsPpmeOjyHuF1ReNpp35b1qW_vTQxYqggc4bH-zOqiG3W24hTgwTg'}
2025-07-23 20:09:26,892 [INFO] __main__.handler: Attempt 1 /10 attempts
2025-07-23 20:09:26,893 [INFO] __main__.handler: Current list of API servers (['proxy', 'dev-kasm'])
2025-07-23 20:09:26,923 [DEBUG] __main__.handler: Health check return: {'ok': True}
2025-07-23 20:09:27,006 [DEBUG] __main__.handler: Saved configuration changes.
2025-07-23 20:09:27,007 [INFO] __main__.handler: Component registration successful
The following lines (from the above snippet) show that the component utilized our registration token and then cleaned up automatically:
...
2025-07-23 20:09:26,774 [DEBUG] __main__.handler: Loaded registration token from /usr/local/etc/rdpproxy/conf/registration_token.txt
...
2025-07-23 20:09:26,891 [INFO] __main__.handler: Successfully removed registration token file at /usr/local/etc/rdpproxy/conf/registration_token.txt
Windows desktop agents may not automatically recover after creating the registration_token.txt
file, so you may need to manually restart the service.