Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions doc/how_to/authentication/providers/azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,40 @@ The `CLIENT_SECRET` corresponds to the `Value` below:
The `REDIRECT_URI` should be included in the list of Web Redirect URIs:

![REDIRECT_URI](../../../_static/images/azure_oauth_uris.png)

## Handling multiple accounts

Users who are signed into more than one Microsoft account in the same browser (for
example a corporate and a personal account) may be logged in with the wrong account,
or be confused about which account is used. You can control this by forwarding a
`prompt` parameter to the Azure authorization endpoint via `oauth_extra_params`:

```bash
panel serve app.py \
--oauth-provider=azure \
--oauth-key='CLIENT_ID' \
--oauth-secret='CLIENT_SECRET' \
--cookie-secret='COOKIE_SECRET' \
--oauth-redirect-uri=REDIRECT_URI \
--oauth-extra-params "{'tenant': 'TENANT_ID', 'prompt': 'select_account'}" \
...
```

- `prompt=select_account` always shows the account picker so the user can choose which
account to sign in with.
- `prompt=login` forces the user to re-authenticate.

Restricting `tenant` to your directory ID (rather than `common`) additionally ensures
that only accounts from your organization are accepted.

```{note}
Forwarding arbitrary authorization parameters such as `prompt` requires the Panel
release that includes [#8635](https://github.com/holoviz/panel/pull/8635).
```

```{warning}
Multiple accounts are sometimes mistaken for the cause of a `Could not open websocket`
error. That error is almost always a request-header **size** problem rather than an
account problem — see [Troubleshooting OAuth](../trouble_shooting) for the diagnosis
and fixes.
```
106 changes: 84 additions & 22 deletions doc/how_to/authentication/trouble_shooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,89 @@ Error rendering Bokeh items: Error: Could not open websocket
[bokeh 3.7.2] Websocket connection 0 disconnected, will not attempt to reconnect
```

#### Cause: Token Too Big
#### Cause: Request Headers Too Big

This is by far the most common cause. The most important thing to understand is
that it is almost always a **header size** problem, *not* a problem with "multiple
accounts" or your browser, even though those are common ways to stumble onto it.

When a Bokeh/Panel session starts, the server hands the browser a **session token**
and the browser sends it back in the `Sec-WebSocket-Protocol` request header when it
opens the WebSocket. With OAuth enabled, this token embeds the originating request's
cookies and headers — and your OAuth cookies (`access_token`, `id_token`,
`refresh_token`) can be large, especially with Azure/Entra ID accounts that belong to
many groups, or when using `--oauth-encryption-key` (which inflates them further).

If the resulting header exceeds the limit your proxy allows for a single request
header line (for nginx this is `large_client_header_buffers`, **default 8 KB**),
the proxy rejects the WebSocket upgrade — typically with an `HTTP 400` — *before* it
ever reaches the Panel server, and the browser reports `Could not open websocket`.

Because the failing header is often only a few hundred bytes over the limit, the same
app can appear to "work" in an incognito window, a different browser, or after
clearing cookies — simply because those carry slightly smaller headers and slip back
under the limit. These are **not** reliable fixes; the header will creep back over the
limit as tokens grow.

##### How to diagnose

In your browser developer tools, open the **Network** tab, reproduce the failure, and
inspect the failed `.../ws` request:

- If it shows *"Provisional headers are shown"* / no response and an `HTTP 400`, and
the request never appears in your Panel server logs, the proxy is rejecting it.
- Look at the size of the `Sec-WebSocket-Protocol` and `Cookie` request headers. If
either approaches or exceeds ~8 KB, this is your problem. (Chromium's
`chrome://net-export` / `edge://net-export` capture shows the exact failure reason.)

##### Solutions

1. **Increase your proxy's request-header buffer** (recommended for production). For
nginx, raise `large_client_header_buffers` (and `client_header_buffer_size`), e.g.
`large_client_header_buffers 4 32k;`. Note these are *request*-header settings —
raising response buffers such as nginx's `proxy_buffer_size` will **not** help.
On the Kubernetes `ingress-nginx` controller these are **not** available as
per-`Ingress` annotations; they must be set in the controller's **ConfigMap**
(`large-client-header-buffers`, `client-header-buffer-size`) — setting them as
`nginx.ingress.kubernetes.io/...` annotations has no effect.

2. **Stop embedding the large OAuth cookies in the token.** Serve with
`--exclude-cookies access_token id_token refresh_token` so they are not packed into
the session token (they are still sent in the `Cookie` header and used for
authentication). This shrinks the token dramatically. Note that
`pn.state.access_token`, `pn.state.refresh_token` and `pn.state.user_info` are read
from the session context, so they will not be available inside the app when those
cookies are excluded — exclude only the ones your app does not need.

3. **Reduce the size of the tokens themselves.** Request fewer scopes, and for Azure
configure group **overage** (so large group lists are not inlined into the token).

4. **Run without an encryption key** (less secure, but produces smaller cookies and
works with default proxy configurations) by removing `--oauth-encryption-key` /
`PANEL_OAUTH_ENCRYPTION`.

For more details see [Issue #7909](https://github.com/holoviz/panel/issues/7909) and
[Issue #8634](https://github.com/holoviz/panel/issues/8634).

#### A note on "multiple accounts"

Being signed into multiple accounts (e.g. a corporate and a personal Microsoft
account) does **not** by itself break the WebSocket — those extra accounts live as
cookies on the identity provider's domain, not on your app's domain, so they do not
enlarge your app's request headers. If multiple accounts *seem* to matter, it is
usually because one account simply has larger tokens and tips you over the header
limit described above.

What multiple accounts *can* cause is the wrong account being selected during login.
To control this, forward a `prompt` parameter to the provider via
`oauth_extra_params`, for example for Azure/Entra ID:

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.
This is particularly common when using the `--oauth-encryption-key` parameter, which significantly increases the token size.

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.

Potential solutions include:

1. **Update your proxy settings** to allow larger header sizes (recommended for production environments).

2. **Run without an encryption key** (less secure, but works with default proxy configurations)

For more details on this issue, see [Issue #7909](https://github.com/holoviz/panel/issues/7909).

#### Cause: Multiple Accounts

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.

If this confirms that browser history or configuration is causing the issue, try one or more of these steps:
```bash
panel serve app.py --oauth-provider=azure \
--oauth-extra-params "{'tenant': 'TENANT_ID', 'prompt': 'select_account'}" \
...
```

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