Skip to content

Commit fe4f6c2

Browse files
docs: Fix the "Could not open websocket" / multiple-accounts OAuth troubleshooting (#8637)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d2adee2 commit fe4f6c2

2 files changed

Lines changed: 121 additions & 22 deletions

File tree

doc/how_to/authentication/providers/azure.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,40 @@ The `CLIENT_SECRET` corresponds to the `Value` below:
4040
The `REDIRECT_URI` should be included in the list of Web Redirect URIs:
4141

4242
![REDIRECT_URI](../../../_static/images/azure_oauth_uris.png)
43+
44+
## Handling multiple accounts
45+
46+
Users who are signed into more than one Microsoft account in the same browser (for
47+
example a corporate and a personal account) may be logged in with the wrong account,
48+
or be confused about which account is used. You can control this by forwarding a
49+
`prompt` parameter to the Azure authorization endpoint via `oauth_extra_params`:
50+
51+
```bash
52+
panel serve app.py \
53+
--oauth-provider=azure \
54+
--oauth-key='CLIENT_ID' \
55+
--oauth-secret='CLIENT_SECRET' \
56+
--cookie-secret='COOKIE_SECRET' \
57+
--oauth-redirect-uri=REDIRECT_URI \
58+
--oauth-extra-params "{'tenant': 'TENANT_ID', 'prompt': 'select_account'}" \
59+
...
60+
```
61+
62+
- `prompt=select_account` always shows the account picker so the user can choose which
63+
account to sign in with.
64+
- `prompt=login` forces the user to re-authenticate.
65+
66+
Restricting `tenant` to your directory ID (rather than `common`) additionally ensures
67+
that only accounts from your organization are accepted.
68+
69+
```{note}
70+
Forwarding arbitrary authorization parameters such as `prompt` requires the Panel
71+
release that includes [#8635](https://github.com/holoviz/panel/pull/8635).
72+
```
73+
74+
```{warning}
75+
Multiple accounts are sometimes mistaken for the cause of a `Could not open websocket`
76+
error. That error is almost always a request-header **size** problem rather than an
77+
account problem — see [Troubleshooting OAuth](../trouble_shooting) for the diagnosis
78+
and fixes.
79+
```

doc/how_to/authentication/trouble_shooting.md

Lines changed: 84 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,89 @@ Error rendering Bokeh items: Error: Could not open websocket
6868
[bokeh 3.7.2] Websocket connection 0 disconnected, will not attempt to reconnect
6969
```
7070

71-
#### Cause: Token Too Big
71+
#### Cause: Request Headers Too Big
72+
73+
This is by far the most common cause. The most important thing to understand is
74+
that it is almost always a **header size** problem, *not* a problem with "multiple
75+
accounts" or your browser, even though those are common ways to stumble onto it.
76+
77+
When a Bokeh/Panel session starts, the server hands the browser a **session token**
78+
and the browser sends it back in the `Sec-WebSocket-Protocol` request header when it
79+
opens the WebSocket. With OAuth enabled, this token embeds the originating request's
80+
cookies and headers — and your OAuth cookies (`access_token`, `id_token`,
81+
`refresh_token`) can be large, especially with Azure/Entra ID accounts that belong to
82+
many groups, or when using `--oauth-encryption-key` (which inflates them further).
83+
84+
If the resulting header exceeds the limit your proxy allows for a single request
85+
header line (for nginx this is `large_client_header_buffers`, **default 8&nbsp;KB**),
86+
the proxy rejects the WebSocket upgrade — typically with an `HTTP 400`*before* it
87+
ever reaches the Panel server, and the browser reports `Could not open websocket`.
88+
89+
Because the failing header is often only a few hundred bytes over the limit, the same
90+
app can appear to "work" in an incognito window, a different browser, or after
91+
clearing cookies — simply because those carry slightly smaller headers and slip back
92+
under the limit. These are **not** reliable fixes; the header will creep back over the
93+
limit as tokens grow.
94+
95+
##### How to diagnose
96+
97+
In your browser developer tools, open the **Network** tab, reproduce the failure, and
98+
inspect the failed `.../ws` request:
99+
100+
- If it shows *"Provisional headers are shown"* / no response and an `HTTP 400`, and
101+
the request never appears in your Panel server logs, the proxy is rejecting it.
102+
- Look at the size of the `Sec-WebSocket-Protocol` and `Cookie` request headers. If
103+
either approaches or exceeds ~8&nbsp;KB, this is your problem. (Chromium's
104+
`chrome://net-export` / `edge://net-export` capture shows the exact failure reason.)
105+
106+
##### Solutions
107+
108+
1. **Increase your proxy's request-header buffer** (recommended for production). For
109+
nginx, raise `large_client_header_buffers` (and `client_header_buffer_size`), e.g.
110+
`large_client_header_buffers 4 32k;`. Note these are *request*-header settings —
111+
raising response buffers such as nginx's `proxy_buffer_size` will **not** help.
112+
On the Kubernetes `ingress-nginx` controller these are **not** available as
113+
per-`Ingress` annotations; they must be set in the controller's **ConfigMap**
114+
(`large-client-header-buffers`, `client-header-buffer-size`) — setting them as
115+
`nginx.ingress.kubernetes.io/...` annotations has no effect.
116+
117+
2. **Stop embedding the large OAuth cookies in the token.** Serve with
118+
`--exclude-cookies access_token id_token refresh_token` so they are not packed into
119+
the session token (they are still sent in the `Cookie` header and used for
120+
authentication). This shrinks the token dramatically. Note that
121+
`pn.state.access_token`, `pn.state.refresh_token` and `pn.state.user_info` are read
122+
from the session context, so they will not be available inside the app when those
123+
cookies are excluded — exclude only the ones your app does not need.
124+
125+
3. **Reduce the size of the tokens themselves.** Request fewer scopes, and for Azure
126+
configure group **overage** (so large group lists are not inlined into the token).
127+
128+
4. **Run without an encryption key** (less secure, but produces smaller cookies and
129+
works with default proxy configurations) by removing `--oauth-encryption-key` /
130+
`PANEL_OAUTH_ENCRYPTION`.
131+
132+
For more details see [Issue #7909](https://github.com/holoviz/panel/issues/7909) and
133+
[Issue #8634](https://github.com/holoviz/panel/issues/8634).
134+
135+
#### A note on "multiple accounts"
136+
137+
Being signed into multiple accounts (e.g. a corporate and a personal Microsoft
138+
account) does **not** by itself break the WebSocket — those extra accounts live as
139+
cookies on the identity provider's domain, not on your app's domain, so they do not
140+
enlarge your app's request headers. If multiple accounts *seem* to matter, it is
141+
usually because one account simply has larger tokens and tips you over the header
142+
limit described above.
143+
144+
What multiple accounts *can* cause is the wrong account being selected during login.
145+
To control this, forward a `prompt` parameter to the provider via
146+
`oauth_extra_params`, for example for Azure/Entra ID:
72147

73-
You may experience this issue when the *token* used by the client to create the WebSocket connection to the Panel server has grown larger than allowed by your proxy settings.
74-
This is particularly common when using the `--oauth-encryption-key` parameter, which significantly increases the token size.
75-
76-
To troubleshoot, try removing the `--oauth-encryption-key` parameter or `PANEL_OAUTH_ENCRYPTION` environment variable to see if encryption is the cause of the issue.
77-
78-
Potential solutions include:
79-
80-
1. **Update your proxy settings** to allow larger header sizes (recommended for production environments).
81-
82-
2. **Run without an encryption key** (less secure, but works with default proxy configurations)
83-
84-
For more details on this issue, see [Issue #7909](https://github.com/holoviz/panel/issues/7909).
85-
86-
#### Cause: Multiple Accounts
87-
88-
You may experience this issue when using multiple accounts (for example corporate and personal) in the same browser. Try opening your Panel application in a private/incognito browser window to test if this might be the cause.
89-
90-
If this confirms that browser history or configuration is causing the issue, try one or more of these steps:
148+
```bash
149+
panel serve app.py --oauth-provider=azure \
150+
--oauth-extra-params "{'tenant': 'TENANT_ID', 'prompt': 'select_account'}" \
151+
...
152+
```
91153

92-
1. Clear your browser cache and cookies (select `Delete ALL browsing data` in your browser settings)
93-
2. Log out from all accounts before accessing your Panel application.
94-
3. Use a dedicated browser profile for your Panel application
154+
`prompt=select_account` always shows the account picker so the user can choose the
155+
right account; `prompt=login` forces re-authentication. See the
156+
[Azure provider docs](providers/azure) for details.

0 commit comments

Comments
 (0)