NewApp Monitoring is liveSign up free
AgentbeamBeta
AI agent vulnerability · Open WebUI

CVE-2026-59219: Open WebUI: Realtime endpoints accept Redis-revoked JWTs after signout/backchannel logout

highCVSS 3.1 7.1GHSA-855v-hq7w-jmjw

With Redis configured, Open WebUI rejects revoked JWTs over HTTP but still accepts them on Socket.IO and terminal websocket connections. A token revoked by sign-out or OIDC back-channel logout keeps working for realtime access. Versions 0.9.0 up to 0.10.0 are affected. Upgrade to 0.10.0.

Published Jul 24, 2026 · Analysis Oct 25, 2026

What an attacker can do

HTTP auth in get_current_user calls is_valid_token, which checks the Redis keys for a revoked jti and a per-user revoked_at. The realtime paths call decode_token() only, which checks signature and expiry. That covers Socket.IO connect, user-join, join-channels and join-note in socket/main.py, and the terminal websocket first-message auth in routers/terminals.py.

So a stolen token survives the usual remedy. After the victim signs out, or the identity provider sends back-channel logout, REST calls return 401, but a new Socket.IO connection with the same token still authenticates. The attacker can enter SESSION_POOL as the victim, join their user, channel and note rooms, read realtime channel messages, note updates and presence, act in collaboration as them, and pass terminal websocket auth when terminal servers are configured.

REST data and state-changing REST endpoints stay protected. The attacker needs a valid but revoked JWT, and a deployment with Redis. Without Redis, sign-out does not invalidate JWTs by design, so this bug adds nothing there.

Who is affected

open-webui from 0.9.0 up to but not including 0.10.0, and only when Redis is configured for revocation. The reporter reproduced it on 0.9.6 and main.

pip show open-webui

If you do not run Redis, tokens are not revocable at all on these versions, which is a separate design limit.

open-webui

Ecosystem
PyPI
Affected
>= 0.9.0, < 0.10.0
Fixed in
0.10.0
Check
pip show open-webui

How to fix it

Upgrade to open-webui 0.10.0:

pip install --upgrade open-webui==0.10.0

The revocation logic now lives in a shared helper, is_token_revoked, covering per-token jti and per-user revoked_at. It runs on Socket.IO connect, user-join, join-channels and join-note, and on the terminal websocket first-message auth, using the main app Redis. HTTP behaviour is unchanged. The release notes say revoked or expired sessions are rejected on real-time and terminal connections.

If a token may have leaked, upgrade, then restart so open sockets reconnect, and rotate the signing secret if you need to invalidate everything.

How to tell if you were hit

Compare the Socket.IO connections and terminal websocket sessions in your logs against sign-out and back-channel logout times for the same user. A connect or user-join event authenticated after the user's sign-out, or from a new address, indicates a revoked token in use. Check Redis for the {prefix}:auth:token:{jti}:revoked and {prefix}:auth:user:{id}:revoked_at keys to see which tokens were revoked.

Original advisory

From the GitHub Advisory Database (CC BY 4.0).

Summary

With 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.

Affected component

  • backend/open_webui/socket/main.py — Socket.IO connect, user-join, join-channels, join-note
  • backend/open_webui/routers/terminals.py — terminal websocket first-message auth
  • backend/open_webui/utils/auth.py — the revocation check was applied to HTTP only

Root cause

HTTP auth enforces revocation:

# utils/auth.py — get_current_user
if data.get('jti') and not await is_valid_token(request, data):
    raise HTTPException(status_code=401, detail='Invalid token')

Realtime 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):

# socket/main.py — connect / user-join / join-channels / join-note
data = decode_token(auth['token'])
# routers/terminals.py — _resolve_authenticated_connection
data = decode_token(token)

Impact

A 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.

Proof of Concept

Reporter 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.

Fix

Apply 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.

Affected / Patched

  • Affected: >= 0.9.0, < 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).
  • 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.

Credited to huslayer826, Classic298.

Questions

Can the attacker use the revoked token on REST endpoints?

No. HTTP still returns 401 for it. The advisory says REST data and state-changing REST endpoints are not reachable. Only the realtime Socket.IO and terminal websocket surfaces accept it.

Am I affected without Redis?

This specific bug is not. Without Redis, per-token revocation is not supported and sign-out does not invalidate JWTs by design.

Which versions are affected by CVE-2026-59219?

0.9.0 up to but not including 0.10.0, with Redis configured.

References

How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology

More Open WebUI vulnerabilities

All Open WebUI entries