Embed a Streamlit in Snowflake app for viewers without a Snowflake account¶
This tutorial embeds a Streamlit in Snowflake app in an external page for viewers who don’t sign in to Snowflake, such as a customer-facing dashboard on a public portal or a status page. The app runs as a service user you create, so every viewer sees the same data.
If each viewer should instead see data scoped to their own Snowflake privileges, follow Embed a Streamlit in Snowflake app for viewers with a Snowflake account instead. For background on the two models, see Embedding Streamlit in Snowflake apps in external pages.
Before you begin¶
- You need an existing Streamlit in Snowflake app on a container runtime. If you don’t have one, see Getting started with Streamlit in Snowflake. To check or change an app’s runtime, see Runtime environments for Streamlit apps.
- You need the ACCOUNTADMIN role, or another role with the ALTER ACCOUNT privilege, to register allowed embedding domains.
- Your web server must be able to make outbound HTTPS calls to Snowflake.
- Your page must be served over HTTPS from the same origin you register. For local development, an
http://localhostorigin is also allowed.
Step 1: Register allowed embedding domains¶
Register the external domains where embedded apps are allowed to load by setting the
STREAMLIT_EMBEDDING_CONTROLS account parameter. Embedding only works for the domains you register
here. Run the following as ACCOUNTADMIN:
Each entry must be an absolute HTTPS origin: https://host or https://host:port. Wildcards, paths,
and query strings aren’t supported. Matching ignores scheme and host case, default ports, and trailing
slashes.
For local development, you can also register a localhost origin over http, for example
http://localhost:3000. All other hosts must use https.
Snowflake validates the document when you set the parameter, so malformed YAML, an unrecognized top-level key, or an invalid origin is rejected before the value is saved.
Step 2: Create the service user and grant privileges¶
Your backend needs a Snowflake identity to mint embed URLs with. Use a TYPE = SERVICE user with a
narrow, dedicated role, because it only needs to reach the one app you’re embedding.
The service user doesn’t need a warehouse. Minting an embed URL doesn’t open a SQL session.
The role that mints the embed URL needs two privileges on the app:
- USAGE, to resolve the app. A role without it gets the same response as if the app didn’t exist.
- EMBED, to mint an embed URL for it.
Important
Owning the app doesn’t imply EMBED. The app owner must be granted EMBED explicitly, the same as any other role.
Because EMBED is granted separately from USAGE, you can share an app with a role for viewing in Snowsight without also letting that role publish it to an external page. To stop a role from minting embed URLs while leaving its ability to view the app intact, revoke EMBED on its own:
For more information, see Privileges required to create and use a Streamlit app.
Important
Every viewer of the embedded app shares this role’s access. Grant it USAGE and EMBED on the one app you intend to embed, and nothing else, because the viewer session inherits the role the URL was minted under.
Step 3: Configure credentials¶
Choose the credential type that fits where your backend runs, then finish configuring the embed_svc
user you created in Step 2.
A programmatic access token is the simplest option. The service user needs a network policy before you can add one.
- Create a network policy that restricts connections to your backend’s egress IP range:
- Generate the token. The secret is shown exactly once, so copy it before you close the results:
- Store
token_secretin your backend’s secret store. Never commit it to source control.
For more information, see Using programmatic access tokens for authentication.
Key-pair authentication avoids a shared secret and supports key rotation.
- Generate an encrypted RSA key pair:
- Register the public key on the service user:
- Verify the fingerprint:
Store the private key file securely on your backend. Your backend signs a short-lived JWT with it on each mint. For more information, see Key-pair authentication and key-pair rotation.
Workload identity federation lets a workload authenticate using its cloud provider’s native identity, such as an AWS IAM role, a Microsoft Entra ID identity, a GCP service account, or any OIDC issuer, so there’s no long-lived secret to store.
-
As a workload administrator, configure your service so its provider can issue an attestation of the workload’s identity.
-
As a Snowflake administrator, map that identity onto the
embed_svcuser you already created. For example, with an OIDC issuer:
Because WIF authenticates with a live federated token, your backend must run in the environment that issues it. For more information, see Workload identity federation.
Step 4: Mint the embed URL from your backend¶
Your backend mints an embed URL by calling the following endpoint. No SQL session and no Snowflake driver are required.
Send the following headers:
| Header | Value |
|---|---|
Content-Type | application/json |
Authorization | Bearer <token>, where the token depends on your credential type |
X-Snowflake-Authorization-Token-Type | The credential type, such as PROGRAMMATIC_ACCESS_TOKEN, KEYPAIR_JWT, OAUTH, or WORKLOAD_IDENTITY_FEDERATION |
X-Snowflake-Role | The role to resolve and mint the app under |
X-Snowflake-Role is required. The endpoint doesn’t fall back to the user’s default role or to
PUBLIC, and a request without it fails with 400. The role you name must be granted to the
authenticating user and must hold USAGE and EMBED on the app.
The request body names the origin that will host the iframe:
The origin must be registered in allowed_embedding_domains and must be the exact origin the browser
loads your page from. The response is the embed URL:
Important
Treat the embed URL as a bearer credential. It grants a viewer session for the app with the privileges of the role it was minted under. The authorization code it carries is valid for 10 minutes, is single-use, and is invalidated the first time it’s redeemed. Don’t log, cache, or persist it. Mint a fresh URL for each embed session, and never expose your minting endpoint publicly or without authentication.
Each credential type is the same request with a different Authorization header:
| Credential | Authorization | X-Snowflake-Authorization-Token-Type |
|---|---|---|
| PAT | Bearer <token_secret> | PROGRAMMATIC_ACCESS_TOKEN |
| Key-pair JWT | Bearer <RS256 JWT> | KEYPAIR_JWT |
| WIF | Bearer WIF.<AWS|AZURE|GCP|OIDC>.<token> | WORKLOAD_IDENTITY_FEDERATION |
The following examples use a Next.js route handler. Any server-side language works, because the endpoint takes a plain HTTPS POST.
Set environment variables in .env.local. Never commit this file, and store these values in a secret
store when you deploy:
Set environment variables in .env.local. Never commit this file or your private key:
With WIF the identity is carried in the token, so there’s no user name to configure:
Step 5: Render the app in an iframe¶
Pass the embed URL from your backend to the src attribute of an <iframe>.
Keep the following in mind:
- The page must be served from the exact origin you passed as
parent_originwhen you minted the URL. A non-matching origin is rejected when the app renders, even though the URL minted successfully. - Mint the URL once per embed session. Fetching it with
cache: "no-store", and guarding against a second fetch, keeps a component that mounts twice from spending the single-use code. - If your app uses a download button such as
st.download_button, and you set thesandboxattribute on the iframe, includeallow-downloads. Otherwise the browser blocks the download. - If your app uses a widget that captures audio or video, such as
st.audio_inputorst.camera_input, and you set thesandboxattribute, includeallow-popups. Otherwise the browser blocks the prompt that asks for microphone or camera access.
Listen for lifecycle events¶
The embedded app sends postMessage events to the parent window, so your page can tell the difference
between an app that is still starting and one that failed.
| Event | When it fires |
|---|---|
SNOWFLAKE_EMBED_LOADED | The app is fully rendered and interactive. |
SNOWFLAKE_EMBED_ERROR | The app failed to load. |
SNOWFLAKE_EMBED_SUSPENDED | The app was suspended due to inactivity. |
SNOWFLAKE_EMBED_SESSION_EXPIRED | The embed session expired and can no longer be refreshed. |
Each message has a type field, a timestamp in Unix milliseconds, and an optional appId that
identifies which app sent the event when you embed more than one app on a page.
What’s next?¶
- Troubleshoot embedded Streamlit in Snowflake apps: Diagnose mint failures, blank iframes, and apps that load for some viewers but not others.
- Security considerations: What the embedding domain list does and doesn’t protect against before you expose sensitive data.
- Embed a Streamlit in Snowflake app for viewers with a Snowflake account: Scope each viewer’s data to their own Snowflake privileges instead.
- Embedding samples: Runnable backends for each credential type, including a zero-dependency Node example.