Skip to content

Conversation

@lukasmasuch
Copy link
Collaborator

@lukasmasuch lukasmasuch commented Dec 12, 2025

Describe your changes

Authlib (1.6.6) & tornado (6.5.3) got updated, causing some issues with our CI.

The authlib session aspect likely also breaks existing usage and requires a patch.

Closes #13335


Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@snyk-io
Copy link
Contributor

snyk-io bot commented Dec 12, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 12, 2025

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-13333/streamlit-1.52.1-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-13333.streamlit.app (☁️ Deploy here if not accessible)

@lukasmasuch lukasmasuch added security-assessment-completed Security assessment has been completed for PR change:bugfix PR contains bug fix implementation impact:users PR changes affect end users labels Dec 12, 2025
@lukasmasuch lukasmasuch marked this pull request as ready for review December 12, 2025 11:26
Copilot AI review requested due to automatic review settings December 12, 2025 11:26
@lukasmasuch lukasmasuch changed the title Fix auth-related CI issues caused by dependency updates Fix auth-related issues caused by dependency updates Dec 12, 2025
Copy link
Contributor

Copilot AI left a 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 addresses CI failures caused by updates to the authlib and tornado dependencies. The main change adapts to authlib 1.6.6+ behavior, which now always writes state to session objects even when a cache is available. The fix provides an empty dictionary as a session placeholder instead of None to accommodate this new behavior.

Key Changes:

  • Modified OIDC authentication to use empty dict session placeholders instead of None to work with authlib 1.6.6+
  • Updated test assertions to handle modified error message format from updated dependencies
  • Added explanatory comments documenting the authlib version-specific behavior

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
lib/streamlit/web/server/oidc_mixin.py Updated session handling in two methods to use empty dict instead of None, with comments explaining authlib 1.6.6+ behavior
lib/tests/streamlit/web/server/oauth_authlib_routes_test.py Split error message assertion into two separate checks to handle updated error format from dependency

@github-actions
Copy link
Contributor

github-actions bot commented Dec 12, 2025

📈 Frontend coverage change detected

The frontend unit test (vitest) coverage has increased by 0.0400%

  • Current PR: 86.3000% (12497 lines, 1711 missed)
  • Latest develop: 86.2600% (12497 lines, 1717 missed)

🎉 Great job on improving test coverage!

📊 View detailed coverage comparison

@lukasmasuch
Copy link
Collaborator Author

@cursor review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no bugs!


@lukasmasuch lukasmasuch requested a review from Copilot December 12, 2025 11:46
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Dec 12, 2025
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Dec 12, 2025
@github-actions
Copy link
Contributor

Summary

This PR fixes authentication-related CI issues caused by dependency updates, specifically addressing compatibility with authlib 1.6.6+. The new authlib version changed its behavior to always write OAuth state to session even when a cache is available, which broke Streamlit's Tornado integration since Tornado doesn't have persistent sessions across HTTP requests.

Main changes:

  • Added get_state_data() and clear_state_data() method overrides in TornadoIntegration to retrieve/clear state directly from cache
  • Changed session from None to an empty dict in oidc_mixin.py to satisfy authlib's new session requirements
  • Added comprehensive unit tests for the new state management methods
  • Updated existing test assertions to be compatible with authlib's new error message format

Code Quality

The implementation is clean, well-structured, and follows the existing patterns in the codebase:

Strengths:

  • ✅ Clear class and method docstrings explaining the override rationale (lines 31-38, 45-50, 63-69 in authlib_tornado_integration.py)
  • ✅ Appropriate use of # noqa: ARG002 for intentionally unused session parameters that are required by the parent class signature
  • ✅ Type annotations are complete throughout the changes
  • ✅ Key format f"_state_{self.name}_{state}" matches the parent class implementation, ensuring consistency
  • ✅ The fix maintains compatibility with both older authlib versions and 1.6.6+

Minor observations:

  • Line 54 in authlib_tornado_integration.py uses # type: ignore[attr-defined] for self._get_cache_data(key). This is acceptable since _get_cache_data is a protected method from the authlib parent class that isn't properly typed in the external library.

Test Coverage

The test coverage is excellent. The new tests thoroughly cover the critical state management functionality:

