Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feedback requested!] Proposal - Native authentication support for Streamlit #8518

Open
sfc-gh-jcarroll opened this issue Apr 17, 2024 · 14 comments
Labels
area:backend area:server type:enhancement Requests for feature enhancements or new features

Comments

@sfc-gh-jcarroll
Copy link
Collaborator

sfc-gh-jcarroll commented Apr 17, 2024

Summary

We are investigating approaches to add native authentication support for Streamlit, as it is one of the top most requested community features. This is a rough proposal for how it might work, which is shared for your feedback.

Goals

Goal 1: Provide the building blocks for integrating more robust auth solutions including:

Goal 2: Provide an out-of-the-box native integration which covers the most common use cases, especially for developers getting started. To that end, we propose standardizing on OAuth2 & OpenID Connect as the natively supported protocols in Streamlit.

  • This would be an opinionated and minimal implementation, geared towards integrating with standardized OIDC providers.
  • Goal would be, with a few lines of config / secrets, you can be set up with login() / logout() methods and the users verified email, OAuth2 token, and potentially other claims from a JWT readily available through a standard API (likely attributes on st.user).
  • Ideally, include an easy integration with cookies support so it's trivial to maintain the user auth session across tabs for some time. However, we still need to investigate whether this is feasible or has other major implications.

Why OpenID Connect (OIDC)?

  • From our perspective, OIDC seems like the most modern, standardized authentication protocol we can use which enables a standard format of user identity claims (including roles / groups) and minimal custom code across providers.
  • We also see a wide range of adoption across identity providers both in the consumer space (Google, Apple, etc) and enterprise space (Auth0, AWS Cognito, MSFT Entra ID, etc).
  • For most app developers who aren't working in a more sophisticated existing platform, it seems to cover the typical use case for "single sign on" for relatively low fuss and configuration required in the provider side.

Non-goal: Replace the use of reverse proxy in higher security environments

We know this practice of running Streamlit behind an auth proxy and forwarding authenticated requests to the app is fairly common among Streamlit usage in bigger companies or those with higher security requirements. We want to build library features that make that easier to run (such as documented headers support) but not replace it or build extremely hardened security into Streamlit, since it isn't our expertise.

But, we think this proposal can still unblock many developers operating in somewhat lower stakes environments who experience a lot of friction today.

VERY ROUGH API sketch

Here's a very rough first idea of how the API might work for the built-in OIDC.

# .streamlit/secrets.toml or similar

[auth] # reserved group for defining auth, could also support multiple like [auth.google]
type = "oidc"
provider = "google" # or api_base_url="accounts.google.com"
client_id = "SOME ID"
client_secret = "SOME SECRET"
default_ttl = "7d" # if we have some easy cookie support

Once this is configured, you can call some standard methods in the app which will redirect to the appropriate OIDC flow. Streamlit will automatically expose an OAuth2 callback endpoint which handles the response from the identity provider.

In the case below, calling login() will simply redirect to the OAuth2 flow. When the user returns from the flow, their identity values will be available in st.user. Calling logout() will clear the values from st.user in that session along with any browser cookies.

# streamlit_app.py

if st.user.logged_in(): # verify log in, including cookie from an earlier session
    st.write(f"Welcome, {st.user.name}!")
    st.sidebar.button("Logout", on_click=st.user.logout)
else:
    st.sidebar.button("Login", on_click=st.user.login)

Other authentication protocols

We know other protocols are used today, such as looking up a username / password in a database (e.g. Streamlit-Authenticator), or more complex delegated authentication schemes like SAML / LDAP. In some cases, the existing plugins / components for these can work pretty well. We'd like to provide some hooks to make them more consistent with the native support (maybe similar to st.connection), but it may not be in the initial launch.

What do you think?

If this proposal and approach sounds good and meets your use case, please give a 👍 . Also, we'd love to hear any comments about what you like, don't like, or would change, or any use case you have which would not be supported. Thanks!!

@sfc-gh-jcarroll sfc-gh-jcarroll added the type:enhancement Requests for feature enhancements or new features label Apr 17, 2024
Copy link

To help Streamlit prioritize this feature, react with a 👍 (thumbs up emoji) to the initial post.

Your vote helps us identify which enhancements matter most to our users.

Visits

@amanchaudhary-95
Copy link

I'm really excited about these features. Currently, I'm using Azure AD for login authentication and I'm managing user login by third party cookies manager and session state. With these feature, all of this can be done natively. st.user and st.cookie is much awaited feature for me.

As you are also developing st.redirect, please add a parameter target='_self' or target='_blank' to redirect in the same page or in new tab. The current redirect APIs such as st.link_button and st.page_link don't have this parameter and they by default open the link in new tab. Also, they open the link only after clicking them. The st.redirct should redirect at the link without clicking it, same as st.page_switch.

