ThinLinc 4.21.0 introduced native OpenID Connect (OIDC) authentication for ThinLinc Web Access. This allows you to use an external identity provider such as Keycloak to authenticate users before starting their Linux remote desktop sessions.
With this configuration, users open ThinLinc Web Access in their browser and click Sign in with Keycloak. ThinLinc redirects the browser to Keycloak, Keycloak authenticates the user, and ThinLinc maps the returned OIDC username claim to an existing Linux account.
Prerequisites
Before you begin, you need:
- Ubuntu Server 24.04 LTS.
- Root access or a sudo-enabled user.
- A public IPv4 address.
- DNS records for ThinLinc.
- TCP ports
22,80, and443open. - ThinLinc 4.21.0 or later.
Part I - Install ThinLinc Server
Step 1 - Update Ubuntu 24.04
Update the package index:
sudo apt update
Upgrade installed packages:
sudo apt upgrade -y
Install the required packages:
sudo apt install -y wget unzip curl openssh-server dnsutils nginx certbot python3-certbot-nginx
Step 2 - Configure the Server Hostname
Set the fully qualified hostname:
sudo hostnamectl set-hostname thinlinc.linuxbuz.com
Verify it:
hostnamectl
hostname -f
Expected output:
thinlinc.linuxbuz.com
Step 3 - Create the ThinLinc Administrator User
Create a Linux account:
sudo adduser thinadm
Add it to the sudo group:
sudo usermod -aG sudo thinadm
Verify the account:
getent passwd thinadm
Example output:
thinadm:x:1000:1000:,,,:/home/thinadm:/bin/bash
Switch to the new user:
su - thinadm
Verify sudo access:
sudo -v
Step 4 - Add ThinLinc Commands to sudo secure_path
Open the sudoers file safely:
sudo visudo
Find the Defaults secure_path line and add the ThinLinc paths:
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/opt/thinlinc/bin:/opt/thinlinc/sbin"
Save and exit.
Step 5 - Download ThinLinc Server 4.21.0
Download ThinLinc:
wget https://www.cendio.com/downloads/server/tl-4.21.0-server.zip
Extract it:
unzip tl-4.21.0-server.zip
Enter the extracted directory:
cd tl-4.21.0-server
Step 6 - Install ThinLinc Server
Run the installer:
sudo ./install-server
If GTK+ is unavailable, ThinLinc automatically falls back to text mode:
Could not start GTK+. Using text mode fallback...
Press Enter when prompted.
The installer detects the ThinLinc package:
thinlinc-server_4.21.0-4708_amd64.deb
Press Enter again to install it.
After installation, you should see:
Installation of ThinLinc completed!
When prompted:
Run ThinLinc setup now [Yes/no]?
Enter:
Yes
Step 7 - Complete ThinLinc Setup
Accept the license agreement:
Do you accept the terms of the license agreement [yes/No]? yes
Select the first server as the master:
Server type [Master/agent]? Master
Allow ThinLinc to analyze the system.
When prompted for NFS support, select:
Automatically install the necessary packages [Yes/no]? Yes
When prompted for Python LDAP support, select:
Automatically install the necessary packages [Yes/no]? Yes
For the externally reachable address, select:
hostname
ThinLinc should detect:
thinlinc.linuxbuz.com
Enter an administrator email address when requested.
If you do not need printer redirection:
Configure location-based printer queue (nearest) [Yes/no]? no
Configure client-side printer queue (thinlocal) [Yes/no]? no
Install the ThinLinc AppArmor configuration:
Install AppArmor configuration [Yes/no]? Yes
After setup finishes, reboot:
sudo reboot
Step 8 - Verify ThinLinc Services
After reboot, check:
sudo systemctl status vsmserver
sudo systemctl status vsmagent
sudo systemctl status tlwebaccess
Each enabled service should show:
Active: active (running)
Verify the agent hostname:
/opt/thinlinc/bin/tl-config /vsmagent/agent_hostname
Expected:
thinlinc.linuxbuz.com
If needed, set it explicitly:
sudo /opt/thinlinc/bin/tl-config /vsmagent/agent_hostname=thinlinc.linuxbuz.com
Restart the agent:
sudo systemctl restart vsmagent
Part II - Publish ThinLinc Through Nginx
Step 9 - Verify ThinLinc Web Access
ThinLinc Web Access normally listens on HTTPS port 300.
Check it:
sudo ss -lntp | grep :300
Step 10 - Configure Nginx for ThinLinc
Create the Nginx site:
sudo nano /etc/nginx/sites-available/thinlinc
Add:
server {
listen 80;
listen [::]:80;
server_name thinlinc.linuxbuz.com;
proxy_read_timeout 999h;
location / {
proxy_pass https://127.0.0.1:300/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /connect/thinlinc.linuxbuz.com/ {
proxy_pass https://127.0.0.1:300/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/thinlinc /etc/nginx/sites-enabled/thinlinc
Remove the default site:
sudo rm -f /etc/nginx/sites-enabled/default
Test Nginx:
sudo nginx -t
Reload Nginx:
sudo systemctl reload nginx
Step 11 - Configure ThinLinc for the Reverse Proxy
Set the public login page:
sudo /opt/thinlinc/bin/tl-config /webaccess/login_page=https://thinlinc.linuxbuz.com/
Trust the local Nginx proxy:
sudo /opt/thinlinc/bin/tl-config /webaccess/trusted_proxies=127.0.0.1
Restart Web Access:
sudo systemctl restart tlwebaccess
Step 12 - Configure the Firewall
Allow SSH:
sudo ufw allow OpenSSH
Allow HTTP:
sudo ufw allow 80/tcp
Allow HTTPS:
sudo ufw allow 443/tcp
Enable UFW:
sudo ufw enable
Check the rules:
sudo ufw status numbered
When Nginx handles browser access, port 300 does not need to be exposed publicly.
Step 13 - Install a TLS Certificate for ThinLinc
Request a Let’s Encrypt certificate:
sudo certbot --nginx -d thinlinc.linuxbuz.com
Enable HTTPS redirection when prompted.
Test renewal:
sudo certbot renew --dry-run
Open your web browser and verify the ThinLinc web access using the URL:
https://thinlinc.linuxbuz.com
The ThinLinc OIDC redirect URI will be:
https://thinlinc.linuxbuz.com/
Part III - Configure Keycloak for ThinLinc
Step 14 - Open the Keycloak Admin Console
Open the web browser and access your Keycloak Admin Console.
Log in with your admin username and password.
Step 15 - Create the Keycloak Realm
Click the realm selector and click Manage realms.
Click on Create realm.
Enter Realm name:
Realm name: thinlinc
Leave it enabled and click Create.
Step 16 - Create the ThinLinc User
Open Users from the Keycloak console.
Click Create new user.
Set:
Username: thinadm
Optionally enter:
Email
First name
Last name
For Required user actions, leave the field empty for the initial setup.
Set:
Email verified: Off
unless you are actually using email verification.
Click Create.
Set the User Password
Go to Users > thinadm > Credentials tab.
Click Set password.
Enter a strong password, set: “Temporary: Off” and click Save to save the password.
For this tutorial:
Keycloak username = thinadm
Linux username = thinadm
Step 17 - Create the ThinLinc OIDC Client
On the Keycloak console, click Client > Create client.
Configure the following values:
Client type: OpenID Connect
Client ID: thinlinc-webaccess
Name: ThinLinc Web Access
Click Next.
Step 18 - Configure the Authentication Flow
Click Capability config and set the following settings:
Client authentication: On
Standard flow: On
Disable options that are not required:
Direct access grants: Off
Implicit flow: Off
Service accounts roles: Off
Authorization: Off
Click Next.
Step 19 - Configure the Redirect URI
Provide your ThinLinc URLs:
Root URL:https://thinlinc.linuxbuz.com/
Home URL:https://thinlinc.linuxbuz.com/
Valid redirect URIs: https://thinlinc.linuxbuz.com/
For this server-side OIDC flow, Web origins can normally remain empty.
Click Save to the client.
Step 20 - Copy the Keycloak Client Secret
Open Clients > thinlinc-webaccess > Credentials
Copy the generated Client secret and save it for later use.
You now have:
Client ID:thinlinc-webaccess
Client Secret:<GENERATED_SECRET>
Do not publish or paste the client secret into documentation.
Step 21 - Verify the Keycloak Realm Endpoint
Open the URL below in your web browser to verify the Keycloak realm endpoint.
https://your-keycloak-domain/realms/thinlinc/
A response similar to the following is normal:
realm = "thinlinc"
token-service = "https://your-keycloak-domain/realms/thinlinc/protocol/openid-connect"
account-service = "https://your-keycloak-domain/realms/thinlinc/account"
This confirms that the realm exists.
However, this is not the OIDC discovery endpoint.
Step 22 - Verify the OIDC Discovery URL
The correct discovery URL is:
https://your-keycloak-domain/realms/thinlinc/.well-known/openid-configuration
Test it:
curl -fsSL 'https://your-keycloak-domain/realms/thinlinc/.well-known/openid-configuration' | python3 -m json.tool | head -40
You should see values including:
issuer
authorization_endpoint
token_endpoint
userinfo_endpoint
jwks_uri
The issuer should be:
https://your-keycloak-domain/realms/thinlinc
The JWKS endpoint should be similar to:
https://your-keycloak-domain/realms/thinlinc/protocol/openid-connect/certs
Test it:
curl -fsSL 'https://your-keycloak-domain/realms/thinlinc/protocol/openid-connect/certs' | python3 -m json.tool | head -30
Part IV - Configure ThinLinc OIDC from the Command Line
Step 23 - Store the Keycloak Client Secret
Follow the steps below on the ThinLinc server.
Create a protected directory:
sudo install -d -m 700 -o root -g root /opt/thinlinc/etc/tlwebaccess
Read the client secret into a temporary shell variable:
read -rsp 'Paste Keycloak client secret: ' SECRET
echo
Paste the Keycloak client secret and press Enter.
Write it to the file:
printf '%s' "$SECRET" | sudo tee /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt >/dev/null
Clear the variable:
unset SECRET
Set ownership:
sudo chown root:root /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
Set secure permissions:
sudo chmod 600 /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
Verify:
sudo ls -l /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
Expected permissions:
-rw------- 1 root root ... keycloak_secret.txt
Important: Do not leave this file at mode
644. The client secret must not be readable by other users.
Step 24 - Check the Secret File Safely
You can verify that the file does not contain unwanted whitespace without printing the secret:
sudo python3 - <<'PY'
p = "/opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt"
data = open(p, "rb").read()
print("Length:", len(data))
print("Ends with newline:", data.endswith(b"\n"))
print("Ends with CRLF:", data.endswith(b"\r\n"))
print("Leading whitespace:", len(data) != len(data.lstrip()))
print("Trailing whitespace:", len(data) != len(data.rstrip()))
PY
Ideally:
Ends with newline: False
Ends with CRLF: False
Leading whitespace: False
Trailing whitespace: False
Step 25 - Configure the Keycloak OIDC Provider in ThinLinc
Set the username claim:
sudo /opt/thinlinc/bin/tl-config /webaccess/oidc/keycloak/username_claim=preferred_username
Set the Keycloak client ID:
sudo /opt/thinlinc/bin/tl-config /webaccess/oidc/keycloak/client_id=thinlinc-webaccess
Set the client-secret file:
sudo /opt/thinlinc/bin/tl-config /webaccess/oidc/keycloak/client_secret_path=/opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
Set the Keycloak discovery URL:
sudo /opt/thinlinc/bin/tl-config '/webaccess/oidc/keycloak/discovery_url=https://your-keycloak-domain/realms/thinlinc/.well-known/openid-configuration'
Add the profile scope:
sudo /opt/thinlinc/bin/tl-config /webaccess/oidc/keycloak/scope=profile
Set a friendly login button:
sudo /opt/thinlinc/bin/tl-config '/webaccess/oidc/keycloak/button_text=Sign in with Keycloak'
Step 26 - Verify the ThinLinc OIDC Configuration
Run:
sudo /opt/thinlinc/bin/tl-config -Ra /webaccess/oidc/keycloak
Expected output:
button_text = Sign in with Keycloak
scope = profile
discovery_url = https://your-keycloak-domain/realms/thinlinc/.well-known/openid-configuration
client_secret_path = /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
client_id = thinlinc-webaccess
username_claim = preferred_username
The important parameters are:
username_claim
discovery_url
client_id
client_secret_path
Step 27 - Restart ThinLinc Web Access
Restart:
sudo systemctl restart tlwebaccess
Check status:
sudo systemctl status tlwebaccess --no-pager
Expected:
Active: active (running)
Part V - Test Keycloak Authentication
Step 28 - Open ThinLinc Web Access
Open a private or incognito browser window and browse to https://thinlinc.linuxbuz.com/.
Click on Sign in with Keycloak, and the browser should redirect to the Keycloak URL https://your-keycloak-domain/realms/thinlinc/.
Enter the Keycloak credentials below and click Sign in.
Username: thinadm
Password: <Keycloak password>
Keycloak authenticates the user and redirects the browser back to https://thinlinc.linuxbuz.com/.
ThinLinc receives:
preferred_username=thinadm
It then resolves:
getent passwd thinadm
If the account exists and PAM permits access, ThinLinc starts the remote desktop session.
Part VI - Optional: Disable ThinLinc Password Login
After Keycloak authentication works reliably, you can disable the normal ThinLinc Web Access password form.
Run:
sudo /opt/thinlinc/bin/tl-config /webaccess/login/password=false
Restart:
sudo systemctl restart tlwebaccess
To restore password login later:
sudo /opt/thinlinc/bin/tl-config /webaccess/login/password=true
Restart again:
sudo systemctl restart tlwebaccess
Part VII - Troubleshooting
External Authentication Failed, Try Again
If ThinLinc displays:
External authentication failed, try again.
first verify the OIDC discovery endpoint:
curl -fsSL 'https://your-keycloak-domain/realms/thinlinc/.well-known/openid-configuration' | python3 -m json.tool | head -40
Then verify ThinLinc:
sudo /opt/thinlinc/bin/tl-config -Ra /webaccess/oidc/keycloak
Expected:
button_text = Sign in with Keycloak
scope = profile
discovery_url = https://your-keycloak-domain/realms/thinlinc/.well-known/openid-configuration
client_secret_path = /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
client_id = thinlinc-webaccess
username_claim = preferred_username
Check the client-secret file:
sudo ls -l /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
Expected:
-rw------- 1 root root ...
Check that it is not empty:
sudo wc -c /opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
If there is any doubt, copy the current secret again from:
Keycloak > Clients > thinlinc-webaccess > Credentials
and recreate the file.
Capture the ThinLinc OIDC Error
Monitor:
sudo tail -f /var/log/tlwebaccess.log
If needed:
sudo journalctl -u tlwebaccess -f
Reproduce the login failure in an incognito browser window.
You can also run:
sudo grep -Ei 'oidc|error|warning|invalid|claim|authentication' /var/log/tlwebaccess.log | tail -30
Common failures include:
invalid_client
Failed to exchange authorization code
preferred_username claim missing
Could not resolve user
Do not share client secrets, authorization codes, access tokens, or ID tokens.
Invalid Client Credentials
Verify:
sudo /opt/thinlinc/bin/tl-config /webaccess/oidc/keycloak/client_id
Expected:
thinlinc-webaccess
In Keycloak, verify:
Client authentication: On
If you regenerate the Keycloak client secret, you must update:
/opt/thinlinc/etc/tlwebaccess/keycloak_secret.txt
Then restart:
sudo systemctl restart tlwebaccess
Keycloak Login Works but ThinLinc Rejects the User
Check:
getent passwd thinadm
Check:
id thinadm
The mapping must be:
preferred_username=thinadm
Linux username=thinadm
If they do not match, ThinLinc cannot map the authenticated identity to the Linux user.
Keycloak Username and Linux Username Are Different
Example:
Keycloak username: john@example.com
Linux username: john
In that case, preferred_username will not map directly.
Create an administrator-controlled Keycloak claim such as:
linux_username
with:
john
Then configure ThinLinc:
sudo /opt/thinlinc/bin/tl-config /webaccess/oidc/keycloak/username_claim=linux_username
Restart:
sudo systemctl restart tlwebaccess
Do not use a user-editable claim for Linux account mapping.
Sign in With Keycloak Button Does Not Appear
Check:
sudo /opt/thinlinc/bin/tl-config -Ra /webaccess/oidc/keycloak
Verify:
client_id
client_secret_path
discovery_url
username_claim
Restart:
sudo systemctl restart tlwebaccess
Monitor:
sudo journalctl -u tlwebaccess -f
ThinLinc Desktop Does Not Start After Authentication
OIDC authentication and Linux account authorization are separate.
Check:
id thinadm
Check password/account status:
sudo passwd -S thinadm
Review ThinLinc logs:
sudo journalctl -u tlwebaccess -f
Review VSM Server logs:
sudo journalctl -u vsmserver -f
Review VSM Agent logs:
sudo journalctl -u vsmagent -f
Conclusion
You now have a complete Keycloak and ThinLinc OIDC environment for browser-based Linux remote desktop access.