New tests in TornadoIntegrationStateDataTest:

  1. test_get_state_data_from_cache_with_empty_session - Verifies cache retrieval works with empty session
  2. test_get_state_data_returns_none_without_cache - Edge case: no cache configured
  3. test_get_state_data_returns_none_for_missing_state - Edge case: state not in cache
  4. test_clear_state_data_removes_from_cache - Verifies deletion functionality
  5. test_full_state_flow_with_fresh_sessions - Critical: End-to-end flow test simulating real Tornado behavior with fresh sessions per request

Test quality assessment:

  • ✅ Tests follow numpy docstring style with clear descriptions
  • ✅ Tests are properly typed with explicit return type annotations
  • ✅ Tests use real AuthCache instances rather than mocks where appropriate
  • ✅ The full flow test (test_full_state_flow_with_fresh_sessions) is particularly valuable as it validates the entire OAuth state lifecycle with fresh sessions - exactly mimicking the real-world Tornado behavior

Test updates:

  • The assertion change in oauth_authlib_routes_test.py (lines 120-121) from a single combined check to two separate assertions (b"Missing" and b"authorize_url") is appropriate - it accommodates potential variations in error message formatting from different authlib versions while still validating the essential error content.

Backwards Compatibility

Fully backwards compatible. The changes:

  • ✅ Don't modify any public API
  • ✅ Work with both older authlib versions (through cache) and authlib 1.6.6+ (with empty dict sessions)
  • ✅ The overridden methods maintain the same signature as the parent class
  • ✅ The empty dict {} satisfies authlib's new requirement to write state to session while the cache override ensures Tornado can still retrieve state across separate HTTP requests

Security & Risk

No security concerns identified:

  • ✅ OAuth state continues to be properly managed through the cache mechanism
  • ✅ The state data lifecycle (set → get → clear) is preserved correctly
  • ✅ No changes to authentication flows, token handling, or cookie management
  • ✅ The cache-based state storage is the appropriate pattern for Tornado's stateless request model

Regression risk: Low

  • The changes are isolated to the authlib integration layer
  • The core OAuth logic remains unchanged
  • Comprehensive unit tests verify the fix works correctly

Recommendations

No blocking issues found. Minor optional suggestions:

  1. Consider adding a comment about version compatibility (optional): In the class docstring for TornadoIntegration, you could reference the specific authlib version (1.6.6) that introduced the breaking change for future maintainability. The current docstring already explains this well, so this is purely optional.

  2. Test assertion precision (optional): The split assertion in oauth_authlib_routes_test.py could be slightly more precise by checking for the presence of both terms in sequence, but the current approach is sufficient and handles version differences gracefully.

Verdict

APPROVED: This PR correctly fixes the authlib 1.6.6+ compatibility issue for Tornado's sessionless architecture. The implementation is clean, well-documented, thoroughly tested, and maintains backwards compatibility. The changes are isolated to the authentication integration layer with no impact on public APIs or security.


This is an automated AI review. Please verify the feedback and use your judgment.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

@github-actions github-actions bot added the do-not-merge PR is blocked from merging label Dec 12, 2025
@lukasmasuch lukasmasuch removed the do-not-merge PR is blocked from merging label Dec 12, 2025
@github-actions
Copy link
Contributor

Summary

This PR updates Streamlit’s Authlib↔Tornado integration to remain compatible with Authlib 1.6.6+ (which now always tracks OAuth state in “session”), by using a placeholder session dict and overriding state retrieval/cleanup to rely on Streamlit’s cache. It also tightens up a couple tests that became brittle after dependency updates.