I hope to see these features very soon 😀😀

@Asaurus1
Copy link
Contributor

Would love support for ADFS/SSO auth so you don't have to stand up a whole separate endpoint in nginx just to get that one little thing.

@flrs
Copy link

flrs commented Apr 23, 2024

👍

@dkmiller
Copy link

Big +1. Bonus points if it's an extendable model, so the community can contribute standard auth providers, e.g. Google IAP, Microsoft Entra ID, etc.

@ranjith520
Copy link

ranjith520 commented Apr 27, 2024

I have created exactly this my own with azure ad and azure ad to control parts based on azure groups RBAC model for pages and components (functions) . It's working as expected once ready will release it GitHub. I m new to python but community can make it beetter.

How do I get to talk to streamlit to see how they can enhance it and release ?

@janaka
Copy link

janaka commented May 15, 2024

at a high-level sounds good.

not supporting federated enterprise SSO is fine but please make sure you don't block using something like WorkOS, BoxyHQ etc.

@link89
Copy link

link89 commented May 16, 2024

It would be better to break up this feature into 2 phases to implement

phase 1: cookie or token based session

streamlit should support cookie/token based session, which would allow user to keep state when refresh or reopen url.
This feature should have nothing to do with authentication,

phase 2: add auth support

implement authentication based on the APIs implemented in phase 1.

The phase 1 can be implemented very soon so that app developers can start to make use of it to solve their issues.
Then phase 2 can be gradually implemented.

@sayala-edison
Copy link

Hi! Are you considering to include Role-Based Access Control (RBAC) within the SSO authentication management?

Currently, while SSO protects access to the app, it does not support role-based page access, limiting our ability to tailor user experiences based on roles. We are trying to build that, and it would be extremly useful to be native as adhoc does not work as we would like to...

Current Challenges:

  1. General Page Access: The current configuration (config.toml) applies globally, allowing all authenticated users to access the same set of pages without differentiation by roles.
  2. Workaround Approaches:
    • Multiple TOML Files: We attempted to create multiple config files (config_id.toml) for each role defined in Azure. While this worked to an extent, it required all files to reside on the server, which complicates management and increases the risk of unauthorized access.
      When a user with a different assigned role logged in, as we load the pages for its TOML file, the available pages for previously logged users changed
    • Conditional Page Access: Our current approach involves displaying all pages in the sidebar but restricting access upon selection based on user roles. This results in a suboptimal user experience as users see pages they cannot access.

Proposed Solution:

Integrate role-based access control within the SSO authentication management, allowing the following:

  1. Role Assignment: Define roles within the app configuration or through SSO integration (e.g., Azure AD).
  2. Role-Based Page Access: Specify page access permissions within config.toml or another configuration file, restricting visibility and access based on user roles.
  3. Dynamic Sidebar: Automatically adjust the sidebar to show only the pages the logged-in user has access to, improving user experience and security.

Benefits:

  • Enhanced Security: Restrict access to sensitive pages based on user roles.
  • Improved User Experience: Users see only relevant pages, reducing confusion and navigation complexity.
  • Simplified Management: Centralized role definitions and access control, minimizing the need for multiple configuration files and reducing maintenance overhead.

@janaka
Copy link

janaka commented May 24, 2024

Just wanted to give this a heavier thumbs up. This is an important one. A concrete example of building in extensible layers. hence dropping down a layer becomes a first-class escape hatch. ST Native authN should use all the same first class APIs that a a third party integration can use.

This point also applies to the request for built-in authZ. FWIW I'm this appeared tomorrow I wouldn't switch. when I switch it will be ABAC and use a decoupled centralised policy-based solution like Open Policy Agent, Cerbos, Oso...

It would be better to break up this feature into 2 phases to implement

phase 1: cookie or token based session

streamlit should support cookie/token based session, which would allow user to keep state when refresh or reopen url. This feature should have nothing to do with authentication,

phase 2: add auth support

implement authentication based on the APIs implemented in phase 1.

The phase 1 can be implemented very soon so that app developers can start to make use of it to solve their issues. Then phase 2 can be gradually implemented.

@sfc-gh-jcarroll
Copy link
Collaborator Author

Thanks for the continued great feedback. Agree about providing lower level APIs and escape hatches.

For folks advocating for cookie or session based token where a user can keep state - how do you imagine that working? Is storing some app state in local storage enough? I'm not sure that Streamlit will be in the business of keeping a session state in memory or providing some persistence layer after a user leaves, although we could think about how to make that easier to plug in.

