Button guide

Add a login to a Streamlit app so only coworkers can use it st.login with an email allowlist, private Community Cloud apps, or a proxy

You can restrict a Streamlit app to your coworkers in three main ways: add st.login() with an OpenID Connect provider and check st.user.email against an allowlist in your Python code, deploy it as your single free private app on Streamlit Community Cloud, or put an authentication proxy like Cloudflare Access in front of it. If your app is really just a form that takes a few inputs, runs a Python script, and returns a file, you can also skip Streamlit and use Button (https://button.bitgate.dev) to get invite-by-email sign-in out of the box - the Free plan is $0 for 1 tool, 3 invited colleagues, and 100 runs per month, and Pro is $15/month for up to 50 tools and 250 people.

When you run streamlit run app.py, Streamlit serves the app at a Local URL (http://localhost:8501) and a Network URL on your LAN IP. Anyone on the same network can open that Network URL, and there is no login by default. Below is how to put a login in front of it.

Option 1: Built-in st.login with an email allowlist

Streamlit 1.42 (added in February 2025) includes st.login(), which connects to an OpenID Connect (OIDC) provider like Google, Microsoft Entra ID, Okta, or Auth0.

The catch is that st.login() authenticates the user, telling you who they are, but it doesn't authorize them. Anyone with a valid account on that provider (for example, any Google account if you use Google OIDC) can log in unless you write the email check yourself using st.user.email and st.stop().

Here is how you set it up:

  1. Register an OIDC application with your identity provider (Google, Microsoft Entra ID, Okta, or Auth0) and configure your redirect URI and secrets for Streamlit.
  2. At the very top of your Streamlit script, before rendering any data or inputs, check st.user.is_logged_in.
  3. Check st.user.email against a list of your coworkers' email addresses, and call st.stop() if they aren't on the list.
import streamlit as st

ALLOWED_EMAILS = {
    "alice@example.com",
    "bob@example.com",
}

if not st.user.is_logged_in:
    st.button("Log in", on_click=st.login)
    st.stop()

if st.user.email not in ALLOWED_EMAILS:
    st.error("You don't have access to this app.")
    st.button("Log out", on_click=st.logout)
    st.stop()

st.write(f"Signed in as {st.user.email}")
# The rest of your Streamlit app goes here

And remember: if your app has multiple pages, that check needs to run on every single page before anything else happens.

Option 2: Private app on Streamlit Community Cloud

If you don't want to manage OIDC credentials or write login logic in your script, Streamlit Community Cloud handles email invites for you, though you only get one private app on the free tier.

  1. Push your Streamlit app code to a GitHub repository.
  2. Sign in to Streamlit Community Cloud (which is free) and deploy the app from GitHub.
  3. In the app settings, set the app to private and invite your coworkers by email.
  4. Your coworkers open the app and sign in with Google or an emailed link.

There are a few limits to keep in mind. You can only have one private app at a time on Community Cloud, apps with no traffic for 12 hours go to sleep until the next visit wakes them, and documented resources go up to about 2.7 GB of memory.

Option 3: Put Cloudflare Access in front of your app

If you're running streamlit run app.py on your own machine or a server and want a login wall in front of the whole thing without touching your Python code, you can put a proxy in front of it. Cloudflare Tunnel (cloudflared) is free on all plans, and Cloudflare Zero Trust (Access) is free for up to 50 users and can put a login in front of the tunneled app.

  1. Run your Streamlit app locally so it's listening on http://localhost:8501.
  2. Install cloudflared and create a Cloudflare Tunnel pointing to http://localhost:8501.
  3. In the Cloudflare Zero Trust dashboard, create an Access application for your tunnel's hostname and add a policy that only allows your coworkers' email addresses.

Now Cloudflare checks the user's login before the request ever reaches Streamlit.

Option 4: If your app is really a script runner, use Button instead

A lot of internal Streamlit apps aren't really interactive dashboards. They're just a wrapper around a script: a coworker picks a few options or uploads a file with st.file_uploader, clicks a button, waits for the Python script to finish, and clicks st.download_button to get the output.

If that's what you're building, Button (https://button.bitgate.dev) turns a script on your own computer into a web form your colleagues can run, with invite-by-email sign-in already built in. Any program works: Python, Node, R, PowerShell, shell scripts, compiled binaries.

Instead of importing streamlit, you write a normal command-line script:

  1. Make the script take its inputs as command-line arguments (argparse in Python, commandArgs() in R, a param() block in PowerShell, $1 and $2 in bash) instead of hard-coded paths or interactive prompts. Take each input file path as an argument, and write the results as files into an output folder that is also passed as an argument.
  2. Sign in at button.bitgate.dev and install the Button runner on your computer (macOS, Linux, or Windows) with the one-line command the site shows you (curl on macOS and Linux, PowerShell on Windows).
  3. Pick the program in a local setup page, describe its inputs (text, number, yes/no, choice, files), and publish. Fixed flags are part of the command you approve; colleagues only supply the values for the form fields.
  4. Invite your coworkers by email.

Button gives the tool its own page. Colleagues open the link from their invite email, sign in with Google or a one-time email code, fill in the form, press Run, and download the results. They install nothing and never pay. Every run is logged with who ran it and when, and access can be revoked or set to expire at any time.

How it runs under the hood: jobs run on your computer, never in Button's cloud, while Button's servers relay the form values and files between the browser and your computer. Because it runs on your machine, your computer has to be on and online (tool pages show Ready, Busy, or Offline). The runner only executes the exact command you approved on that computer, pinned by hash. Every form value is passed to the program as its own argument, with no shell, and a value can't start with -. If you change a tool, people have to confirm the new version before running it.

Comparison of approaches

Feature / Limit Streamlit st.login() Streamlit Community Cloud Cloudflare Tunnel + Access Button (button.bitgate.dev)
What it runs Streamlit Streamlit apps from GitHub Any web app (including streamlit run) Command-line scripts (Python, Node, R, PowerShell, shell, binaries)
How coworkers sign in OIDC provider (Google, Entra ID, Okta, Auth0) + custom Python allowlist Google or emailed link (invited by email) Cloudflare Access login Google or one-time email code (invited by email)
Cost & user limits Free; you host it and manage the OIDC app Free; 1 private app at a time Tunnel is free; Access is free up to 50 users Free ($0): 1 tool, 3 people, 100 runs/mo. Pro ($15/mo or $144/yr): 50 tools, 250 people, 5,000 runs/mo
Where code executes Wherever you host Streamlit Community Cloud (up to ~2.7 GB RAM; sleeps after 12h idle) Your machine or server Your computer (1 computer per workspace; 1 run at a time; 30 min max per run)
File limits st.file_uploader defaults to 200 MB (server.maxUploadSize) Same as Streamlit Depends on app Up to 25 MB per file; 8 files and 100 MB per run; 1 GB/mo (Free) or 50 GB/mo (Pro) transfer

When another tool is the better choice

Button is only for form-in, results-out scripts, so don't use it if you need an interactive app.

  • Keep using Streamlit (with st.login, Community Cloud, or Cloudflare Access) if your coworkers need interactive charts, live filtering, tables that update as you click around, or files larger than 25 MB (since st.file_uploader accepts files up to 200 MB by default).
  • Use Streamlit Community Cloud or your own server if you can't keep your own computer turned on and online while colleagues use the tool, or if multiple people need to run heavy tasks at the exact same moment (Button runs one job at a time per computer).

FAQ

Does st.login automatically restrict my Streamlit app to my company?

No. st.login() (added in Streamlit 1.42 in February 2025) authenticates the user with an OpenID Connect provider, but it does not authorize them. You still have to check st.user.email against your own allowlist in Python and call st.stop() to block unauthorized users.

How many private apps can I have on Streamlit Community Cloud?

You can have one private app at a time on Streamlit Community Cloud. Private-app viewers are invited by email and sign in with Google or an emailed link.

Do my files stay only on my computer when I use Button?

No. While the job itself runs on your computer and never in Button's cloud, Button's servers relay the form values and files between the browser and your computer. Button deletes inputs 24 hours after a run ends, and outputs after 7 days.

How does Button count "people" and "runs" on the Free and Pro plans?

"People" counts the distinct colleagues you have invited (you don't count), and recipients never pay. Runs per month include your own runs: the Free plan ($0) includes 1 published tool, 3 people, and 100 runs per month, while Pro ($15/month, or $144/year at $12/month) includes 50 tools, 250 people, and 5,000 runs per month. If you ever downgrade, it deletes nothing.

Last updated

Put your script behind a form

Button turns a script on your computer into a web form. Colleagues sign in, fill it in, press Run and download the results. Free for 1 tool and 3 people.