Code Quality

  • Overall: The intent is clear, the docstrings explain the Authlib behavior change well, and the approach (use cache + placeholder session) is reasonable.

  • Potential robustness issue in _get_cache_data_safe fallback selection:

    • In lib/streamlit/web/server/authlib_tornado_integration.py, _get_cache_data_safe checks for the presence of a private Authlib helper _get_cache_data and calls it directly (L48-L54). If Authlib changes that private method’s signature (or it exists but behaves differently), this will raise (e.g., TypeError) and will not fall back to the direct-cache path.
    • Reference: authlib_tornado_integration.py L48-L54.
    • Suggestion: Wrap the call in a try/except (TypeError, Exception) (or at least TypeError) and then fall back to the JSON decode path.
  • New invalid_state path is not handled by the callback handler:

    • In lib/streamlit/web/server/oidc_mixin.py, authorize_access_token now raises OAuthError(error="invalid_state", ...) when state_data is missing (L94-L99).
    • In lib/streamlit/web/server/oauth_authlib_routes.py, AuthCallbackHandler.get calls token = client.authorize_access_token(self) without any exception handling (L174-L176).
    • Result: if state_data is missing/expired/malformed, this will likely propagate and yield a 500 instead of a controlled redirect (and a log).
    • References:
      • oidc_mixin.py L94-L99
      • oauth_authlib_routes.py L174-L176
    • Suggestion: Catch OAuthError (and possibly broader exceptions from token exchange) in AuthCallbackHandler.get, log a sanitized message, and redirect to base (consistent with how error query params are handled at L147-L162).
  • (Existing risk worth noting, now more relevant) AuthCallbackHandler._get_provider_by_state assumes all cache keys split into exactly 4 underscore-separated parts (oauth_authlib_routes.py L189-L193). If any other key enters auth_cache, this could raise ValueError and break callbacks.

Test Coverage

  • Good additions:

    • Added focused unit tests for the overridden cache-only state flow in lib/tests/streamlit/web/server/authlib_tornado_integration_test.py (new file). The “fresh sessions per request” test (L96-L127) is especially aligned with the Tornado behavior this PR is targeting.
    • Added a unit test for the missing state-data path in lib/tests/streamlit/web/server/oidc_mixin_test.py (L105-L125).
    • Relaxed a brittle assertion in lib/tests/streamlit/web/server/oauth_authlib_routes_test.py to avoid pinning the exact error string (L115-L122).
  • Gap:

    • There is no test asserting that AuthCallbackHandler gracefully handles exceptions from authorize_access_token (including the newly introduced invalid_state). Given the production-facing callback handler, this is an important behavior to lock down.
  • Style note:

    • The new test file uses unittest.TestCase with bare asserts. This matches some existing patterns in the area, but the repo’s Python test guide prefers pytest-style tests where practical.

Backwards Compatibility

  • Public API: No user-facing API changes.
  • Runtime behavior:
    • The new placeholder-session approach should preserve compatibility across Authlib versions and avoid relying on persistent sessions in Tornado.
    • However, without handler-side exception handling, some error cases (expired/missing state) may manifest as 500s, which is a backwards regression in user experience and potentially in stability.

Security & Risk

  • State validation: Adding an explicit invalid_state failure is good for correctness.
  • Risk: Uncaught OAuthError in the callback handler can lead to 500s (and potentially error pages containing information depending on Tornado configuration). Handling and sanitizing errors in AuthCallbackHandler.get reduces this risk.

Recommendations

  1. Handle OAuthError (and token-exchange exceptions) in AuthCallbackHandler.get and redirect to base with a sanitized log message. See lib/streamlit/web/server/oauth_authlib_routes.py:L174-L176.
  2. Make _get_cache_data_safe truly “safe” by falling back to direct cache access when calling Authlib’s private _get_cache_data fails (e.g., signature mismatch). See lib/streamlit/web/server/authlib_tornado_integration.py:L48-L54.
  3. Add a unit test in oauth_authlib_routes_test.py ensuring /oauth2callback does not 500 when authorize_access_token raises OAuthError (including invalid_state), and instead redirects to /.

Verdict

CHANGES REQUESTED: The core fix is sound and well-tested at the integration layer, but the new invalid_state behavior needs to be handled in AuthCallbackHandler to avoid uncaught exceptions/500s during real OAuth callbacks.


This is an automated AI review. Please verify the feedback and use your judgment.

@github-actions github-actions bot added the do-not-merge PR is blocked from merging label Dec 12, 2025
@sfc-gh-lmasuch sfc-gh-lmasuch removed the do-not-merge PR is blocked from merging label Dec 12, 2025
@streamlit streamlit deleted a comment from github-actions bot Dec 12, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 12, 2025

📉 Python coverage change detected

The Python unit test coverage has decreased by 0.0055%

  • Current PR: 92.8493% (20823 statements, 1489 missed)
  • Latest develop: 92.8547% (20797 statements, 1486 missed)

✅ Coverage change is within normal range.

Coverage by files
Name Stmts Miss Cover
streamlit/__init__.py 142 0 100%
streamlit/__main__.py 3 3 0%
streamlit/auth_util.py 100 25 75%
streamlit/cli_util.py 39 6 85%
streamlit/column_config.py 3 0 100%
streamlit/commands/__init__.py 0 0 100%
streamlit/commands/echo.py 54 11 80%
streamlit/commands/execution_control.py 70 10 86%
streamlit/commands/experimental_query_params.py 40 2 95%
streamlit/commands/logo.py 41 6 85%
streamlit/commands/navigation.py 106 2 98%
streamlit/commands/page_config.py 106 4 96%
streamlit/components/__init__.py 0 0 100%
streamlit/components/lib/__init__.py 0 0 100%
streamlit/components/lib/local_component_registry.py 35 2 94%
streamlit/components/types/__init__.py 0 0 100%
streamlit/components/types/base_component_registry.py 14 0 100%
streamlit/components/types/base_custom_component.py 49 6 88%
streamlit/components/v1/__init__.py 5 0 100%
streamlit/components/v1/component_arrow.py 33 8 76%
streamlit/components/v1/component_registry.py 41 3 93%
streamlit/components/v1/components.py 4 4 0%
streamlit/components/v1/custom_component.py 85 7 92%
streamlit/components/v2/__init__.py 24 0 100%
streamlit/components/v2/bidi_component/__init__.py 4 0 100%
streamlit/components/v2/bidi_component/constants.py 5 0 100%
streamlit/components/v2/bidi_component/main.py 152 17 89%
streamlit/components/v2/bidi_component/serialization.py 81 2 98%
streamlit/components/v2/bidi_component/state.py 13 0 100%
streamlit/components/v2/component_definition_resolver.py 30 0 100%
streamlit/components/v2/component_file_watcher.py 117 9 92%
streamlit/components/v2/component_manager.py 97 13 87%
streamlit/components/v2/component_manifest_handler.py 24 0 100%
streamlit/components/v2/component_path_utils.py 68 5 93%
streamlit/components/v2/component_registry.py 121 8 93%
streamlit/components/v2/get_bidi_component_manager.py 8 1 88%
streamlit/components/v2/manifest_scanner.py 224 25 89%
streamlit/components/v2/presentation.py 84 19 77%
streamlit/components/v2/types.py 8 8 0%
streamlit/config.py 412 12 97%
streamlit/config_option.py 79 3 96%
streamlit/config_util.py 288 7 98%
streamlit/connections/__init__.py 6 0 100%
streamlit/connections/base_connection.py 45 0 100%
streamlit/connections/snowflake_connection.py 60 13 78%
streamlit/connections/snowpark_connection.py 44 3 93%
streamlit/connections/sql_connection.py 56 6 89%
streamlit/connections/util.py 33 0 100%
streamlit/cursor.py 130 1 99%
streamlit/dataframe_util.py 501 47 91%
streamlit/delta_generator.py 250 7 97%
streamlit/delta_generator_singletons.py 74 7 91%
streamlit/deprecation_util.py 59 4 93%
streamlit/development.py 1 0 100%
streamlit/elements/__init__.py 0 0 100%
streamlit/elements/alert.py 60 0 100%
streamlit/elements/arrow.py 201 15 93%
streamlit/elements/balloons.py 10 0 100%
streamlit/elements/bokeh_chart.py 9 0 100%
streamlit/elements/code.py 20 1 95%
streamlit/elements/deck_gl_json_chart.py 104 10 90%
streamlit/elements/dialog_decorator.py 38 0 100%
streamlit/elements/doc_string.py 227 9 96%
streamlit/elements/empty.py 16 4 75%
streamlit/elements/exception.py 101 10 90%
streamlit/elements/form.py 56 2 96%
streamlit/elements/graphviz_chart.py 35 1 97%
streamlit/elements/heading.py 56 0 100%
streamlit/elements/html.py 49 0 100%
streamlit/elements/iframe.py 29 0 100%
streamlit/elements/image.py 32 0 100%
streamlit/elements/json.py 39 2 95%
streamlit/elements/layouts.py 140 3 98%
streamlit/elements/lib/__init__.py 0 0 100%
streamlit/elements/lib/built_in_chart_utils.py 390 26 93%
streamlit/elements/lib/color_util.py 100 4 96%
streamlit/elements/lib/column_config_utils.py 169 1 99%
streamlit/elements/lib/column_types.py 189 4 98%
streamlit/elements/lib/dialog.py 67 1 99%
streamlit/elements/lib/dicttools.py 39 2 95%
streamlit/elements/lib/file_uploader_utils.py 30 0 100%
streamlit/elements/lib/form_utils.py 26 0 100%
streamlit/elements/lib/image_utils.py 176 21 88%
streamlit/elements/lib/js_number.py 28 3 89%
streamlit/elements/lib/layout_utils.py 121 1 99%
streamlit/elements/lib/mutable_status_container.py 73 4 95%
streamlit/elements/lib/options_selector_utils.py 90 0 100%
streamlit/elements/lib/pandas_styler_utils.py 80 2 98%
streamlit/elements/lib/policies.py 56 1 98%
streamlit/elements/lib/shortcut_utils.py 42 2 95%
streamlit/elements/lib/streamlit_plotly_theme.py 49 0 100%
streamlit/elements/lib/subtitle_utils.py 76 13 83%
streamlit/elements/lib/utils.py 76 5 93%
streamlit/elements/map.py 110 1 99%
streamlit/elements/markdown.py 65 2 97%
streamlit/elements/media.py 181 8 96%
streamlit/elements/metric.py 104 0 100%
streamlit/elements/pdf.py 49 2 96%
streamlit/elements/plotly_chart.py 129 6 95%
streamlit/elements/progress.py 36 0 100%
streamlit/elements/pyplot.py 39 2 95%
streamlit/elements/snow.py 10 0 100%
streamlit/elements/space.py 12 0 100%
streamlit/elements/spinner.py 44 3 93%
streamlit/elements/text.py 16 0 100%
streamlit/elements/toast.py 26 0 100%
streamlit/elements/vega_charts.py 228 3 99%
streamlit/elements/widgets/__init__.py 0 0 100%
streamlit/elements/widgets/audio_input.py 68 10 85%
streamlit/elements/widgets/button.py 245 6 98%
streamlit/elements/widgets/button_group.py 171 1 99%
streamlit/elements/widgets/camera_input.py 62 10 84%
streamlit/elements/widgets/chat.py 237 58 76%
streamlit/elements/widgets/checkbox.py 52 0 100%
streamlit/elements/widgets/color_picker.py 59 2 97%
streamlit/elements/widgets/data_editor.py 254 14 94%
streamlit/elements/widgets/file_uploader.py 108 18 83%
streamlit/elements/widgets/multiselect.py 105 4 96%
streamlit/elements/widgets/number_input.py 143 5 97%
streamlit/elements/widgets/radio.py 83 5 94%
streamlit/elements/widgets/select_slider.py 97 0 100%
streamlit/elements/widgets/selectbox.py 91 2 98%
streamlit/elements/widgets/slider.py 241 8 97%
streamlit/elements/widgets/text_widgets.py 130 6 95%
streamlit/elements/widgets/time_widgets.py 390 19 95%
streamlit/elements/write.py 166 32 81%
streamlit/emojis.py 4 0 100%
streamlit/env_util.py 21 3 86%
streamlit/error_util.py 33 2 94%
streamlit/errors.py 194 25 87%
streamlit/external/__init__.py 0 0 100%
streamlit/external/langchain/__init__.py 2 0 100%
streamlit/external/langchain/streamlit_callback_handler.py 141 82 42%
streamlit/file_util.py 84 8 90%
streamlit/git_util.py 100 5 95%
streamlit/logger.py 54 0 100%
streamlit/material_icon_names.py 1 0 100%
streamlit/navigation/__init__.py 0 0 100%
streamlit/navigation/page.py 78 2 97%
streamlit/net_util.py 55 3 95%
streamlit/platform.py 10 1 90%
streamlit/runtime/__init__.py 8 0 100%
streamlit/runtime/app_session.py 460 91 80%
streamlit/runtime/caching/__init__.py 19 0 100%
streamlit/runtime/caching/cache_data_api.py 164 3 98%
streamlit/runtime/caching/cache_errors.py 45 4 91%
streamlit/runtime/caching/cache_resource_api.py 122 0 100%
streamlit/runtime/caching/cache_type.py 11 1 91%
streamlit/runtime/caching/cache_utils.py 165 9 95%
streamlit/runtime/caching/cached_message_replay.py 108 1 99%
streamlit/runtime/caching/hashing.py 311 25 92%
streamlit/runtime/caching/legacy_cache_api.py 14 0 100%
streamlit/runtime/caching/storage/__init__.py 2 0 100%
streamlit/runtime/caching/storage/cache_storage_protocol.py 31 2 94%
streamlit/runtime/caching/storage/dummy_cache_storage.py 21 0 100%
streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py 60 0 100%
streamlit/runtime/caching/storage/local_disk_cache_storage.py 86 4 95%
streamlit/runtime/connection_factory.py 85 9 89%
streamlit/runtime/context.py 140 0 100%
streamlit/runtime/context_util.py 18 0 100%
streamlit/runtime/credentials.py 139 4 97%
streamlit/runtime/download_data_util.py 27 0 100%
streamlit/runtime/forward_msg_cache.py 23 2 91%
streamlit/runtime/forward_msg_queue.py 63 4 94%
streamlit/runtime/fragment.py 112 2 98%
streamlit/runtime/media_file_manager.py 110 7 94%
streamlit/runtime/media_file_storage.py 15 0 100%
streamlit/runtime/memory_media_file_storage.py 68 0 100%
streamlit/runtime/memory_session_storage.py 15 0 100%
streamlit/runtime/memory_uploaded_file_manager.py 41 1 98%
streamlit/runtime/metrics_util.py 193 12 94%
streamlit/runtime/pages_manager.py 59 2 97%
streamlit/runtime/runtime.py 248 18 93%
streamlit/runtime/runtime_util.py 30 1 97%
streamlit/runtime/script_data.py 16 0 100%
streamlit/runtime/scriptrunner/__init__.py 5 0 100%
streamlit/runtime/scriptrunner/exec_code.py 49 5 90%
streamlit/runtime/scriptrunner/magic.py 83 1 99%
streamlit/runtime/scriptrunner/magic_funcs.py 10 1 90%
streamlit/runtime/scriptrunner/script_cache.py 27 0 100%
streamlit/runtime/scriptrunner/script_runner.py 230 27 88%
streamlit/runtime/scriptrunner_utils/__init__.py 0 0 100%
streamlit/runtime/scriptrunner_utils/exceptions.py 11 1 91%
streamlit/runtime/scriptrunner_utils/script_requests.py 106 5 95%
streamlit/runtime/scriptrunner_utils/script_run_context.py 134 2 99%
streamlit/runtime/secrets.py 242 25 90%
streamlit/runtime/session_manager.py 60 1 98%
streamlit/runtime/state/__init__.py 7 0 100%
streamlit/runtime/state/common.py 52 2 96%
streamlit/runtime/state/presentation.py 19 4 79%
streamlit/runtime/state/query_params.py 134 5 96%
streamlit/runtime/state/query_params_proxy.py 71 0 100%
streamlit/runtime/state/safe_session_state.py 77 9 88%
streamlit/runtime/state/session_state.py 433 29 93%
streamlit/runtime/state/session_state_proxy.py 62 8 87%
streamlit/runtime/state/widgets.py 15 1 93%
streamlit/runtime/stats.py 79 6 92%
streamlit/runtime/theme_util.py 46 1 98%
streamlit/runtime/uploaded_file_manager.py 39 3 92%
streamlit/runtime/websocket_session_manager.py 66 0 100%
streamlit/source_util.py 36 1 97%
streamlit/string_util.py 92 8 91%
streamlit/temporary_directory.py 18 1 94%
streamlit/testing/__init__.py 0 0 100%
streamlit/testing/v1/__init__.py 2 0 100%
streamlit/testing/v1/app_test.py 242 6 98%
streamlit/testing/v1/element_tree.py 1371 87 94%
streamlit/testing/v1/local_script_runner.py 71 2 97%
streamlit/testing/v1/util.py 17 0 100%
streamlit/time_util.py 28 1 96%
streamlit/type_util.py 138 12 91%
streamlit/url_util.py 39 5 87%
streamlit/user_info.py 87 8 91%
streamlit/util.py 38 1 97%
streamlit/version.py 3 0 100%
streamlit/watcher/__init__.py 3 0 100%
streamlit/watcher/event_based_path_watcher.py 181 24 87%
streamlit/watcher/folder_black_list.py 14 1 93%
streamlit/watcher/local_sources_watcher.py 127 9 93%
streamlit/watcher/path_watcher.py 42 3 93%
streamlit/watcher/polling_path_watcher.py 55 2 96%
streamlit/watcher/util.py 49 1 98%
streamlit/web/__init__.py 0 0 100%
streamlit/web/bootstrap.py 153 20 87%
streamlit/web/cache_storage_manager_config.py 5 0 100%
streamlit/web/cli.py 186 17 91%
streamlit/web/server/__init__.py 5 0 100%
streamlit/web/server/app_static_file_handler.py 29 3 90%
streamlit/web/server/authlib_tornado_integration.py 42 5 88%
streamlit/web/server/bidi_component_request_handler.py 65 8 88%
streamlit/web/server/browser_websocket_handler.py 115 31 73%
streamlit/web/server/component_file_utils.py 24 0 100%
streamlit/web/server/component_request_handler.py 55 4 93%
streamlit/web/server/media_file_handler.py 65 9 86%
streamlit/web/server/oauth_authlib_routes.py 118 18 85%
streamlit/web/server/oidc_mixin.py 46 0 100%
streamlit/web/server/routes.py 90 7 92%
streamlit/web/server/server.py 188 11 94%
streamlit/web/server/server_util.py 67 5 93%
streamlit/web/server/stats_request_handler.py 58 5 91%
streamlit/web/server/upload_file_request_handler.py 59 14 76%
streamlit/web/server/websocket_headers.py 19 1 95%
TOTAL 20823 1489 93%

