{"id":"CVE-2026-59219","aliases":["EUVD-2026-42625","GHSA-855v-hq7w-jmjw","PYSEC-2026-3595"],"status":"published","title":"Open WebUI: Realtime endpoints accept Redis-revoked JWTs after signout/backchannel logout","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. From 0.9.0 before 0.10.0 with Redis configured, Socket.IO connect, user-join, join-channels, join-note, and the terminal websocket first-message authentication used decode_token without the Redis-backed is_valid_token revocation check, allowing revoked JWTs to continue authenticating realtime connections. This issue is fixed in version 0.10.0.","severity":{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00551,"percentile":0.46505,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-613"],"published":"2026-07-09T16:43:24Z","modified":"2026-10-28T02:30:46Z","fixes":[{"ecosystem":"PyPI","package":"open-webui","fixed_in":["0.10.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"0.10.0"}]}]},{"source":"osv","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"0.10.0"}]}],"versions":["0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5","0.9.6"]},{"source":"osv","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"0.10.0"}]}],"versions":["0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5","0.9.6"]}],"products":[{"vendor":"open-webui","product":"open-webui"},{"vendor":"openwebui","product":"open_webui"}],"references":[{"url":"https://github.com/open-webui/open-webui/security/advisories/GHSA-855v-hq7w-jmjw","tags":["x_refsource_CONFIRM","exploit","Mitigation","Vendor Advisory","Exploit","advisory","web"]},{"url":"https://github.com/open-webui/open-webui/commit/33b91bd8ae8a100a5a306c91441a7d0b422c4cde","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/open-webui/open-webui/releases/tag/v0.10.0","tags":["x_refsource_MISC","Product","Release Notes","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-59219","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-855v-hq7w-jmjw","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59219.json","tags":["advisory"]},{"url":"https://github.com/open-webui/open-webui","tags":["package"]},{"url":"https://pypi.org/project/open-webui","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-59219","title":"Open WebUI: Realtime endpoints accept Redis-revoked JWTs after signout/backchannel logout","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. From 0.9.0 before 0.10.0 with Redis configured, Socket.IO connect, user-join, join-channels, join-note, and the terminal websocket first-message authentication used decode_token without the Redis-backed is_valid_token revocation check, allowing revoked JWTs to continue authenticating realtime connections. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T16:43:24.542Z","modified":"2026-07-09T17:30:39.13Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-613"],"updated_at":"2026-11-02T14:19:58.560602Z"}],"euvd":[{"id":"EUVD-2026-42625","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. From 0.9.0 before 0.10.0 with Redis configured, Socket.IO connect, user-join, join-channels, join-note, and the terminal websocket first-message authentication used decode_token without the Redis-backed is_valid_token revocation check, allowing revoked JWTs to continue authenticating realtime connections. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T16:43:24Z","modified":"2026-07-09T17:30:39Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"updated_at":"2026-11-02T18:20:03.742277Z"}],"ghsa":[{"id":"GHSA-855v-hq7w-jmjw","title":"Open WebUI: Realtime endpoints accept Redis-revoked JWTs after signout/backchannel logout","description":"## Summary\n\nWith Redis configured, Open WebUI supports JWT revocation: `POST /api/v1/auths/signout` (per-token `jti`) and OIDC back-channel logout (per-user `revoked_at`) record revocations in Redis, and HTTP auth (`get_current_user`) rejects revoked tokens with 401. The realtime authentication surfaces do not perform this check: Socket.IO connect / user-join / join-channels / join-note and the terminal websocket first-message auth validate tokens with `decode_token()` only (signature + expiry). A JWT revoked by sign-out or back-channel logout therefore continues to authenticate new realtime connections, even though the same token is rejected on HTTP.\n\n## Affected component\n\n- `backend/open_webui/socket/main.py` — Socket.IO `connect`, `user-join`, `join-channels`, `join-note`\n- `backend/open_webui/routers/terminals.py` — terminal websocket first-message auth\n- `backend/open_webui/utils/auth.py` — the revocation check was applied to HTTP only\n\n## Root cause\n\nHTTP auth enforces revocation:\n\n```python\n# utils/auth.py — get_current_user\nif data.get('jti') and not await is_valid_token(request, data):\n    raise HTTPException(status_code=401, detail='Invalid token')\n```\n\nRealtime auth calls `decode_token()` only, which verifies signature + expiry but never consults the Redis revocation keys (`{prefix}:auth:token:{jti}:revoked`, `{prefix}:auth:user:{id}:revoked_at`):\n\n```python\n# socket/main.py — connect / user-join / join-channels / join-note\ndata = decode_token(auth['token'])\n# routers/terminals.py — _resolve_authenticated_connection\ndata = decode_token(token)\n```\n\n## Impact\n\nA JWT revoked by user sign-out or OIDC back-channel logout still authenticates new realtime connections. A stolen token therefore retains realtime access after the victim signs out or the IdP performs back-channel logout — the very remediation for a compromised token. The token can populate `SESSION_POOL` as the victim, join their user/channel/note rooms (receiving realtime channel messages, collaborative-note updates and presence), drive socket-level collaboration as the victim, and pass terminal websocket authentication when terminal servers are configured. HTTP remains correctly protected (401), so REST data and state-changing REST endpoints are not reachable with the revoked token.\n\n## Proof of Concept\n\nReporter PoC on a Redis-backed deployment (v0.9.6 and main): after `POST /api/v1/auths/signout`, HTTP returns 401 for the token while a Socket.IO user-join with the same token still authenticates, and the terminal WS reaches terminal-server lookup rather than rejecting it as `Invalid token`.\n\n## Fix\n\nApply the revocation check on the realtime paths. The logic is factored into `is_token_revoked(redis, decoded)` (covering per-token `jti` and per-user `revoked_at`); the Socket.IO handlers and the terminal WS reject tokens that fail it, using the main app Redis where revocations are stored. HTTP `is_valid_token` delegates to the same helper, so HTTP behaviour is unchanged.\n\n## Affected / Patched\n\n- Affected: `\u003e= 0.9.0, \u003c 0.10.0`, and only when Redis is configured (without Redis, per-token revocation is not supported and sign-out does not invalidate JWTs by design).\n- Patched: v0.10.0. The revocation check (`is_valid_token`, covering per-token `jti` and per-user `revoked_at`) is applied on Socket.IO connect / user-join / join-channels / join-note and the terminal websocket first-message auth, using the main app Redis where revocations are stored. HTTP `is_valid_token` delegates to the same logic, so HTTP behaviour is unchanged.","status":"published","published":"2026-07-24T17:00:11Z","modified":"2026-07-24T17:00:13Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-613"],"updated_at":"2026-11-02T14:40:52.46233Z"}],"nvd":[{"id":"CVE-2026-59219","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. From 0.9.0 before 0.10.0 with Redis configured, Socket.IO connect, user-join, join-channels, join-note, and the terminal websocket first-message authentication used decode_token without the Redis-backed is_valid_token revocation check, allowing revoked JWTs to continue authenticating realtime connections. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T17:17:02.877Z","modified":"2026-07-10T18:17:21.79Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high","assessor":"secondary"}],"cwes":["CWE-613"],"updated_at":"2026-11-02T14:12:28.4135Z"}],"osv":[{"id":"CVE-2026-59219","title":"Open WebUI: Realtime endpoints accept Redis-revoked JWTs after signout/backchannel logout","description":"Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. From 0.9.0 before 0.10.0 with Redis configured, Socket.IO connect, user-join, join-channels, join-note, and the terminal websocket first-message authentication used decode_token without the Redis-backed is_valid_token revocation check, allowing revoked JWTs to continue authenticating realtime connections. This issue is fixed in version 0.10.0.","status":"published","published":"2026-07-09T16:43:24.542Z","modified":"2026-10-28T02:30:46.672767604Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-613"],"updated_at":"2026-11-02T15:45:12.496548Z"},{"id":"GHSA-855v-hq7w-jmjw","title":"Open WebUI: Realtime endpoints accept Redis-revoked JWTs after signout/backchannel logout","description":"## Summary\n\nWith Redis configured, Open WebUI supports JWT revocation: `POST /api/v1/auths/signout` (per-token `jti`) and OIDC back-channel logout (per-user `revoked_at`) record revocations in Redis, and HTTP auth (`get_current_user`) rejects revoked tokens with 401. The realtime authentication surfaces do not perform this check: Socket.IO connect / user-join / join-channels / join-note and the terminal websocket first-message auth validate tokens with `decode_token()` only (signature + expiry). A JWT revoked by sign-out or back-channel logout therefore continues to authenticate new realtime connections, even though the same token is rejected on HTTP.\n\n## Affected component\n\n- `backend/open_webui/socket/main.py` — Socket.IO `connect`, `user-join`, `join-channels`, `join-note`\n- `backend/open_webui/routers/terminals.py` — terminal websocket first-message auth\n- `backend/open_webui/utils/auth.py` — the revocation check was applied to HTTP only\n\n## Root cause\n\nHTTP auth enforces revocation:\n\n```python\n# utils/auth.py — get_current_user\nif data.get('jti') and not await is_valid_token(request, data):\n    raise HTTPException(status_code=401, detail='Invalid token')\n```\n\nRealtime auth calls `decode_token()` only, which verifies signature + expiry but never consults the Redis revocation keys (`{prefix}:auth:token:{jti}:revoked`, `{prefix}:auth:user:{id}:revoked_at`):\n\n```python\n# socket/main.py — connect / user-join / join-channels / join-note\ndata = decode_token(auth['token'])\n# routers/terminals.py — _resolve_authenticated_connection\ndata = decode_token(token)\n```\n\n## Impact\n\nA JWT revoked by user sign-out or OIDC back-channel logout still authenticates new realtime connections. A stolen token therefore retains realtime access after the victim signs out or the IdP performs back-channel logout — the very remediation for a compromised token. The token can populate `SESSION_POOL` as the victim, join their user/channel/note rooms (receiving realtime channel messages, collaborative-note updates and presence), drive socket-level collaboration as the victim, and pass terminal websocket authentication when terminal servers are configured. HTTP remains correctly protected (401), so REST data and state-changing REST endpoints are not reachable with the revoked token.\n\n## Proof of Concept\n\nReporter PoC on a Redis-backed deployment (v0.9.6 and main): after `POST /api/v1/auths/signout`, HTTP returns 401 for the token while a Socket.IO user-join with the same token still authenticates, and the terminal WS reaches terminal-server lookup rather than rejecting it as `Invalid token`.\n\n## Fix\n\nApply the revocation check on the realtime paths. The logic is factored into `is_token_revoked(redis, decoded)` (covering per-token `jti` and per-user `revoked_at`); the Socket.IO handlers and the terminal WS reject tokens that fail it, using the main app Redis where revocations are stored. HTTP `is_valid_token` delegates to the same helper, so HTTP behaviour is unchanged.\n\n## Affected / Patched\n\n- Affected: `\u003e= 0.9.0, \u003c 0.10.0`, and only when Redis is configured (without Redis, per-token revocation is not supported and sign-out does not invalidate JWTs by design).\n- Patched: v0.10.0. The revocation check (`is_valid_token`, covering per-token `jti` and per-user `revoked_at`) is applied on Socket.IO connect / user-join / join-channels / join-note and the terminal websocket first-message auth, using the main app Redis where revocations are stored. HTTP `is_valid_token` delegates to the same logic, so HTTP behaviour is unchanged.","status":"published","published":"2026-07-24T17:00:11Z","modified":"2026-08-04T14:40:39.825527733Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-613"],"updated_at":"2026-11-02T15:46:41.732368Z"},{"id":"PYSEC-2026-3595","title":"Open WebUI: Realtime endpoints accept Redis-revoked JWTs after signout/backchannel logout","description":"## Summary\n\nWith Redis configured, Open WebUI supports JWT revocation: `POST /api/v1/auths/signout` (per-token `jti`) and OIDC back-channel logout (per-user `revoked_at`) record revocations in Redis, and HTTP auth (`get_current_user`) rejects revoked tokens with 401. The realtime authentication surfaces do not perform this check: Socket.IO connect / user-join / join-channels / join-note and the terminal websocket first-message auth validate tokens with `decode_token()` only (signature + expiry). A JWT revoked by sign-out or back-channel logout therefore continues to authenticate new realtime connections, even though the same token is rejected on HTTP.\n\n## Affected component\n\n- `backend/open_webui/socket/main.py` — Socket.IO `connect`, `user-join`, `join-channels`, `join-note`\n- `backend/open_webui/routers/terminals.py` — terminal websocket first-message auth\n- `backend/open_webui/utils/auth.py` — the revocation check was applied to HTTP only\n\n## Root cause\n\nHTTP auth enforces revocation:\n\n```python\n# utils/auth.py — get_current_user\nif data.get('jti') and not await is_valid_token(request, data):\n    raise HTTPException(status_code=401, detail='Invalid token')\n```\n\nRealtime auth calls `decode_token()` only, which verifies signature + expiry but never consults the Redis revocation keys (`{prefix}:auth:token:{jti}:revoked`, `{prefix}:auth:user:{id}:revoked_at`):\n\n```python\n# socket/main.py — connect / user-join / join-channels / join-note\ndata = decode_token(auth['token'])\n# routers/terminals.py — _resolve_authenticated_connection\ndata = decode_token(token)\n```\n\n## Impact\n\nA JWT revoked by user sign-out or OIDC back-channel logout still authenticates new realtime connections. A stolen token therefore retains realtime access after the victim signs out or the IdP performs back-channel logout — the very remediation for a compromised token. The token can populate `SESSION_POOL` as the victim, join their user/channel/note rooms (receiving realtime channel messages, collaborative-note updates and presence), drive socket-level collaboration as the victim, and pass terminal websocket authentication when terminal servers are configured. HTTP remains correctly protected (401), so REST data and state-changing REST endpoints are not reachable with the revoked token.\n\n## Proof of Concept\n\nReporter PoC on a Redis-backed deployment (v0.9.6 and main): after `POST /api/v1/auths/signout`, HTTP returns 401 for the token while a Socket.IO user-join with the same token still authenticates, and the terminal WS reaches terminal-server lookup rather than rejecting it as `Invalid token`.\n\n## Fix\n\nApply the revocation check on the realtime paths. The logic is factored into `is_token_revoked(redis, decoded)` (covering per-token `jti` and per-user `revoked_at`); the Socket.IO handlers and the terminal WS reject tokens that fail it, using the main app Redis where revocations are stored. HTTP `is_valid_token` delegates to the same helper, so HTTP behaviour is unchanged.\n\n## Affected / Patched\n\n- Affected: `\u003e= 0.9.0, \u003c 0.10.0`, and only when Redis is configured (without Redis, per-token revocation is not supported and sign-out does not invalidate JWTs by design).\n- Patched: v0.10.0. The revocation check (`is_valid_token`, covering per-token `jti` and per-user `revoked_at`) is applied on Socket.IO connect / user-join / join-channels / join-note and the terminal websocket first-message auth, using the main app Redis where revocations are stored. HTTP `is_valid_token` delegates to the same logic, so HTTP behaviour is unchanged.","status":"published","published":"2026-08-04T11:34:42.714822Z","modified":"2026-08-04T14:30:28.570448901Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"updated_at":"2026-11-02T15:52:56.847083Z"}]}}
