-
Notifications
You must be signed in to change notification settings - Fork 4k
Make st.logout use end_session_endpoint if provided in OIDC config #11901
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
Conversation
🎉 Snyk checks have passed. No issues have been found so far.✅ security/snyk check is complete. No issues have been found. (View Details) ✅ license/snyk check is complete. No issues have been found. (View Details) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this looks reasonable to me, but let me see if I can get a second reviewer from the team that's more familiar with this feature.
I left a few comments with a few nits from a first pass. Note that I haven't given the tests a careful read yet as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR enhances the logout flow to support OIDC end_session_endpoint and records the OIDC provider in the session cookie.
- Store the
providerfield in the auth cookie during login. - Extend
AuthLogoutHandlerto clear the cookie and redirect to the provider’send_session_endpointif present. - Add new tests for logout with and without an end_session_endpoint, and update the
logoutdocstring.
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| lib/streamlit/web/server/oauth_authlib_routes.py | Implement _get_provider_logout_url, always clear the auth cookie, and redirect to OIDC logout |
| lib/tests/streamlit/web/server/oauth_authlib_routes_test.py | Add tests for logout behavior when an end_session_endpoint is provided or missing |
| lib/streamlit/user_info.py | Update logout() docstring to describe remote OIDC logout support |
Comments suppressed due to low confidence (1)
lib/streamlit/web/server/oauth_authlib_routes.py:171
- Consider adding a test case to cover the branch where the auth cookie does not include an "origin" field, ensuring that the fallback to
self.request.protocolandself.request.hostis exercised.
origin = user_info.get("origin")
Copilot
AI
Jul 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The test explicitly sets cookie_secret on a new Application instance via self.get_app(), but the AsyncHTTPTestCase harness already applies this secret returned by get_app for handling secure cookies. You can remove this redundant assignment to simplify the test.
| self.get_app().settings["cookie_secret"] = "test_cookie_secret" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Wouldn't it be possible to construct this from the configured redirect_uri (the url without /oauth2callback) which I think is required anyways? just protocol and host (or just origin) might be wrong in some setups. E.g. if app is deployed under an url path via a proxy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very good catch and calling it a nit really does not do it justice.
My guess is that without the fix, the code would actually fail under some deployment scenarios.
Thank you for noticing it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there might still be some deployment scenarios where the current get_origin_from_secrets implementation won't work here. Redirecting to the origin of our redirect_uri may not work in cases where the app isn't being served from the root path of its origin. For example, it's possible to set up an app served from https://my.streamlit.app/path/to/app with redirect url https://my.streamlit.app/path/to/app/oauth2callback for which the redirect after logout will break.
It might be fine to merge this change as-is and make a quick followup since the end_session_endpoint doesn't work at the moment anyway, but it'd be slightly preferable to fix this in this PR.
(This might change the correct way to approach my other comment)
|
Thank you for the quick reply @vdonato ! Do you happen to know what the general policy is here on PRs in terms of addessing multiple issues. I'm actually thinking of adding the code to also handle #10378 as well as introduce a way to refresh the user data (in case the user changes their settings such as language). The changes will basically impact the same files and should be easy to review together with what is already there. I can of course wait for this to get merged and/or do parallel PR-s as well, but doing them together seems a bit more efficient. |
|
Hey @velochy, I think #10378 is a sufficiently disjoint issue (in terms of functionality) that I'd prefer having a fix for it go in as a second PR, although in general we're not always opposed to handling separate issues in combined PRs. For this PR, it looks like some of the unit tests in CI are unhappy for whatever reason, but once those are fixed, I think this should be overall good to go, and I can take a second pass in reviewing the code then. |
5bdfefe to
c4b3b79
Compare
|
@vdonato fixed all of the nits. As for the unhappy tests, I'm having trouble reproducing them. Is there documentation somewhere on how to setup min-deps env myself? I see it's python 3.9, but a clean conda with only that still has no issues so it has to be something else there. |
f91f318 to
f757726
Compare
|
@vdonato figured out why tests were failing. Issue was backwards compatibility w tornado. |
|
It seems the tests are now all passing - except the check on labels which I believe one of the maintainers has to fill out? Is there anything I still need to do for this to get merged? @vdonato |
|
Hey @velochy, thanks for getting this PR into a good state! And sorry for my radio silence on this PR, I've been caught up with some other work over the past week. Let me give this another read, but assuming the changes made were mostly just to get tests to work again, I'm assuming this change should be more or less good to go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left one comment about addressing some deployment scenarios that I think may still not work + also a nit, but overall this LGTM! This should be good to go once my redirect_uri question is addressed
lib/streamlit/auth_util.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: The graphite request for a docstring may be overkill, but it might be useful to tweak this name to be get_redirect_uri_origin_from_secrets or something similar that makes it more clear what origin corresponds to now that the function lives a bit further away from where it's used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vdonato just so I understand your idea correctly, would it work if I:
a) change the name like you recommend
b) move the fallback of redirect uri missing here
c) change the fallback to use server.baseUrlPath (if present) ?
Or is there something I'm still not thinking of or misunderstanding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this more, I think we might end up needing two separate helper functions since we'll still need this function to get the origin for the other/older use case in AuthCallbackHandler.get (we could probably just keep the _get_origin_from_secrets method where it was and define a new one for the logout redirect url).
The fallback code can stay where it is since passing the request to this helper doesn't seem ideal, and the main functional change we'll want to make is to build post_logout_redirect_uri using server.baseUrlPath so that we handle the case where an app isn't served from the root of its origin.
Another thing that I was worried about is whether we have to consider cases where the Streamlit server thinks the base path where the app is being served is one thing, but the browser thinks it's another (this scenario is somewhat contrived, but I think it's possible if you, for example, have a load balancer in front of your Streamlit instance that receives traffic for the app at one path and routes it to another). I don't think this is an issue, though, because the login URLs generated in user_info.py are set using server.baseUrlPath, so it seems like we already have an implicit assumption that this doesn't happen for the auth feature to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm getting confused now, so trying to lay down a clear plan again:
- Revert the _get_origin_from_secrets as it was originally for oauthCallback
- Instead of using redirect_uri from secrets to construct redirect for logout, just use server.baseUrlPath
Or did you mean something more elaborate still?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think that's more or less what needs to be done. We are essentially relying on the assumption that our redirect_uri is equal to {origin}/{server.baseUrlPath}/oauth2callback for things to work, but we seem to be making that assumption for login already, so I think it's safe to do the same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reviewing the code a little, I decided to go for a slightly different solution, and instead of taking server.baseUrlPath I am just stripping /oauth2callback from redirect uri.
The main reason I believe this is the better approach is that for deployments that work at domain root, baseUrlPath might not be set to the correct external URL (as things will work without it), whereas redirect_uri is guaranteed to be the public url or login would fail.
I do have to admit this is a very unfortunate case of duplication, however. In the ideal world, one of those two configuration parameters should be deprecated (with my bet being the redirect uri being the better candidate for it), but this would require a whole process for something as widely used as streamlit, so it might be just a bit too much trouble for the value it would create.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable to me! Agreed on being able to only have one of server.baseUrlPath/redirect_uri would be ideal, but changing things so that we only need one of these two config values is definitely outside of the scope of this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there might still be some deployment scenarios where the current get_origin_from_secrets implementation won't work here. Redirecting to the origin of our redirect_uri may not work in cases where the app isn't being served from the root path of its origin. For example, it's possible to set up an app served from https://my.streamlit.app/path/to/app with redirect url https://my.streamlit.app/path/to/app/oauth2callback for which the redirect after logout will break.
It might be fine to merge this change as-is and make a quick followup since the end_session_endpoint doesn't work at the moment anyway, but it'd be slightly preferable to fix this in this PR.
(This might change the correct way to approach my other comment)
52c6305 to
c732ffd
Compare
|
Thanks for your continued work on this @velochy! This LGTM -- I made one last change to move the new imports to the top of the test file that I thought would be easier to quickly do myself rather than make a request for you to do so. It looks like there's also still one CI stage that's being problematic, but it seems to be a new one (at least I don't recognize it) that's non-required and failing for reasons unrelated to this change, so this PR should be merge-able once other CI checks pass. |
|
@sfc-gh-nbellante it seems both issues should be fixed by afding client_id to the request, which should be quire easy to do. I will create a PR over today with the change |
…onfig (#11901)" (#12179) This reverts commit 98258bf. ## Describe your changes - For more details see this comment: #12182 (comment) ## GitHub Issue Link (if applicable) ## Testing Plan - Explanation of why no additional tests are needed - Unit Tests (JS and/or Python) - E2E Tests - Any manual testing needed? --- **Contribution License Agreement** By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.
…onfig (#11901)" (#12179) This reverts commit 98258bf. ## Describe your changes - For more details see this comment: #12182 (comment) ## GitHub Issue Link (if applicable) ## Testing Plan - Explanation of why no additional tests are needed - Unit Tests (JS and/or Python) - E2E Tests - Any manual testing needed? --- **Contribution License Agreement** By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.
|
Merged the PR #12182 that adds client_id. However, two things likely still need doing: |
|
@sfc-gh-bnisco Can this PR be re-opened? It makes more sense to continue this thread than the #12182 hotfix one. |
|
Hi @velochy, good question! I would like to, but it is not possible to re-open a merged PR in GitHub. We'll have to continue the thread in any other non-merged PR. |
|
Understood. I will open a new PR once #12044 gets merged then |
…2) (#12693) ## Describe your changes Currently, st.logout ignores end_session_endpoint (ESE) in the OIDC configuration. This can cause issues, as some OIDC providers keep their own cookies and skip the login screen if they have an active session. This change: a) Writes the name of provider into the session cookie (so it would be clear which provider the user is currently logged into) b) Makes st.logout redirect to ESE if one is provided This is a revised version of #11901 to adress the issues raised in #12144 (comment) Most notably: 1) client_id is added to ESE request (original issue, with attempted fix at #12182) 2) id_token_hint is also added (addressing #12144 (comment)) 3) ESE request redirect target it changed to /oauth2callback (adressing #12144 (comment)) This is a follow-up to #12044 ## GitHub Issue Link (if applicable) Closes #11900 ## Testing Plan - unit tests added both for a case where ESE is provided as well as when not --- **Contribution License Agreement** By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license. --------- Co-authored-by: Vincent Donato <vincent@streamlit.io> Co-authored-by: Ken McGrady <ken.mcgrady@gmail.com>
Describe your changes
Currently, st.logout ignores end_session_endpoint (ESE) in the OIDC configuration.
This can cause issues, as some OIDC providers keep their own cookies and skip the login screen if they have an active session.
This change:
a) Writes the name of provider into the session cookie (so it would be clear which provider the user is currently logged into)
b) Makes st.logout redirect to ESE if one is provided.
GitHub Issue Link (if applicable)
Closes #11900
Testing Plan
Contribution License Agreement
By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.