I am deploying a streamlit app as an azure webapp to azure.
I succesfully configured Active Directory to handle user sign-in and then redirect to my app.
Now I would like to access the name of the user that signed in to store it together with the entered data of the user in a database.
The method that I found to get the user name is to run this javascript snippet in my browser console:
However, I am unsure how I could access this data on the python side where the database entries are made.
Do I need to make a small custom component for this which runs the snippet and sends it to the python backend?
Hi,
this is exactly the same problem that I have! Did you solve it in the meantime?
Maybe we can work together. I tried using the microsoft authentication library (msal) for python.
It works to some extent…
Azure populates some environment variables that I retrieved, I defined the others:
APPLICATION_PERMISSIONS = ["https://database.windows.net//.default"]
CLIENT_ID = os.environ["WEBSITE_AUTH_CLIENT_ID"]
AUTHORITY = "https://login.microsoftonline.com/<some code here (client or App ID?)>"
CLIENT_SECRET = os.environ["MICROSOFT_PROVIDER_AUTHENTICATION_SECRET"]
I manage to receive a bearer or access token, telling me that someone authenticated, but not who.
result = client_app.acquire_token_for_client(scopes=APPLICATION_PERMISSIONS)
token = result['access_token']
token_decoded = base64.b64decode(token)
I am stuck requesting an id token to actually identify the authenticated user. I guess that you do it like this (I defined the tasks.read scope in the azure portal):
Yeah I did find a solution to read the user email from the http headers:
import streamlit as st
from streamlit.script_run_context import get_script_run_ctx
from streamlit.server.server import Server
def read_aad_username():
session_id = get_script_run_ctx().session_id
session_info = Server.get_current()._get_session_info(session_id)
headers = session_info.ws.request.headers
headers = headers._dict
if "X-Ms-Client-Principal-Name" in headers:
username = headers["X-Ms-Client-Principal-Name"]
st.write(f"Logged in as {username}")
else:
st.warning(f"could not directly read username from azure active directory.")
username = None
return username
If you need more info about the user then you have to send a query to the “/.auth/me” endpoint with the auth-token in the http header. You will also have to give your apps’ service principal the necessary rights to read that data about the user.
However, if you are running a Linux WebApp in azure, be aware of a current bug in active directory which makes it necessary for users to delete browser cookies after a restart: Restarting Azure App Service on Linux with Azure Active Directory authentication resets /.auth/me/
import streamlit as st
from streamlit_javascript import st_javascript
def read_aad_username():
js_code = """(await fetch("/.auth/me")
.then(function(response) {return response.json();}).then(function(body) {return body;}))
"""
return_value = st_javascript(js_code)
username = None
if return_value == 0:
pass # this is the result before the actual value is returned
elif isinstance(return_value, list) and len(return_value) > 0:
username = return_value[0]["user_id"]
st.write(f"Logged in as {username}")
else:
st.warning(f"could not directly read username from azure active directory: {return_value}.")
st.warning(f"A workaround to this is to clear your browser cookies for this site and reloading it.")
return username
But that is only needed if you need to know more than just the user email address in which case the first version is enough.
At least the first version works in reading only the username.
The other one always fails, even if I delete all cookies and user incognito browsing mode.
But I am good for now with only the username!
for the second one to work your web-app will need to be configured with the correct permissions, otherwise you will always get an empty list from the /.auth/me endpoint.
Relying on a HTTP header such as X-Ms-Client-Principal-Name to carry identity information is not secure - they can be faked so, so easily, and in effect impersonate a user.
Then, as long as you have a valid token from the issuer you are expecting (i.e. Microsoft and specifically your tenant), you can get the email claim from the access token returned by acquire_token_by_auth_code_flow.
In the code you quote above, acquire_token_for_client doesn’t, I think, get the token you are expecting - this method is for offline daemon apps (e.g. system components) to authenticate, NOT authenticating on behalf of a user. You should be using acquire_token_by_auth_code_flow if you are expecting to authorize on behalf of an authenticated user.
Hi @Kevin_Dixon, thank you very much for your input.
While I also don’t like relying on http headers, it seemed to be the most robust solution for my usecase due to the Microsoft bug mentioned before.
Regarding the fakability of the X-Ms-Client-Principal-Name header, I’m no specialist regarding that but it isn’t set by the client but by the app-service-auth-session wrapper based on the Active Directory login that the user is using. Can you describe how you would fake that?
Hi @thunderbug1 without getting too much into the dark arts, its very simple with browser development tools or indeed cli tools as simple as curl to modify headers. With token in hand for a user, it would be a simple deal to use that token but modify the header and thus pretend to be another user (all this outside of Streamlit).
Not tried it, so you never know I might be wrong. But… you have a secure and verifiable token, so why not use that to carry your identity information.
yeah, I think just setting it in the client request wouldn’t work. This is what the documentation says about it:
Access user claims in app code
For all language frameworks, App Service makes the claims in the incoming token (whether from an authenticated end user or a client application) available to your code by injecting them into the request headers. External requests aren’t allowed to set these headers, so they are present only if set by App Service. Some example headers include:
Hey Goyo - thanks for your prompt reply. However, I can’t really figure out, how this helps me authentication the end user with Microsoft Active Directory? Can you be a little me specific?
If you just want something basic like the logged in user email this should be enough (in version 1.14+):
from streamlit.web.server.websocket_headers import _get_websocket_headers
headers = _get_websocket_headers()
if "X-Ms-Client-Principal-Name" in headers:
user_email = headers["X-Ms-Client-Principal-Name"]
st.write(headers) # have a look at what else is in the dict
I have not yet figured out how to access app roles and such though. I assume you would have to delve into the Azure AD / MSAL documentation, maybe someone else here is up to speed.
I know I am a little late to the party, but since I wanted an authentication component for Streamlit myself (and could not immediately find something that did what I wanted), I ended up making one myself:
It’s based on Microsoft’s own MSAL.JS library, and it can be configured like its Javascript equivalent from the Python side. In particular, it supports Azure AD (as well as the Open ID Connect protocol in general). Once a user is logged in, the component returns the entire login token to Streamlit, allowing users to use whatever pieces (like the access token) if one wants to do this. I guess it can also be used to manipulate the Streamlit session.
Thanks for this one, but I have trouble getting a token from this. Upon clicking the Login button, it opens a new tab and authenticates, but the streamlit page doesn’t get updated to show the token. Am I doing something wrong with the redirect URI? or it should be set to '' as it is.
As illustrated in the README on Github, the token will (once it is set after a valid login) live in the dictionary corresponding to the python component object. You can then use the contents of the the token however you want (for instance to display your username or to invoke an API that requries an Access token).
Do you know what could be the reason for getting an error AADSTS9002326: Cross-origin token redemption is permitted only for the ‘Single-Page Application’ client-type. Request origin: when trying to login?
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.