OIDC login fails with "Internal error" at /api/oidc_callback (HTTP 500) when the identity provider returns the granted OAuth scopes in a different order than Kasm requested — even though the scope set is identical.
Kasm's OIDC handler uses requests-oauthlib/oauthlib. In oidc_callback → process_callback → oauth2_session.fetch_token, oauthlib's validate_token_parameters() compares requested vs. returned scope order-sensitively and raises Warning: Scope has changed from "openid profile email groups" to "profile groups openid email". Kasm does not catch this warning, so it propagates as an unhandled exception and the callback returns 500.
Critically, the token exchange itself succeeds — the IdP returns HTTP 200 on POST /token with valid tokens. The failure is entirely on the Kasm side, in oauthlib's scope comparison, one step after a successful exchange. It is not a TLS, certificate, client-secret, JWKS/signature, or claims issue.
This surfaced when the identity provider (PocketID) was upgraded into its v2 line, which rewrote its OAuth/OIDC engine onto ory/fosite. Fosite returns the granted scopes in a fixed internal order that differs from the request order, which is what trips oauthlib. No Kasm or IdP configuration values were changed; the IdP version bump alone triggered it. Upgrading Kasm 1.18.1 → 1.19.0 did not resolve it, since the fault is in the client library's reaction to the response, not Kasm's version.
Relevant traceback (from kasm_api):
File "api_server/client_api.py", line 851, in oidc_callback
File "api_server/authentication/oidc/__init__.py", line 24, in process_callback
File ".../requests_oauthlib/oauth2_session.py", line 406, in fetch_token
File ".../oauthlib/oauth2/rfc6749/parameters.py", line 473, in validate_token_parameters
raise w
Warning: Scope has changed from "openid profile email groups" to "profile groups openid email".
To Reproduce
Configure an OIDC provider whose token endpoint returns granted scopes in a different order than requested (e.g. PocketID v2 / any ory/fosite-based provider). Kasm requests openid profile email groups.
Attempt to log in via the OIDC provider from the Kasm login page.
Authenticate successfully at the IdP (IdP logs show 200 on POST /token).
Browser is redirected to /api/oidc_callback → Kasm returns HTTP 500, "Internal error".
Expected behavior
Kasm should treat an identical scope set returned in a different order as equivalent and complete the login, rather than raising on the reordering.
Screenshots
N/A — generic "Internal error" page; the actionable detail is the kasm_api traceback above.
Workspaces Version
Version 1.19.0 (issue also present on 1.18.1; upgrade did not resolve).
Workspaces Installation Method
linuxserver.io (Docker-in-Docker image), running on Unraid.
Client Browser (please complete the following information):
Reproduced across multiple browsers (provider-independent):
OS: macOS, WIndows
Browser: safari, chrome, edge
Version: N/A
Workspace Server Information:
Note: on the linuxserver.io image these must be run against the inner Docker (prefix with docker exec ...). Paste your real output for each.
uname -a
docker exec kasm uname -a
Linux 9adf74987206 6.18.33-Unraid #1 SMP PREEMPT_DYNAMIC Mon May 25 10:59:51 PDT 2026 x86_64 x86_64 x86_64 GNU/Linux
cat /etc/os-release
docker exec kasm cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.4 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
sudo docker info
docker exec kasm sudo docker info
Client: Docker Engine - Community
Version: 29.6.1
Context: default
Debug Mode: false
Plugins:
compose: Docker Compose (Docker Inc.)
Version: v5.3.1
Path: /usr/libexec/docker/cli-plugins/docker-compose
Server:
Containers: 8
Running: 8
Paused: 0
Stopped: 0
Images: 48
Server Version: 29.6.1
Storage Driver: fuse-overlayfs
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 2
Plugins:
Volume: local
Network: bridge host ipvlan kasmweb/sidecar:amd64-1.5-rolling-daily macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
CDI spec directories:
/etc/cdi
/var/run/cdi
Swarm: inactive
Runtimes: io.containerd.runc.v2 nvidia runc
Default Runtime: runc
Init Binary: docker-init
containerd version: e53c7c1516c3b2bff98eb76f1f4117477e6f4e66
runc version: v1.3.6-0-g491b69ba
init version: de40ad0
Security Options:
seccomp
Profile: builtin
cgroupns
Kernel Version: 6.18.33-Unraid
Operating System: Ubuntu 24.04.4 LTS (containerized)
OSType: linux
Architecture: x86_64
CPUs: 12
Total Memory: 31.27GiB
Name: 9adf74987206
ID: 61b7ee76-715c-4003-90d6-d070fe89b25e
Docker Root Dir: /opt/docker
Debug Mode: false
Experimental: false
Insecure Registries:
::1/128
127.0.0.0/8
Live Restore Enabled: false
Firewall Backend: iptables
EnableUserlandProxy: true
UserlandProxyPath: /usr/bin/docker-proxy
sudo docker ps | grep kasm
docker exec kasm sudo docker ps | grep kasm
88570112fd94 kasmweb/api:1.19.0-rolling "/bin/sh -c /usr/bin…" 20 minutes ago Up 20 minutes (healthy) 8080/tcp kasm_api
c3b46b17b345 kasmweb/postgres:1.19.0-rolling "docker-entrypoint.s…" 26 minutes ago Up 20 minutes (healthy) 5432/tcp kasm_db
dd6a21f38c47 kasmweb/rdp-https-gateway:1.19.0-rolling "/usr/bin/entrypoint…" About an hour ago Up 19 minutes (healthy) kasm_rdp_https_gateway
2c1d809b2b9a kasmweb/proxy:1.19.0-rolling "/docker-entrypoint.…" About an hour ago Up 19 minutes 80/tcp, 0.0.0.0:6333->6333/tcp, [::]:6333->6333/tcp kasm_proxy
fe8ee229ca4e kasmweb/rdp-gateway:1.19.0-rolling "/start.sh" About an hour ago Up 18 minutes (healthy) 0.0.0.0:3389->3389/tcp, [::]:3389->3389/tcp kasm_rdp_gateway
d7c8e1976b81 kasmweb/agent:1.19.0-rolling "python3 /src/Provis…" About an hour ago Up 20 minutes (healthy) 4444/tcp kasm_agent
f74748ed1fe8 kasmweb/manager:1.19.0-rolling "python3 /src/api_se…" About an hour ago Up 20 minutes (healthy) 8181/tcp kasm_manager
dd8a4d075371 kasmweb/kasm-guac:1.19.0-rolling "/dockerentrypoint.sh" About an hour ago Up 20 minutes (unhealthy) kasm_guac
Additional context
Identity provider: PocketID, running v2.10.0 at time of failure. It was upgraded from a pre-v2 build on Saturday; Kasm OIDC login had worked until then and broke immediately after the upgrade, with no config changes on either side. This pins the trigger to PocketID's v2 move to ory/fosite.
Root cause is the scope ordering difference, not the scope contents. Requested openid profile email groups; returned profile groups openid email.
Reordering the scope in Kasm's OIDC config does not work as a fix — the requested scope appears to be re-normalized before being sent, so it can't be made to match the provider's returned order from configuration.
Confirmed working workaround: set OAUTHLIB_RELAX_TOKEN_SCOPE=1 in the environment of the kasm_api service. This is only a workaround (disables oauthlib's scope-change check globally for that client, and must be re-applied after upgrades since the compose file is regenerated).
This affects compatibility with any ory/fosite-based OIDC provider, so a fix in Kasm's OIDC handler would prevent recurrence beyond just PocketID.
OIDC login fails with "Internal error" at /api/oidc_callback (HTTP 500) when the identity provider returns the granted OAuth scopes in a different order than Kasm requested — even though the scope set is identical.
Kasm's OIDC handler uses requests-oauthlib/oauthlib. In oidc_callback → process_callback → oauth2_session.fetch_token, oauthlib's validate_token_parameters() compares requested vs. returned scope order-sensitively and raises Warning: Scope has changed from "openid profile email groups" to "profile groups openid email". Kasm does not catch this warning, so it propagates as an unhandled exception and the callback returns 500.
Critically, the token exchange itself succeeds — the IdP returns HTTP 200 on POST /token with valid tokens. The failure is entirely on the Kasm side, in oauthlib's scope comparison, one step after a successful exchange. It is not a TLS, certificate, client-secret, JWKS/signature, or claims issue.
This surfaced when the identity provider (PocketID) was upgraded into its v2 line, which rewrote its OAuth/OIDC engine onto ory/fosite. Fosite returns the granted scopes in a fixed internal order that differs from the request order, which is what trips oauthlib. No Kasm or IdP configuration values were changed; the IdP version bump alone triggered it. Upgrading Kasm 1.18.1 → 1.19.0 did not resolve it, since the fault is in the client library's reaction to the response, not Kasm's version.
Relevant traceback (from kasm_api):
To Reproduce
Configure an OIDC provider whose token endpoint returns granted scopes in a different order than requested (e.g. PocketID v2 / any ory/fosite-based provider). Kasm requests openid profile email groups.
Attempt to log in via the OIDC provider from the Kasm login page.
Authenticate successfully at the IdP (IdP logs show 200 on POST /token).
Browser is redirected to /api/oidc_callback → Kasm returns HTTP 500, "Internal error".
Expected behavior
Kasm should treat an identical scope set returned in a different order as equivalent and complete the login, rather than raising on the reordering.
Screenshots
N/A — generic "Internal error" page; the actionable detail is the kasm_api traceback above.
Workspaces Version
Version 1.19.0 (issue also present on 1.18.1; upgrade did not resolve).
Workspaces Installation Method
linuxserver.io (Docker-in-Docker image), running on Unraid.
Client Browser (please complete the following information):
Reproduced across multiple browsers (provider-independent):
OS: macOS, WIndows
Browser: safari, chrome, edge
Version: N/A
Workspace Server Information:
Note: on the linuxserver.io image these must be run against the inner Docker (prefix with docker exec ...). Paste your real output for each.
uname -a
cat /etc/os-release
sudo docker info
sudo docker ps | grep kasm
Additional context
Identity provider: PocketID, running v2.10.0 at time of failure. It was upgraded from a pre-v2 build on Saturday; Kasm OIDC login had worked until then and broke immediately after the upgrade, with no config changes on either side. This pins the trigger to PocketID's v2 move to ory/fosite.
Root cause is the scope ordering difference, not the scope contents. Requested openid profile email groups; returned profile groups openid email.
Reordering the scope in Kasm's OIDC config does not work as a fix — the requested scope appears to be re-normalized before being sent, so it can't be made to match the provider's returned order from configuration.
Confirmed working workaround: set OAUTHLIB_RELAX_TOKEN_SCOPE=1 in the environment of the kasm_api service. This is only a workaround (disables oauthlib's scope-change check globally for that client, and must be re-applied after upgrades since the compose file is regenerated).
This affects compatibility with any ory/fosite-based OIDC provider, so a fix in Kasm's OIDC handler would prevent recurrence beyond just PocketID.