📊 View detailed coverage comparison

@lukasmasuch
Copy link
Collaborator Author

Handle OAuthError (and token-exchange exceptions) in AuthCallbackHandler.get

Make _get_cache_data_safe truly “safe”

These two aspects are somewhat valid, but already working that way in the previous state. They should be made more robust in a follow-up.

@sfc-gh-lwilby
Copy link
Collaborator

In lib/streamlit/web/server/authlib_tornado_integration.py, _get_cache_data_safe checks for the presence of a private Authlib helper _get_cache_data and calls it directly (L48-L54). If Authlib changes that private method’s signature (or it exists but behaves differently), this will raise (e.g., TypeError) and will not fall back to the direct-cache path.

See this had been fixed ✅

pass

# Fallback: direct cache access with JSON decoding
# This mirrors the implementation in FrameworkIntegration._get_cache_data
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the downside to using this method? Is it always available, or only given a certain authlib version?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, the method is easy enough that we can always just use the fallback and not even rely on it at all

assert result == {"redirect_uri": "http://example.com"}

def test_get_state_data_returns_none_without_cache(self) -> None:
"""Verify get_state_data returns None when no cache is configured."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the expected case for this when the user is logged out, or their authentication is expired?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, it was testing an edge case that cannot happen in actual Streamlit usage. Removed the test again since cache cannot be None

lukasmasuch and others added 7 commits December 12, 2025 15:17
Co-authored-by: Laura Wilby <laura.wilby@snowflake.com>
Co-authored-by: Laura Wilby <laura.wilby@snowflake.com>
Co-authored-by: Laura Wilby <laura.wilby@snowflake.com>
Copy link
Collaborator

@sfc-gh-lwilby sfc-gh-lwilby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lukasmasuch lukasmasuch merged commit ed462e5 into develop Dec 12, 2025
42 checks passed
@lukasmasuch lukasmasuch deleted the fix/auth-ci-issues-after-dep-updates branch December 12, 2025 15:15
github-actions bot pushed a commit that referenced this pull request Dec 15, 2025
## Describe your changes

Authlib (1.6.6) & tornado (6.5.3) got updated, causing some issues with
our CI.

The authlib session aspect likely also breaks existing usage and
requires a patch.


Closes #13335

---

**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: Laura Wilby <laura.wilby@snowflake.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:bugfix PR contains bug fix implementation impact:users PR changes affect end users security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New updated Authlib Version 1.6.6 in streamlit[auth] does not work

4 participants