Skip to content

[Bug] - OIDC callback 500s on reordered-but-identical scopes (fosite/PocketID v2) #894

Description

@Schnugaf

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions