A deterministic local reproduction of the logic is:
importasyncioimportjsonclassFakeUpstream:def__init__(self):self.sent=[]asyncdefsend_str(self,data):self.sent.append(('str',data))asyncdefsimulate(auth_type,key='secret-token'):upstream=FakeUpstream()ifauth_type=='bearer':awaitupstream.send_str(json.dumps({'type':'auth','token':key}))returnupstream.sentforauth_typein['bearer','session','system_oauth','none']:sent=asyncio.run(simulate(auth_type))print(f'{auth_type} -> sent = {sent}')
Actual output:
bearer -> sent = [('str', '{"type": "auth", "token": "secret-token"}')]
session -> sent = []
system_oauth -> sent = []
none -> sent = []
Logs & Screenshots
Relevant current websocket code path (backend/open_webui/routers/terminals.py):
This looks like an implementation mismatch rather than a security issue.
I have a narrow fix ready that centralizes the auth assembly used by proxy_terminal and reuses it for the websocket handshake so the two paths do not drift.
Originally created by @shaun0927 on GitHub (Apr 16, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/23788
### Check Existing Issues
- [x] I have searched for any existing and/or related issues.
- [x] I have searched for any existing and/or related discussions.
- [x] I have also searched in the CLOSED issues AND CLOSED discussions and found no exact duplicate.
- [x] I am using the latest version of Open WebUI.
### Installation Method
Git Clone
### Open WebUI Version
latest `main` as of 2026-04-16 (latest release also checked: `v0.8.12`)
### Ollama Version (if applicable)
_No response_
### Operating System
macOS Sequoia
### Browser (if applicable)
_No response_
### Confirmation
- [x] I have read and followed all instructions in `README.md`.
- [x] I am using the latest version of Open WebUI.
- [x] I have provided a deterministic local reproduction of the affected code path.
- [x] I have included the relevant code path, expected behavior, actual behavior, and exact reproduction steps.
### Expected Behavior
The terminal websocket proxy should support the same auth modes as the terminal HTTP proxy.
If `proxy_terminal` supports `bearer`, `session`, and `system_oauth`, then `ws_terminal` should not silently drop the non-bearer auth paths.
### Actual Behavior
`proxy_terminal` handles:
- `bearer`
- `session`
- `system_oauth`
but `ws_terminal` only sends upstream auth material when `auth_type == 'bearer'`.
That makes the websocket path behavior inconsistent with the HTTP proxy path.
This is **not** the same bug as `#22581` (`ws://` vs `wss://` under HTTPS).
It is an auth-mode mismatch.
There was also a nearby closed PR `#23603` (`Fix terminal orchestrator ws auth`), but the current `main` / `v0.8.12` code still shows the mismatch below.
### Steps to Reproduce
Current HTTP proxy path in `backend/open_webui/routers/terminals.py`:
```python
if auth_type == 'bearer':
headers['Authorization'] = f'Bearer {connection.get("key", "")}'
elif auth_type == 'session':
cookies = request.cookies
headers['Authorization'] = f'Bearer {request.state.token.credentials}'
elif auth_type == 'system_oauth':
cookies = request.cookies
oauth_token = request.headers.get('x-oauth-access-token', '')
if oauth_token:
headers['Authorization'] = f'Bearer {oauth_token}'
```
Current websocket path:
```python
auth_type = connection.get('auth_type', 'bearer')
if auth_type == 'bearer':
key = connection.get('key', '')
await upstream.send_str(_json.dumps({'type': 'auth', 'token': key}))
```
A deterministic local reproduction of the logic is:
```python
import asyncio
import json
class FakeUpstream:
def __init__(self):
self.sent = []
async def send_str(self, data):
self.sent.append(('str', data))
async def simulate(auth_type, key='secret-token'):
upstream = FakeUpstream()
if auth_type == 'bearer':
await upstream.send_str(json.dumps({'type': 'auth', 'token': key}))
return upstream.sent
for auth_type in ['bearer', 'session', 'system_oauth', 'none']:
sent = asyncio.run(simulate(auth_type))
print(f'{auth_type} -> sent = {sent}')
```
Actual output:
```text
bearer -> sent = [('str', '{"type": "auth", "token": "secret-token"}')]
session -> sent = []
system_oauth -> sent = []
none -> sent = []
```
### Logs & Screenshots
Relevant current websocket code path (`backend/open_webui/routers/terminals.py`):
```python
auth_type = connection.get('auth_type', 'bearer')
if auth_type == 'bearer':
key = connection.get('key', '')
await upstream.send_str(_json.dumps({'type': 'auth', 'token': key}))
```
### Additional Information
This looks like an implementation mismatch rather than a security issue.
I have a narrow fix ready that centralizes the auth assembly used by `proxy_terminal` and reuses it for the websocket handshake so the two paths do not drift.
I opened a narrow fix PR for this report: #23790. The PR reuses the same terminal auth assembly for the HTTP proxy and websocket handshake so non-bearer auth modes do not drift between the two paths.
<!-- gh-comment-id:4260961446 -->
@shaun0927 commented on GitHub (Apr 16, 2026):
I opened a narrow fix PR for this report: #23790. The PR reuses the same terminal auth assembly for the HTTP proxy and websocket handshake so non-bearer auth modes do not drift between the two paths.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @shaun0927 on GitHub (Apr 16, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/23788
Check Existing Issues
Installation Method
Git Clone
Open WebUI Version
latest
mainas of 2026-04-16 (latest release also checked:v0.8.12)Ollama Version (if applicable)
No response
Operating System
macOS Sequoia
Browser (if applicable)
No response
Confirmation
README.md.Expected Behavior
The terminal websocket proxy should support the same auth modes as the terminal HTTP proxy.
If
proxy_terminalsupportsbearer,session, andsystem_oauth, thenws_terminalshould not silently drop the non-bearer auth paths.Actual Behavior
proxy_terminalhandles:bearersessionsystem_oauthbut
ws_terminalonly sends upstream auth material whenauth_type == 'bearer'.That makes the websocket path behavior inconsistent with the HTTP proxy path.
This is not the same bug as
#22581(ws://vswss://under HTTPS).It is an auth-mode mismatch.
There was also a nearby closed PR
#23603(Fix terminal orchestrator ws auth), but the currentmain/v0.8.12code still shows the mismatch below.Steps to Reproduce
Current HTTP proxy path in
backend/open_webui/routers/terminals.py:Current websocket path:
A deterministic local reproduction of the logic is:
Actual output:
Logs & Screenshots
Relevant current websocket code path (
backend/open_webui/routers/terminals.py):Additional Information
This looks like an implementation mismatch rather than a security issue.
I have a narrow fix ready that centralizes the auth assembly used by
proxy_terminaland reuses it for the websocket handshake so the two paths do not drift.@shaun0927 commented on GitHub (Apr 16, 2026):
I opened a narrow fix PR for this report: #23790. The PR reuses the same terminal auth assembly for the HTTP proxy and websocket handshake so non-bearer auth modes do not drift between the two paths.
@tjbck commented on GitHub (Apr 16, 2026):
Intended behaviour.