For discussion on RBAC:

  • In Streamlit, I'm not sure how we could effectively go lower level than controlling it by page. If someone sees a way to go lower level or has a specific suggestion (ideally an API example snippet :) ) it's very welcome.
  • The current proposal includes the role information such as in a JWT propagating to st.user and being available for easy querying. To control pages dynamically, check out the nearly complete feature in [Coming soon] Multi-page apps: Improved API and new navigation UI features #8388 for dynamic pages API and you can see a WHL file to play around with.
  • I believe that with st.navigation() + roles/scope information in st.user it becomes fairly trivial to implement RBAC at a page level in one place in your app. Thoughts? Does it make sense?

It's interesting about Cerbos or some of the decoupled policy-based solutions. I'm not sure we will build it into Streamlit since so many others are working on great solutions. But I could see how that could be layered in with the features above and make a great tutorial or example someone can start from. 🤔 Maybe it's enough.

Thank you!

@janaka
Copy link

janaka commented May 25, 2024

@sfc-gh-jcarroll I don't have a concrete design idea. But...

  • cookie or token based session - give first-class access to managing 1) cookies 2) HTTP headers of all kinds at certain points of the page request lifecycle. If you look at workaround implementation, everywhere that an unsupported API is used, that's you hint. Everytime you think "ooh that wasn't design for this, that's internal, that could break during an ST refactor" that's another hint. If you enable first class API that hook into the underlying HTTP server request life cycle, routing etc. that unlocks a lot, maybe all advanced use cases. Of cause ST in batteries included, so a newbie to St wouldn't need to worry about any of that until they need to get more advanced.

  • AuthZ - from my comment I wasn't implying anything that's third party specific. Just don't block (unintentionally of course). End of the day authZ enforcement comes down to allow/deny based on a boolean. That's the generic integration interface. Hot take: If every St UI component has a disable prop and another bool prop that prevents rendering that's might be sufficient. It's many important for components that black box tool much e.g. Sidenav.

Apologies if you know this already.

Thanks for the continued great feedback. Agree about providing lower level APIs and escape hatches.

For folks advocating for cookie or session based token where a user can keep state - how do you imagine that working? Is storing some app state in local storage enough? I'm not sure that Streamlit will be in the business of keeping a session state in memory or providing some persistence layer after a user leaves, although we could think about how to make that easier to plug in.

For discussion on RBAC:

  • In Streamlit, I'm not sure how we could effectively go lower level than controlling it by page. If someone sees a way to go lower level or has a specific suggestion (ideally an API example snippet :) ) it's very welcome.
  • The current proposal includes the role information such as in a JWT propagating to st.user and being available for easy querying. To control pages dynamically, check out the nearly complete feature in [Coming soon] Multi-page apps: Improved API and new navigation UI features #8388 for dynamic pages API and you can see a WHL file to play around with.
  • I believe that with st.navigation() + roles/scope information in st.user it becomes fairly trivial to implement RBAC at a page level in one place in your app. Thoughts? Does it make sense?

It's interesting about Cerbos or some of the decoupled policy-based solutions. I'm not sure we will build it into Streamlit since so many others are working on great solutions. But I could see how that could be layered in with the features above and make a great tutorial or example someone can start from. 🤔 Maybe it's enough.

Thank you!

@ranjith520
Copy link

ranjith520 commented May 25, 2024 via email

@Aniket-Pradhan
Copy link

Regarding the cookies aspect:

For folks advocating for cookie or session based token where a user can keep state - how do you imagine that working? Is storing some app state in local storage enough?

Yep, a basic form of cookie support would be perfect. Something that isn't lost on a browser refresh or when opening a new instance of the app in a separate tab. Unlike st.session_state, which loses the context on browser refresh/new app instance.

I'm not sure that Streamlit will be in the business of keeping a session state in memory or providing some persistence layer after a user leaves.

We are currently using st.session_state to maintain a user's auth credentials, which is perfect but not ideal, as the user must sign-in again when the browser is refreshed. Implementing auth without cookies will pose such challenges and I'm not sure as to how >streamlit< solve this problem?

although we could think about how to make that easier to plug in.

There are many third-party plugins/wrappers for streamlit which get and set cookies via some js library like streamlit-cookies-controller which makes use of universal-cookie. I would have to go through Streamlit's code to understand how things work here, but something like this can do the trick.

One of the problems with such third-party wrappers is that they trigger an app refresh whenever setting/getting the cookies, which is something that can be avoided if we internalize it within Streamlit and make things more accessible for the devs.

I would be happy to contribute to this use-case since we use Streamlit extensively in our org, and would love to have this feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:backend area:server type:enhancement Requests for feature enhancements or new features
Projects
None yet
Development

No branches or pull requests

12 participants