Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
00ff824
fix(oauth2): don't start the OAuth2 dance for embedded guests
hughhhh Oct 7, 2026
9f2d7ab
fix(snowflake): allow OAuth2 client config from the connection dialog
hughhhh Oct 7, 2026
b774851
feat(db): embedded-only fallback credential on OAuth2 connections
hughhhh Oct 7, 2026
33b369e
feat(db): support Snowflake embedded fallback key pairs
hughhhh Oct 14, 2026
e69700f
Merge pull request #5 from hughhhh/w-7/snowflake-embedded-fallback-pem
hughhhh Oct 14, 2026
55bd9b4
refactor(db): drive the embedded fallback credential from the engine …
hughhhh Oct 14, 2026
67a4403
Merge apache/master into hughhhh/embedded-oauth2-fallback
hughhhh Oct 14, 2026
0c3b16f
fix(db): address embedded OAuth2 fallback review concerns
hughhhh Oct 14, 2026
ba7904c
fix(db): resolve embedded fallback review findings
hughhhh Oct 15, 2026
f88330a
test(db): isolate embedded fallback eligibility checks
hughhhh Oct 15, 2026
5a0ac6c
fix(db): refuse embedded guests on a connection that lost its OAuth2 …
hughhhh Oct 15, 2026
74c1c47
fix(db): QA secret-handling fixes for the embedded credential fallback
hughhhh Oct 15, 2026
4a5cb34
feat(import): carry Secure Extra in bundles and ask for its secrets o…
hughhhh Oct 15, 2026
8277dee
fix(snowflake): run embedded guests with the fallback user's default …
hughhhh Oct 15, 2026
9a301fd
fix(embedded): refuse a guest with no applicable credential as a 400
hughhhh Oct 16, 2026
aea27b5
fix(snowflake): drop the connection's authenticator on the embedded l…
hughhhh Oct 17, 2026
d380d14
fix(embedded): readable errors and an admin log when a guest can't co…
hughhhh Oct 17, 2026
fdf237e
fix(embedded): clearer guest credential section and accurate setup docs
hughhhh Oct 17, 2026
3c9dfdd
docs(embedded): say what a guest on an OAuth2 connection actually sees
hughhhh Oct 17, 2026
aca5dab
fix(embedded): log a rejected fallback credential on the raw connecti…
hughhhh Oct 17, 2026
bdf7917
fix(embedded): no connection credentials on a guest login; bundles ca…
hughhhh Oct 19, 2026
5107dba
fix(embedded): tell a refused guest why, and the admin how to fix it
hughhhh Oct 29, 2026
4c72545
docs(embedded): which dashboards stop loading, and the fallback's limits
hughhhh Oct 29, 2026
084c0a4
Merge remote-tracking branch 'origin/master' into hughhhh/embedded-oa…
hughhhh Oct 30, 2026
780cd01
chore(embedded): regenerate the OpenAPI spec and translation template
hughhhh Oct 30, 2026
248b23d
fix(embedded): type the Secure Extra action's object payload
hughhhh Oct 30, 2026
aa638a1
fix(embedded): bundles skip a Secure Extra block an engine masked whole
hughhhh Oct 30, 2026
37eb661
fix(embedded): overwrite imports ask for a secret stored as the mask
hughhhh Oct 30, 2026
e932e3d
fix(embedded): dashboard imports with overwrite_all ask for missing s…
hughhhh Oct 30, 2026
73a6c41
Merge remote-tracking branch 'origin/master' into hughhhh/embedded-oa…
hughhhh Oct 30, 2026
02e3bbb
Merge remote-tracking branch 'pr/45068' into ws/docs
hughhhh Oct 30, 2026
3608687
Merge remote-tracking branch 'origin/master' into ws/base
hughhhh Oct 31, 2026
df0e27a
Merge branch 'ws/base' into ws/docs
hughhhh Oct 31, 2026
c71591e
Merge branch 'master' into oauth2/embedded-docs-messages
hughhhh Nov 2, 2026
58b12b1
fix(embedded): address review of the credential checks and docs
hughhhh Nov 2, 2026
2beea0c
fix(embedded): address review of the credential and import checks
hughhhh Nov 2, 2026
647cb7c
fix(embedded): address second review of import and credential handling
hughhhh Nov 2, 2026
8e0061d
test(embedded): pin the partial bundle marker and correct the docs
hughhhh Nov 2, 2026
d127287
fix(embedded): address third review of bundles, filters and key inputs
hughhhh Nov 2, 2026
58b8365
Merge remote-tracking branch 'origin/master' into HEAD
hughhhh Nov 2, 2026
32d5a84
test(embedded): stop the dashboard import test depending on test order
hughhhh Nov 2, 2026
38cf7e7
Merge remote-tracking branch 'origin/master' into oauth2/embedded-doc…
hughhhh Nov 3, 2026
6d02100
fix(embedded): address reviews of imports, annotation keys and key pairs
hughhhh Nov 3, 2026
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
48 changes: 48 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,54 @@ assists people when migrating to a new version.

