Has anyone managed to succesfully implement the Azure Active Directory authentication to your Streamlit app? More specifically, I’m trying to implement the initiate_auth_code_flow() method from the MSAL library to start a user-driven authentication process (as-per Microsofts’ recommendation). I’ve made an App Registratoin in Azure, added a redirect URI (as localhost:8501 for dev. purposes), and added SPA as a platform under Authentication.
Currently, I’m able to use the library by hand (in the backend), but I’m struggling a lot with handling it using the Streamlit frontend.
The issue arrises when clikcing a button that triggers the login(). This correctly imposes the initiate_auth_code_flow() method, which returns a flow. If the auth_uri exists in the flow dict, the user is redirected to Microsofts sign in page. Upon succesful sign in, the user is redirected back to the localhost:8501 with the auth code as a query parameter. However, I don’t know how to pick up this parameter and use it in the acquire_token_by_authorization_code() while maintaining the session, because the auth_uri is generated in one tab, but then auth code parameter is returned upon redirect in a new tab, which ultimately creates a new session/state, thus I get a mismatch.
How do I manage this mismatch? Any ideas?
One idea could be to create a custom endpoint using FastAPI/Flask/etc so receive the parameters, and then login the user. But is there an easier/more Streamlit-native way?
import streamlit as st
import msal
import webbrowser
import requests
client_id = "xxxx"
tenant_id = "yyy"
redirect_uri = "http://localhost:8501/"
scopes = ["https://graph.microsoft.com/.default"]
authority = f"https://login.microsoftonline.com/{tenant_id}"
endpoint = "https://graph.microsoft.com/v1.0/me"
app = msal.PublicClientApplication(
client_id, authority=authority, verify=False
)
def get_token_from_cache():
accounts = app.get_accounts()
if not accounts:
return None
result = app.acquire_token_silent(scopes, account=accounts[0])
if "access_token" in result:
return result["access_token"]
else:
return None
def login():
flow = app.initiate_auth_code_flow(scopes=scopes, state=['somestupidstate'])
if "auth_uri" not in flow:
return st.write("Failed with token")
auth_uri = flow['auth_uri']
webbrowser.open(auth_uri, new=0)
auth_code = st.experimental_get_query_params()
if 'code' not in auth_code:
return st.write("Failed with token")
result = app.acquire_token_by_authorization_code(auth_code, scopes=scopes)
if "access_token" in result:
return result["access_token"]
else:
return st.write("No token found")
if st.button("Login"):
get_token_from_cache()
token = login()
st.write(st.experimental_get_query_params())
if token:
st.write("Logged in successfully!")
st.write(token)
else:
st.write("Failed to login")
Okay, so I managed to solve this myself, partly thanks to altering my code, and partly thanks to Stackoverflow.
I opted for Selenium for handling my browser object, so my login() method looks like the one below. Furthermore, according to this post, the documentation on MSAL is a bit confusing. Especially regarding what platform to add in the App Registration. Using Selenium might be redundant however, since I added that before changing the platform settings.
def login():
flow = app.initiate_auth_code_flow(
scopes=scopes)
if "auth_uri" not in flow:
return st.write("Failed with token")
auth_uri = flow["auth_uri"]
browser = webdriver.Chrome()
browser.get(auth_uri)
WebDriverWait(browser, 200).until(
EC.url_contains(redirect_uri))
redirected_url = browser.current_url
url = urllib.parse.urlparse(redirected_url)
# parse the query string to get a dictionary of {key: value}
query_params = dict(urllib.parse.parse_qsl(url.query))
#code = query_params.get('code')[0]
result = app.acquire_token_by_auth_code_flow(flow,query_params, scopes=scopes)
browser.quit()
return result
I recently made this Streamlit Component that builds on top of Microsofts own MSAL.JS library: msal-streamlit-authentication: (PyPI). You can find the Github source code via the link if you want.
I have at least managed to get login working using a popup instead of redirect, but I have not testet if it also works with redirects.
Hi, I give your component a try but I cannot get the login token back. I tried the snippet in the readme and the pop-up windows seems to work ok (It pops up and redirects to the redirect uri without error and closes itself) but login token is always None.
In my own experience, if you develop on a local machine, you will have to add e.g. ‘http://localhost:1234’ as an accepted redirect URL to your login provider if your local streamlit app runs on port 1234 and on HTTP. You will of course have to adjust the exact port and protocol for your needs. The thread in question covers how to do this in the Azure portal, if your app uses Azure AD, but similar approaches should be possible via other OIDC providers. Which provider do you use? Please let me know how it ends.
I will try to see if I can update the component so that it does not fail silently but rather extends whatever error message the auth provider sends you. I think (at least) that would allow for more transparency and troubleshooting.
I just got it sorted. I was using the wrong platform setup for Azure AD. It works as expected as soon as I change the platform type to SPA. Thanks for your help!
You are welcome - sounds like a similar issue also described in the other thread. Great that it works! Feel free to share feedback if you want, it is still in an early stage.
I think it is a good suggestion. I will try to find some time to make that adjustment. I thought there was some “beauty” in the current simplicity, but I will try to consider what I think makes the most sense to do. For starters, this was actually meant as a POC on how to mak a custom Streamlit Component, and my team just happened to want some authentication mechanism.
Sorry, I am very new to the Python and Streamlit. Could anyone please explain how to use the CSS class name and HTML id for the login and logout button style customization with this custom component? I couldn’t able to understand from the documents.
Hi @mstaal, I am using this library for authentication. However, whenever I open the page containing the authentication code, it re-runs the entire authentication logic. is there a way to stop happening this
I’ve successfully integrated this library into my application, and it functions flawlessly. However, I’m exploring ways to streamline the authentication process. Presently, the authentication occurs only upon clicking the login button. What I aim for is a seamless authentication experience where the entire process initiates automatically upon loading the web URL. Is there a way to override the login button functionality to achieve this?
This is amazing! We have a project however which would require login without a pop up. Can you provide the steps/commands to set up your component on our system, so that we can customize it to our use-case and build it, to be integrated with our web-app? Thanks.
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.