## Next

- `EMBEDDED_CREDENTIAL_FALLBACK` (new, off by default) changes behaviour for embedded
guests on database connections that authenticate users individually via OAuth2 with
**Impersonate logged in user** enabled. Ordinary service-account connections retain
their existing behavior, even with a deployment-wide OAuth2 client configured. With
the flag on, a guest whose request has no applicable embedded credential — none
configured on the connection, or one missing its username, password, or private key
required by the selected authentication method — is refused instead of connecting
with the credential in the connection's own SQLAlchemy URI. The viewer sees a
`DATABASE_SECURITY_ACCESS_ERROR` saying the chart's connection isn't set up for
embedded dashboards (with `GLOBAL_ASYNC_QUERIES` on, a generic error instead); the
server log names the connection and how to fix it.
**Embedded dashboards that stop loading when the flag is turned on:** any dashboard
whose charts use a Snowflake connection with **Impersonate logged in user** on but no
OAuth2 client (none on the connection and no `DATABASE_OAUTH2_CLIENTS` entry for
Snowflake) stops serving embedded viewers, even though impersonation never changed
the login on such a connection and those viewers were served under its base
account. Logged-in users are unaffected. Before turning the flag on, update each
such connection: either turn off **Impersonate logged in user**, which keeps serving
everyone under the connection's own credential as before, or configure an OAuth2
client and **Embedded guest credentials**. Queries that do run under an embedded credential
get a dedicated cache partition distinct from every logged-in username, including
a separate partition for guests without credentials, so they no longer share a
DATA-cache entry (or, under `GLOBAL_ASYNC_QUERIES`, a deduplicated chart task) with a logged-in user's
per-user result; existing cached entries for such charts are effectively invalidated
for guests. A malformed or incomplete `embedded_credentials` is rejected by
`POST`/`PUT /api/v1/database/` and by `validate_parameters` rather than stored and
ignored while both embedding flags are enabled; this check does not apply to asset import. Validation
uses the engine's credential rules and edited credentials rather than stale stored
values, and rejects whitespace-only usernames. Disabling either flag preserves stored credentials without blocking unrelated edits. Restored async guest
queries use the same credential as synchronous queries. Connection-mutator hooks
receive the selected fallback username. Snowflake OAuth2 scope is
configured explicitly; a role scope selects that role, and omitting it uses the
user's default role. Deployments that leave the fallback flag off are unaffected, except
that, whatever the flag, dashboard, chart and dataset exports now carry any masked
OAuth2 client and embedded credential blocks, and overwriting a connection from a
dashboard bundle merges them into its Secure Extra. A destination still on an earlier
release replaces its whole Secure Extra with those blocks instead, so upgrade import
destinations first. Overwriting a connection by import asks for the OAuth2 client
secret when the file names a different client than the stored one, and for the
embedded credential's secrets when it names a different username, rather than
pairing the stored secret with them.
Read the limitations in the "Embedding Superset" docs before turning it on: with
`GLOBAL_ASYNC_QUERIES` on, logged-in users' charts on a Snowflake OAuth2 connection
run as the connection's base account (this predates the flag, which does not change
it); with **Per user caching** off, logged-in users on an OAuth2 connection can be
shown each other's cached results; connections created in SQLAlchemy URI mode do
not show the OAuth2 or embedded credential sections; and turning off **Impersonate
logged in user** moves embedded viewers, and logged-in users, onto the base account.
- Dashboard and chart `viewers` can no longer be added through the API or the MCP
`manage_dashboard_roles` tool while the `ENABLE_VIEWERS` feature flag is off; such
requests are rejected with a 422. Enforcement of existing viewers is unchanged (a
Expand Down
161 changes: 161 additions & 0 deletions docs/docs/using-superset/embedding.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,166 @@ When enabled, the **Logout** menu item is removed from the user avatar dropdown

Must be `True` to enable the embedded SDK and the guest token endpoint. Without this flag, `embedDashboard` will fail to load.

### `EMBEDDED_CREDENTIAL_FALLBACK`

Allows a database connection that authenticates logged-in users per-user via OAuth2 to
also carry a single Snowflake username with either a password or a PEM key pair, used
**only** for embedded guest requests.

This exists because the two authentication models do not meet. An embedded viewer signs
in with a guest token rather than a Superset account, so there is no per-user OAuth2
token to resolve for them — and an external viewer may have no identity on the
analytical database at all. Without a fallback, embedded dashboards on a per-user OAuth2
connection cannot run queries.

```python
# superset_config.py
FEATURE_FLAGS = {
"EMBEDDED_SUPERSET": True,
"EMBEDDED_CREDENTIAL_FALLBACK": True,
}
```

With both flags on, engines that support it (currently Snowflake) show an **Embedded
guest credentials** section on the **Basic** tab of the connection dialog. The section
is editable only when the connection has an OAuth2 client and **Impersonate logged in
user** is on; otherwise it is disabled and states what is missing.

#### Before turning the flag on

Turning the flag on stops embedded dashboards on some existing connections from
loading for embedded viewers. A Snowflake connection with **Impersonate logged in
user** on but no OAuth2 client (none in its **OAuth2 client information** and no
`DATABASE_OAUTH2_CLIENTS` entry for Snowflake) served embedded viewers under its base
account, because impersonation without OAuth2 never changed the Snowflake login. With
the flag on, Superset refuses those viewers rather than serve them under an identity
nobody chose for them. The viewer sees *This chart can't be shown here because its
database connection isn't set up for embedded dashboards*, and the server log has a
`Refused an embedded viewer's query on database connection ...` warning naming the
connection and the fix. With `GLOBAL_ASYNC_QUERIES` on, the viewer sees the generic
*An error occurred while fetching the data.* instead, and the warning is the same.
Logged-in users are not affected.

Update each such connection before turning the flag on, in one of two ways:

- **Keep serving embedded viewers under the base account:** on the **Advanced** tab,
under **Security**, turn off **Impersonate logged in user**. On a connection with no
OAuth2 client this changes nothing for logged-in users, who were already on the
base account.
- **Serve embedded viewers under a dedicated account:** configure an OAuth2 client and
**Embedded guest credentials** as described below. Logged-in users then sign in to
Snowflake individually through OAuth2.

#### Setting up the credential

1. Enable `EMBEDDED_SUPERSET` and `EMBEDDED_CREDENTIAL_FALLBACK` (above).
2. Open the Snowflake connection for editing. On the **Basic** tab, expand **OAuth2
client information** and enter the client ID, client secret, authorization and token
request URIs, and scope. Alternatively, configure a deployment-wide client for
Snowflake in `DATABASE_OAUTH2_CLIENTS`.
3. On the **Advanced** tab, under **Security**, turn on **Impersonate logged in user**.
On a new connection this option appears only after you connect, so connect first and
then edit the connection.
4. Back on the **Basic** tab, expand **Embedded guest credentials**, enter the Snowflake
username, choose **Password** or **Key pair**, and fill in the secret.
5. Save. The section header shows **Configured** once a credential is stored.

Configure the Snowflake OAuth2 scopes explicitly to match your integration. For
example, `session:role:ANALYST refresh_token` selects the `ANALYST` role and requests a
refresh token. Omitting a role scope uses the user's default role; the dialog does not
prefill a scope or require a specific role scope.

For key-pair authentication, enter the Snowflake username, its PEM private key, and an
optional passphrase; register the corresponding public key with that Snowflake user. The
credential is stored encrypted in the connection's *Secure Extra* under
`embedded_credentials`. Passwords, private keys, and passphrases are masked on read,
while the username stays visible so an administrator can see which database user
embedded queries run as.

The credential must be complete: a username with no password or private key, or the
reverse, is rejected when both embedding flags are enabled. A whitespace-only username
is invalid. Validation uses the engine's credential rules and rejects malformed JSON
or non-object credential values. It checks the edited credential, so an incomplete
imported credential can be completed or cleared.

To remove the credential, expand **Embedded guest credentials**, click **Remove embedded
guest credentials** (or clear every field), and save. This deletes the
`embedded_credentials` key from *Secure Extra*. Removal stays available while the section
is otherwise disabled.

#### Requirements and limitations

- Both `EMBEDDED_SUPERSET` and `EMBEDDED_CREDENTIAL_FALLBACK` must be enabled. While
either is off, a stored credential is preserved, unused, and does not block unrelated
edits.
- The connection needs an OAuth2 client — per-connection **OAuth2 client information**
or a deployment-wide `DATABASE_OAUTH2_CLIENTS` entry — and **Impersonate logged in
user** must be on. Superset refuses to save a credential otherwise, and it never
applies to a connection without OAuth2.
- Embedded viewers run with the embedded account's **default Snowflake role**. The
connection's configured role and authenticator are not used for guest connections.
- The embedded account needs `USAGE` on the connection's warehouse.
- Supported secrets are a password, a Snowflake programmatic access token entered as the
password, or a key pair (PEM private key with an optional passphrase).
- Alerts, reports, connection tests, and logged-in users never use the credential; they
keep using the connection's own credential or their per-user OAuth2 token.
- **Async queries run logged-in users as the base account.** With
`GLOBAL_ASYNC_QUERIES` on, guest queries use the same fallback credential as
synchronous guest queries. Logged-in users' chart queries on a Snowflake OAuth2
connection, however, run in the async worker under the connection's own base
credential instead of the user's OAuth2 token, so Snowflake grants and row access
policies keyed to the user do not apply and the queries are attributed to the base
account. This predates `EMBEDDED_CREDENTIAL_FALLBACK`, and the flag does not change
it. Leave `GLOBAL_ASYNC_QUERIES` off on deployments that depend on per-user OAuth2.
- **Turn on Per user caching on OAuth2 connections.** Guest results always get their
own cache partition (below), but logged-in users do not unless **Per user caching**
(**Advanced** → **Security**) is on, or the `CACHE_IMPERSONATION` or
`CACHE_QUERY_BY_USER` feature flag is enabled. Without one of them, logged-in users
share one cache entry per query, so a user can be shown a result fetched under
another user's OAuth2 identity, including rows their own Snowflake policies would
hide, until the entry expires.
- **Connections created in SQLAlchemy URI mode** show neither the **OAuth2 client
information** nor the **Embedded guest credentials** section, and an existing
connection cannot be switched to the dynamic form while editing. For such a
connection, set `oauth2_client_info` and `embedded_credentials` directly in
**Secure extra** (**Advanced** → **Security**), or configure a deployment-wide
client in `DATABASE_OAUTH2_CLIENTS`.
- **Turning off Impersonate logged in user moves embedded viewers onto the base
Comment thread
sadpandajoe marked this conversation as resolved.
Resolved
account.** The credential applies only while impersonation is on. With it off,
embedded viewers query under the connection's own credential, and so do logged-in
users, who also stop signing in through OAuth2. The connection dialog refuses that
save while **Embedded guest credentials** are set, so clear them first. A
`PUT /api/v1/database/<id>` that sends only `impersonate_user: false` is accepted
and leaves the stored credential in place, unused until impersonation is back on.

Understand what this grants before enabling it:

- **Every embedded viewer of that connection queries as that one database user.** Guest
tokens carry no database identity, so per-user attribution and any database-side
row-level security keyed to the user do not apply. Use a dedicated account with the
narrowest access the embedded dashboards need, and enforce per-viewer restrictions
with Superset's own [row-level security](#security-notes) rules in the guest token.
- **Logged-in users are unaffected.** They continue to authenticate individually via
OAuth2 and have no route to the stored credential.
- **The guest token's `username` claim is never used to connect.** Whoever mints the
token controls that claim, so it is deliberately kept out of the connection identity.
Connection-mutator hooks receive the selected fallback connection username.
- **An embedded viewer never silently falls back to the connection's base credential.**
With this flag on, a guest on a connection with impersonation enabled that has no
OAuth2 client, or no applicable credential — none configured, or one that is
incomplete — is refused with an error rather than connected as whichever account the
connection URL carries. The viewer is told only that the chart's connection isn't
set up for embedded dashboards (with `GLOBAL_ASYNC_QUERIES` on, a generic error
instead); the server log names the connection and the fix.
Deployments that leave the flag off keep their existing behaviour.
- **Guest results are cached separately.** Queries run under the fallback credential get
their own cache partition, distinct from every logged-in username, so an embedded
viewer and a logged-in user never share a cached result (or, with
`GLOBAL_ASYNC_QUERIES`, a deduplicated query task) for the same chart. Missing or incomplete credentials also receive a separate cache key, so a
warm logged-in cache cannot bypass the missing-credential refusal.
- Turning the flag back off is a kill switch, not a deletion: the stored credential stops
being offered and stops being used, but is preserved.

---

## URL Parameters
Expand Down Expand Up @@ -214,6 +374,7 @@ One caveat when the host page lives on a different site than Superset: the defau
- **Row-level security** — pass `rls` rules in the guest token request to restrict which rows are visible to the embedded user. Semantic views cannot enforce guest-token SQL clauses, so queries and cached-result reads are rejected when a global rule or a rule scoped to the semantic view applies. For hosts, a global guest rule deliberately refuses every semantic view, and a dataset-scoped rule can also refuse a semantic view even when the rule was intended for a SQL dataset. Read permissions do not override these restrictions. Semantic views remain accessible when no guest rule applies; SQL datasets continue to enforce their applicable rules.
- **Allowed domains** — restrict which host origins can embed a dashboard by setting **Allowed Domains** per-dashboard in the _Embed_ settings modal. Superset checks the request's `Referer` header against this list before serving the embedded view; an empty list allows any origin, so configure this explicitly for production.
- **Redacted errors** — API responses to a guest token report a generic `An error occurred while fetching the data.` instead of the underlying error, since engine errors quote catalog, schema, table and column names. Errors Superset raises itself — access denials, timeouts, payload validation — keep their message, and the full error is always available in the server logs.
- **OAuth2 connections and guests** — a guest cannot complete an OAuth2 authorization, so Superset does not start one for them, and an embedded viewer on an OAuth2 connection with no applicable credential is never sent into a login flow they cannot finish. With `EMBEDDED_CREDENTIAL_FALLBACK` off, the connection attempt fails with the database driver's authentication error, and the viewer sees the generic `An error occurred while fetching the data.` message. With it on, Superset refuses the query before connecting, and the viewer sees a message saying the chart's database connection isn't set up for embedded dashboards, without naming the connection. Either way the reason is in the server logs. To serve embedded dashboards from such a connection, see [`EMBEDDED_CREDENTIAL_FALLBACK`](#embedded_credential_fallback).


## Guest-token request-header size diagnostics
Expand Down
Loading
Loading