streamlit-nightly 1.41.2.dev20250107__py2.py3-none-any.whl → 1.41.2.dev20250109__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. streamlit/__init__.py +11 -2
  2. streamlit/__main__.py +1 -1
  3. streamlit/auth_util.py +209 -0
  4. streamlit/cli_util.py +1 -1
  5. streamlit/column_config.py +1 -1
  6. streamlit/commands/__init__.py +1 -1
  7. streamlit/commands/echo.py +1 -1
  8. streamlit/commands/execution_control.py +1 -1
  9. streamlit/commands/experimental_query_params.py +1 -1
  10. streamlit/commands/logo.py +1 -1
  11. streamlit/commands/navigation.py +1 -1
  12. streamlit/commands/page_config.py +1 -1
  13. streamlit/components/__init__.py +1 -1
  14. streamlit/components/lib/__init__.py +1 -1
  15. streamlit/components/lib/local_component_registry.py +1 -1
  16. streamlit/components/types/__init__.py +1 -1
  17. streamlit/components/types/base_component_registry.py +1 -1
  18. streamlit/components/types/base_custom_component.py +1 -1
  19. streamlit/components/v1/__init__.py +1 -1
  20. streamlit/components/v1/component_arrow.py +1 -1
  21. streamlit/components/v1/component_registry.py +1 -1
  22. streamlit/components/v1/components.py +1 -1
  23. streamlit/components/v1/custom_component.py +1 -1
  24. streamlit/config.py +1 -1
  25. streamlit/config_option.py +1 -1
  26. streamlit/config_util.py +1 -1
  27. streamlit/connections/__init__.py +1 -1
  28. streamlit/connections/base_connection.py +1 -1
  29. streamlit/connections/snowflake_connection.py +1 -1
  30. streamlit/connections/snowpark_connection.py +1 -1
  31. streamlit/connections/sql_connection.py +1 -1
  32. streamlit/connections/util.py +1 -1
  33. streamlit/cursor.py +1 -1
  34. streamlit/dataframe_util.py +1 -1
  35. streamlit/delta_generator.py +1 -1
  36. streamlit/delta_generator_singletons.py +1 -1
  37. streamlit/deprecation_util.py +1 -1
  38. streamlit/development.py +1 -1
  39. streamlit/elements/__init__.py +1 -1
  40. streamlit/elements/alert.py +1 -1
  41. streamlit/elements/arrow.py +1 -1
  42. streamlit/elements/balloons.py +1 -1
  43. streamlit/elements/bokeh_chart.py +1 -1
  44. streamlit/elements/code.py +1 -1
  45. streamlit/elements/deck_gl_json_chart.py +1 -1
  46. streamlit/elements/dialog_decorator.py +1 -1
  47. streamlit/elements/doc_string.py +1 -1
  48. streamlit/elements/empty.py +1 -1
  49. streamlit/elements/exception.py +1 -1
  50. streamlit/elements/form.py +1 -1
  51. streamlit/elements/graphviz_chart.py +1 -1
  52. streamlit/elements/heading.py +1 -1
  53. streamlit/elements/html.py +1 -1
  54. streamlit/elements/iframe.py +1 -1
  55. streamlit/elements/image.py +1 -1
  56. streamlit/elements/json.py +1 -1
  57. streamlit/elements/layouts.py +1 -1
  58. streamlit/elements/lib/__init__.py +1 -1
  59. streamlit/elements/lib/built_in_chart_utils.py +1 -1
  60. streamlit/elements/lib/color_util.py +1 -1
  61. streamlit/elements/lib/column_config_utils.py +1 -1
  62. streamlit/elements/lib/column_types.py +1 -1
  63. streamlit/elements/lib/dialog.py +1 -1
  64. streamlit/elements/lib/dicttools.py +1 -1
  65. streamlit/elements/lib/event_utils.py +1 -1
  66. streamlit/elements/lib/file_uploader_utils.py +1 -1
  67. streamlit/elements/lib/form_utils.py +1 -1
  68. streamlit/elements/lib/image_utils.py +1 -1
  69. streamlit/elements/lib/js_number.py +1 -1
  70. streamlit/elements/lib/mutable_status_container.py +1 -1
  71. streamlit/elements/lib/options_selector_utils.py +1 -1
  72. streamlit/elements/lib/pandas_styler_utils.py +1 -1
  73. streamlit/elements/lib/policies.py +1 -1
  74. streamlit/elements/lib/streamlit_plotly_theme.py +1 -1
  75. streamlit/elements/lib/subtitle_utils.py +1 -1
  76. streamlit/elements/lib/utils.py +1 -1
  77. streamlit/elements/map.py +1 -1
  78. streamlit/elements/markdown.py +1 -1
  79. streamlit/elements/media.py +1 -1
  80. streamlit/elements/metric.py +1 -1
  81. streamlit/elements/plotly_chart.py +1 -1
  82. streamlit/elements/progress.py +1 -1
  83. streamlit/elements/pyplot.py +1 -1
  84. streamlit/elements/snow.py +1 -1
  85. streamlit/elements/spinner.py +14 -7
  86. streamlit/elements/text.py +1 -1
  87. streamlit/elements/toast.py +1 -1
  88. streamlit/elements/vega_charts.py +1 -1
  89. streamlit/elements/widgets/__init__.py +1 -1
  90. streamlit/elements/widgets/audio_input.py +1 -1
  91. streamlit/elements/widgets/button.py +1 -1
  92. streamlit/elements/widgets/button_group.py +1 -1
  93. streamlit/elements/widgets/camera_input.py +1 -1
  94. streamlit/elements/widgets/chat.py +1 -1
  95. streamlit/elements/widgets/checkbox.py +1 -1
  96. streamlit/elements/widgets/color_picker.py +1 -1
  97. streamlit/elements/widgets/data_editor.py +1 -1
  98. streamlit/elements/widgets/file_uploader.py +1 -1
  99. streamlit/elements/widgets/multiselect.py +1 -1
  100. streamlit/elements/widgets/number_input.py +1 -1
  101. streamlit/elements/widgets/radio.py +1 -1
  102. streamlit/elements/widgets/select_slider.py +1 -1
  103. streamlit/elements/widgets/selectbox.py +1 -1
  104. streamlit/elements/widgets/slider.py +1 -1
  105. streamlit/elements/widgets/text_widgets.py +1 -1
  106. streamlit/elements/widgets/time_widgets.py +1 -1
  107. streamlit/elements/write.py +1 -1
  108. streamlit/emojis.py +1 -1
  109. streamlit/env_util.py +1 -1
  110. streamlit/error_util.py +1 -1
  111. streamlit/errors.py +5 -1
  112. streamlit/external/__init__.py +1 -1
  113. streamlit/external/langchain/__init__.py +1 -1
  114. streamlit/external/langchain/streamlit_callback_handler.py +1 -1
  115. streamlit/file_util.py +1 -1
  116. streamlit/git_util.py +1 -1
  117. streamlit/hello/__init__.py +1 -1
  118. streamlit/hello/animation_demo.py +1 -1
  119. streamlit/hello/dataframe_demo.py +1 -1
  120. streamlit/hello/hello.py +1 -1
  121. streamlit/hello/mapping_demo.py +1 -1
  122. streamlit/hello/plotting_demo.py +1 -1
  123. streamlit/hello/streamlit_app.py +1 -1
  124. streamlit/hello/utils.py +1 -1
  125. streamlit/logger.py +1 -1
  126. streamlit/material_icon_names.py +1 -1
  127. streamlit/navigation/__init__.py +1 -1
  128. streamlit/navigation/page.py +1 -1
  129. streamlit/net_util.py +1 -1
  130. streamlit/platform.py +1 -1
  131. streamlit/proto/Alert_pb2.pyi +1 -1
  132. streamlit/proto/AppPage_pb2.pyi +1 -1
  133. streamlit/proto/ArrowNamedDataSet_pb2.pyi +1 -1
  134. streamlit/proto/ArrowVegaLiteChart_pb2.pyi +1 -1
  135. streamlit/proto/Arrow_pb2.pyi +1 -1
  136. streamlit/proto/AudioInput_pb2.pyi +1 -1
  137. streamlit/proto/Audio_pb2.pyi +1 -1
  138. streamlit/proto/AuthRedirect_pb2.py +27 -0
  139. streamlit/proto/AuthRedirect_pb2.pyi +41 -0
  140. streamlit/proto/AutoRerun_pb2.pyi +1 -1
  141. streamlit/proto/BackMsg_pb2.pyi +1 -1
  142. streamlit/proto/Balloons_pb2.pyi +1 -1
  143. streamlit/proto/Block_pb2.pyi +1 -1
  144. streamlit/proto/BokehChart_pb2.pyi +1 -1
  145. streamlit/proto/ButtonGroup_pb2.pyi +1 -1
  146. streamlit/proto/Button_pb2.pyi +1 -1
  147. streamlit/proto/CameraInput_pb2.pyi +1 -1
  148. streamlit/proto/ChatInput_pb2.pyi +1 -1
  149. streamlit/proto/Checkbox_pb2.pyi +1 -1
  150. streamlit/proto/ClientState_pb2.pyi +1 -1
  151. streamlit/proto/Code_pb2.pyi +1 -1
  152. streamlit/proto/ColorPicker_pb2.pyi +1 -1
  153. streamlit/proto/Common_pb2.pyi +1 -1
  154. streamlit/proto/Components_pb2.pyi +1 -1
  155. streamlit/proto/DataFrame_pb2.pyi +1 -1
  156. streamlit/proto/DateInput_pb2.pyi +1 -1
  157. streamlit/proto/DeckGlJsonChart_pb2.pyi +1 -1
  158. streamlit/proto/Delta_pb2.pyi +1 -1
  159. streamlit/proto/DocString_pb2.pyi +1 -1
  160. streamlit/proto/DownloadButton_pb2.pyi +1 -1
  161. streamlit/proto/Element_pb2.pyi +1 -1
  162. streamlit/proto/Empty_pb2.pyi +1 -1
  163. streamlit/proto/Exception_pb2.pyi +1 -1
  164. streamlit/proto/Favicon_pb2.pyi +1 -1
  165. streamlit/proto/FileUploader_pb2.pyi +1 -1
  166. streamlit/proto/ForwardMsg_pb2.py +12 -9
  167. streamlit/proto/ForwardMsg_pb2.pyi +34 -4
  168. streamlit/proto/GitInfo_pb2.pyi +1 -1
  169. streamlit/proto/GraphVizChart_pb2.pyi +1 -1
  170. streamlit/proto/Heading_pb2.pyi +1 -1
  171. streamlit/proto/Html_pb2.pyi +1 -1
  172. streamlit/proto/IFrame_pb2.pyi +1 -1
  173. streamlit/proto/Image_pb2.pyi +1 -1
  174. streamlit/proto/Json_pb2.pyi +1 -1
  175. streamlit/proto/LabelVisibilityMessage_pb2.pyi +1 -1
  176. streamlit/proto/LinkButton_pb2.pyi +1 -1
  177. streamlit/proto/Logo_pb2.pyi +1 -1
  178. streamlit/proto/Markdown_pb2.pyi +1 -1
  179. streamlit/proto/Metric_pb2.pyi +1 -1
  180. streamlit/proto/MetricsEvent_pb2.pyi +1 -1
  181. streamlit/proto/MultiSelect_pb2.pyi +1 -1
  182. streamlit/proto/NamedDataSet_pb2.pyi +1 -1
  183. streamlit/proto/Navigation_pb2.pyi +1 -1
  184. streamlit/proto/NewSession_pb2.pyi +1 -1
  185. streamlit/proto/NumberInput_pb2.pyi +1 -1
  186. streamlit/proto/PageConfig_pb2.pyi +1 -1
  187. streamlit/proto/PageInfo_pb2.pyi +1 -1
  188. streamlit/proto/PageLink_pb2.pyi +1 -1
  189. streamlit/proto/PageNotFound_pb2.pyi +1 -1
  190. streamlit/proto/PageProfile_pb2.pyi +1 -1
  191. streamlit/proto/PagesChanged_pb2.pyi +1 -1
  192. streamlit/proto/ParentMessage_pb2.pyi +1 -1
  193. streamlit/proto/PlotlyChart_pb2.pyi +1 -1
  194. streamlit/proto/Progress_pb2.pyi +1 -1
  195. streamlit/proto/Radio_pb2.pyi +1 -1
  196. streamlit/proto/RootContainer_pb2.pyi +1 -1
  197. streamlit/proto/Selectbox_pb2.pyi +1 -1
  198. streamlit/proto/SessionEvent_pb2.pyi +1 -1
  199. streamlit/proto/SessionStatus_pb2.pyi +1 -1
  200. streamlit/proto/Skeleton_pb2.pyi +1 -1
  201. streamlit/proto/Slider_pb2.pyi +1 -1
  202. streamlit/proto/Snow_pb2.pyi +1 -1
  203. streamlit/proto/Spinner_pb2.py +2 -2
  204. streamlit/proto/Spinner_pb2.pyi +6 -2
  205. streamlit/proto/TextArea_pb2.pyi +1 -1
  206. streamlit/proto/TextInput_pb2.pyi +1 -1
  207. streamlit/proto/Text_pb2.pyi +1 -1
  208. streamlit/proto/TimeInput_pb2.pyi +1 -1
  209. streamlit/proto/Toast_pb2.pyi +1 -1
  210. streamlit/proto/VegaLiteChart_pb2.pyi +1 -1
  211. streamlit/proto/Video_pb2.pyi +1 -1
  212. streamlit/proto/WidgetStates_pb2.pyi +1 -1
  213. streamlit/proto/__init__.py +1 -1
  214. streamlit/runtime/__init__.py +1 -1
  215. streamlit/runtime/app_session.py +6 -4
  216. streamlit/runtime/caching/__init__.py +1 -1
  217. streamlit/runtime/caching/cache_data_api.py +1 -1
  218. streamlit/runtime/caching/cache_errors.py +1 -1
  219. streamlit/runtime/caching/cache_resource_api.py +1 -1
  220. streamlit/runtime/caching/cache_type.py +1 -1
  221. streamlit/runtime/caching/cache_utils.py +1 -1
  222. streamlit/runtime/caching/cached_message_replay.py +1 -1
  223. streamlit/runtime/caching/hashing.py +1 -1
  224. streamlit/runtime/caching/legacy_cache_api.py +1 -1
  225. streamlit/runtime/caching/storage/__init__.py +1 -1
  226. streamlit/runtime/caching/storage/cache_storage_protocol.py +1 -1
  227. streamlit/runtime/caching/storage/dummy_cache_storage.py +1 -1
  228. streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +1 -1
  229. streamlit/runtime/caching/storage/local_disk_cache_storage.py +1 -1
  230. streamlit/runtime/connection_factory.py +1 -1
  231. streamlit/runtime/context.py +1 -1
  232. streamlit/runtime/credentials.py +1 -1
  233. streamlit/runtime/forward_msg_cache.py +1 -1
  234. streamlit/runtime/forward_msg_queue.py +33 -5
  235. streamlit/runtime/fragment.py +2 -2
  236. streamlit/runtime/media_file_manager.py +1 -1
  237. streamlit/runtime/media_file_storage.py +1 -1
  238. streamlit/runtime/memory_media_file_storage.py +1 -1
  239. streamlit/runtime/memory_session_storage.py +1 -1
  240. streamlit/runtime/memory_uploaded_file_manager.py +1 -1
  241. streamlit/runtime/metrics_util.py +2 -1
  242. streamlit/runtime/pages_manager.py +1 -1
  243. streamlit/runtime/runtime.py +14 -3
  244. streamlit/runtime/runtime_util.py +1 -1
  245. streamlit/runtime/script_data.py +1 -1
  246. streamlit/runtime/scriptrunner/__init__.py +1 -1
  247. streamlit/runtime/scriptrunner/exec_code.py +34 -1
  248. streamlit/runtime/scriptrunner/magic.py +1 -1
  249. streamlit/runtime/scriptrunner/magic_funcs.py +1 -1
  250. streamlit/runtime/scriptrunner/script_cache.py +1 -1
  251. streamlit/runtime/scriptrunner/script_runner.py +22 -11
  252. streamlit/runtime/scriptrunner_utils/__init__.py +1 -1
  253. streamlit/runtime/scriptrunner_utils/exceptions.py +1 -1
  254. streamlit/runtime/scriptrunner_utils/script_requests.py +2 -1
  255. streamlit/runtime/scriptrunner_utils/script_run_context.py +2 -2
  256. streamlit/runtime/secrets.py +1 -1
  257. streamlit/runtime/session_manager.py +2 -2
  258. streamlit/runtime/state/__init__.py +1 -1
  259. streamlit/runtime/state/common.py +1 -1
  260. streamlit/runtime/state/query_params.py +1 -1
  261. streamlit/runtime/state/query_params_proxy.py +1 -1
  262. streamlit/runtime/state/safe_session_state.py +1 -1
  263. streamlit/runtime/state/session_state.py +1 -1
  264. streamlit/runtime/state/session_state_proxy.py +1 -1
  265. streamlit/runtime/state/widgets.py +1 -1
  266. streamlit/runtime/stats.py +1 -1
  267. streamlit/runtime/uploaded_file_manager.py +1 -1
  268. streamlit/runtime/websocket_session_manager.py +2 -2
  269. streamlit/source_util.py +1 -1
  270. streamlit/static/index.html +2 -2
  271. streamlit/static/static/js/{FileDownload.esm.Dod29kzi.js → FileDownload.esm.C6kraHI_.js} +1 -1
  272. streamlit/static/static/js/{FormClearHelper.0CIwSBFI.js → FormClearHelper.Tmz_sTQ8.js} +1 -1
  273. streamlit/static/static/js/{Hooks.D0iBCSJR.js → Hooks.ChcK58kV.js} +1 -1
  274. streamlit/static/static/js/{InputInstructions.BGz0VNnm.js → InputInstructions.CWlmC2Qh.js} +1 -1
  275. streamlit/static/static/js/{ProgressBar.BaPlfiH4.js → ProgressBar.C2LLZx-o.js} +1 -1
  276. streamlit/static/static/js/RenderInPortalIfExists.BFB9tinI.js +1 -0
  277. streamlit/static/static/js/Toolbar.DVKl5Ty9.js +1 -0
  278. streamlit/static/static/js/{base-input.Fk16kR45.js → base-input.C7heAUz7.js} +1 -1
  279. streamlit/static/static/js/{createSuper.B8NDjS0T.js → createSuper.BGkH3lYW.js} +1 -1
  280. streamlit/static/static/js/{data-grid-overlay-editor.C0oC7OWz.js → data-grid-overlay-editor.Cuw9mHAA.js} +1 -1
  281. streamlit/static/static/js/{downloader.BLJWoPl1.js → downloader.Dt0tCM6a.js} +1 -1
  282. streamlit/static/static/js/{es6.D9yZ4-e5.js → es6.DEWLLkEJ.js} +2 -2
  283. streamlit/static/static/js/{getPrototypeOf.DKKmv0PS.js → getPrototypeOf.BobjvJFE.js} +1 -1
  284. streamlit/static/static/js/{iframeResizer.contentWindow.Du2ek_IY.js → iframeResizer.contentWindow.Cb2nEQV3.js} +1 -1
  285. streamlit/static/static/js/index.2xRq6cMk.js +1 -0
  286. streamlit/static/static/js/{index.ncJlJcLt.js → index.3dqlv1Xq.js} +1 -1
  287. streamlit/static/static/js/index.4hESrvbj.js +1 -0
  288. streamlit/static/static/js/index.8LNY4OIb.js +4 -0
  289. streamlit/static/static/js/{index.Cfmr2hoW.js → index.B59J81FK.js} +1 -1
  290. streamlit/static/static/js/{index.CxrCMTqd.js → index.BAlqqBcw.js} +1 -1
  291. streamlit/static/static/js/index.BBm-I8-2.js +1 -0
  292. streamlit/static/static/js/{index.UMKksAwF.js → index.BEEgvyV0.js} +1 -1
  293. streamlit/static/static/js/{index.B8MC65SU.js → index.BSITpAz8.js} +29 -29
  294. streamlit/static/static/js/{index.K_IYAbRz.js → index.BU-BMiNz.js} +51 -51
  295. streamlit/static/static/js/{index.DcJiNltm.js → index.BbullfwT.js} +7 -7
  296. streamlit/static/static/js/index.BhvVhqgM.js +1 -0
  297. streamlit/static/static/js/index.Bo2RO7c-.js +1 -0
  298. streamlit/static/static/js/{index.C2_hseSo.js → index.BwUNY-jU.js} +1 -1
  299. streamlit/static/static/js/{index.C73yVjC9.js → index.C4ggakh6.js} +1 -1
  300. streamlit/static/static/js/index.C6zLaEWH.js +1 -0
  301. streamlit/static/static/js/{index.CaORMlLk.js → index.CJKd6KIo.js} +1 -1
  302. streamlit/static/static/js/{index.DCosyf4Y.js → index.CJrSPe9e.js} +1 -1
  303. streamlit/static/static/js/{index.clUXhmTv.js → index.CKpkbBQ4.js} +13 -13
  304. streamlit/static/static/js/{index.D-s5XFne.js → index.CKyzSK7k.js} +1 -1
  305. streamlit/static/static/js/{index.d_eIUHvJ.js → index.CWzMR4Qn.js} +2 -2
  306. streamlit/static/static/js/{index.DdG-jRFd.js → index.Caaf3Sch.js} +1 -1
  307. streamlit/static/static/js/{index.DU9T3IwS.js → index.CdNirCmM.js} +1 -1
  308. streamlit/static/static/js/{index.BAADbUet.js → index.CksJdg_n.js} +1 -1
  309. streamlit/static/static/js/{index.BZAVJreZ.js → index.Cq6-Dj2J.js} +1 -1
  310. streamlit/static/static/js/{index.CTvaDQgd.js → index.CsnZSzHc.js} +2 -2
  311. streamlit/static/static/js/index.D2_ysKuQ.js +1 -0
  312. streamlit/static/static/js/{index.BGyFJsxB.js → index.DA-jgmvJ.js} +1 -1
  313. streamlit/static/static/js/{index.CgHOvdK_.js → index.DSOFf22B.js} +1 -1
  314. streamlit/static/static/js/index.DTQeyNXN.js +1 -0
  315. streamlit/static/static/js/{index.CzDEy16B.js → index.De91xhMb.js} +1 -1
  316. streamlit/static/static/js/{index.CCLqv4q8.js → index.DeX4c3dx.js} +2 -2
  317. streamlit/static/static/js/{index.BEr9HRGP.js → index.IRVXqVyf.js} +1 -1
  318. streamlit/static/static/js/{index.DxFUMDyF.js → index.b1jbiVGt.js} +1 -1
  319. streamlit/static/static/js/{index.HXS4cXj6.js → index.bQJeNN28.js} +2 -2
  320. streamlit/static/static/js/{index.CGSuEM6K.js → index.kz_FxQ30.js} +1 -1
  321. streamlit/static/static/js/{index.BY0L_QLh.js → index.n7L72_oq.js} +2 -2
  322. streamlit/static/static/js/{input.BX19stYv.js → input.qSadM7Tx.js} +1 -1
  323. streamlit/static/static/js/{memory.DrUTC0E1.js → memory.CROZv0Mn.js} +1 -1
  324. streamlit/static/static/js/{mergeWith.DthgK8_y.js → mergeWith.CO-Rv2Zo.js} +1 -1
  325. streamlit/static/static/js/{number-overlay-editor.17BCi9Ve.js → number-overlay-editor.BRQhYKOF.js} +1 -1
  326. streamlit/static/static/js/{sandbox.5Hd92fh0.js → sandbox.CZKLDBgs.js} +1 -1
  327. streamlit/static/static/js/{slicedToArray.Bu7hUD78.js → slicedToArray.D36w_Bc8.js} +1 -1
  328. streamlit/static/static/js/{textarea.B2pXMKyh.js → textarea.DvzSPS_8.js} +1 -1
  329. streamlit/static/static/js/{timepicker.5G_63qYl.js → timepicker.FNAmrJ96.js} +1 -1
  330. streamlit/static/static/js/{uniqueId.B7repv29.js → uniqueId.BNjI9ROI.js} +1 -1
  331. streamlit/static/static/js/{useBasicWidgetState.ZoM17KXF.js → useBasicWidgetState.B8IH-5cn.js} +1 -1
  332. streamlit/static/static/js/{useOnInputChange.DK_jsnv_.js → useOnInputChange.B1QB2TNw.js} +1 -1
  333. streamlit/static/static/js/{withFullScreenWrapper.ByjGCoNh.js → withFullScreenWrapper.BT91IT4-.js} +1 -1
  334. streamlit/string_util.py +1 -1
  335. streamlit/temporary_directory.py +1 -1
  336. streamlit/testing/__init__.py +1 -1
  337. streamlit/testing/v1/__init__.py +1 -1
  338. streamlit/testing/v1/app_test.py +1 -1
  339. streamlit/testing/v1/element_tree.py +1 -1
  340. streamlit/testing/v1/local_script_runner.py +1 -1
  341. streamlit/testing/v1/util.py +1 -1
  342. streamlit/time_util.py +1 -1
  343. streamlit/type_util.py +1 -1
  344. streamlit/url_util.py +24 -1
  345. streamlit/user_info.py +94 -27
  346. streamlit/util.py +1 -1
  347. streamlit/version.py +1 -1
  348. streamlit/watcher/__init__.py +1 -1
  349. streamlit/watcher/event_based_path_watcher.py +1 -1
  350. streamlit/watcher/folder_black_list.py +1 -1
  351. streamlit/watcher/local_sources_watcher.py +1 -1
  352. streamlit/watcher/path_watcher.py +1 -1
  353. streamlit/watcher/polling_path_watcher.py +1 -1
  354. streamlit/watcher/util.py +1 -1
  355. streamlit/web/__init__.py +1 -1
  356. streamlit/web/bootstrap.py +1 -1
  357. streamlit/web/cache_storage_manager_config.py +1 -1
  358. streamlit/web/cli.py +1 -1
  359. streamlit/web/server/__init__.py +1 -1
  360. streamlit/web/server/app_static_file_handler.py +1 -1
  361. streamlit/web/server/authlib_tornado_integration.py +58 -0
  362. streamlit/web/server/browser_websocket_handler.py +72 -6
  363. streamlit/web/server/component_request_handler.py +1 -1
  364. streamlit/web/server/media_file_handler.py +1 -1
  365. streamlit/web/server/oauth_authlib_routes.py +176 -0
  366. streamlit/web/server/oidc_mixin.py +108 -0
  367. streamlit/web/server/routes.py +6 -4
  368. streamlit/web/server/server.py +41 -4
  369. streamlit/web/server/server_util.py +25 -1
  370. streamlit/web/server/stats_request_handler.py +1 -1
  371. streamlit/web/server/upload_file_request_handler.py +3 -2
  372. streamlit/web/server/websocket_headers.py +1 -1
  373. {streamlit_nightly-1.41.2.dev20250107.data → streamlit_nightly-1.41.2.dev20250109.data}/scripts/streamlit.cmd +1 -1
  374. {streamlit_nightly-1.41.2.dev20250107.dist-info → streamlit_nightly-1.41.2.dev20250109.dist-info}/METADATA +14 -2
  375. streamlit_nightly-1.41.2.dev20250109.dist-info/RECORD +560 -0
  376. {streamlit_nightly-1.41.2.dev20250107.dist-info → streamlit_nightly-1.41.2.dev20250109.dist-info}/WHEEL +1 -1
  377. streamlit/static/static/js/RenderInPortalIfExists.CDLwOGJN.js +0 -1
  378. streamlit/static/static/js/Toolbar.U2Q07lHA.js +0 -1
  379. streamlit/static/static/js/index.C9Sks6cD.js +0 -1
  380. streamlit/static/static/js/index.CIPlrA_8.js +0 -1
  381. streamlit/static/static/js/index.CKwgjL0N.js +0 -1
  382. streamlit/static/static/js/index.Chl3QVe6.js +0 -1
  383. streamlit/static/static/js/index.Cnd636WC.js +0 -4
  384. streamlit/static/static/js/index.DbcRQLkk.js +0 -1
  385. streamlit/static/static/js/index.DnCNih03.js +0 -1
  386. streamlit/static/static/js/index.NpAsSk7C.js +0 -1
  387. streamlit/static/static/js/index.n6SGF_yN.js +0 -1
  388. streamlit/vendor/ipython/__init__.py +0 -0
  389. streamlit/vendor/ipython/modified_sys_path.py +0 -67
  390. streamlit_nightly-1.41.2.dev20250107.dist-info/RECORD +0 -556
  391. {streamlit_nightly-1.41.2.dev20250107.dist-info → streamlit_nightly-1.41.2.dev20250109.dist-info}/entry_points.txt +0 -0
  392. {streamlit_nightly-1.41.2.dev20250107.dist-info → streamlit_nightly-1.41.2.dev20250109.dist-info}/top_level.txt +0 -0
streamlit/user_info.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -14,62 +14,129 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import TYPE_CHECKING, Iterator, Mapping, NoReturn, Union
17
+ from typing import (
18
+ TYPE_CHECKING,
19
+ Final,
20
+ Iterator,
21
+ Mapping,
22
+ NoReturn,
23
+ Union,
24
+ )
18
25
 
19
- from streamlit.errors import StreamlitAPIException
26
+ from streamlit import config, runtime
27
+ from streamlit.auth_util import (
28
+ encode_provider_token,
29
+ get_secrets_auth_section,
30
+ is_authlib_installed,
31
+ validate_auth_credentials,
32
+ )
33
+ from streamlit.errors import StreamlitAPIException, StreamlitAuthError
34
+ from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
35
+ from streamlit.runtime.metrics_util import gather_metrics
20
36
  from streamlit.runtime.scriptrunner_utils.script_run_context import (
21
37
  get_script_run_ctx as _get_script_run_ctx,
22
38
  )
39
+ from streamlit.url_util import make_url_path
23
40
 
24
41
  if TYPE_CHECKING:
25
42
  from streamlit.runtime.scriptrunner_utils.script_run_context import UserInfo
26
43
 
27
44
 
45
+ AUTH_LOGIN_ENDPOINT: Final = "/auth/login"
46
+ AUTH_LOGOUT_ENDPOINT: Final = "/auth/logout"
47
+
48
+
49
+ @gather_metrics("login")
50
+ def login(provider: str | None = None) -> None:
51
+ """Initiate the login for the given provider.
52
+
53
+ Parameters
54
+ ----------
55
+ provider : str or None
56
+ The provider to use for login. This value must match the name of a
57
+ provider configured in the app's auth section of ``secrets.toml`` file.
58
+ If None, the default provider in the auth section will be used.
59
+ """
60
+ if provider is None:
61
+ provider = "default"
62
+
63
+ context = _get_script_run_ctx()
64
+ if context is not None:
65
+ if not is_authlib_installed():
66
+ raise StreamlitAuthError(
67
+ """To use authentication features, you need to install """
68
+ """Authlib>=1.3.2, e.g. via `pip install Authlib`."""
69
+ )
70
+ validate_auth_credentials(provider)
71
+ fwd_msg = ForwardMsg()
72
+ fwd_msg.auth_redirect.url = generate_login_redirect_url(provider)
73
+ context.enqueue(fwd_msg)
74
+
75
+
76
+ @gather_metrics("logout")
77
+ def logout() -> None:
78
+ """Logout the current user."""
79
+ context = _get_script_run_ctx()
80
+ if context is not None:
81
+ context.user_info.clear()
82
+ session_id = context.session_id
83
+
84
+ if runtime.exists():
85
+ instance = runtime.get_instance()
86
+ instance.clear_user_info_for_session(session_id)
87
+
88
+ base_path = config.get_option("server.baseUrlPath")
89
+
90
+ fwd_msg = ForwardMsg()
91
+ fwd_msg.auth_redirect.url = make_url_path(base_path, AUTH_LOGOUT_ENDPOINT)
92
+ context.enqueue(fwd_msg)
93
+
94
+
95
+ def generate_login_redirect_url(provider: str) -> str:
96
+ """Generate the login redirect URL for the given provider."""
97
+ provider_token = encode_provider_token(provider)
98
+ base_path = config.get_option("server.baseUrlPath")
99
+ login_path = make_url_path(base_path, AUTH_LOGIN_ENDPOINT)
100
+ return f"{login_path}?provider={provider_token}"
101
+
102
+
28
103
  def _get_user_info() -> UserInfo:
29
104
  ctx = _get_script_run_ctx()
30
105
  if ctx is None:
31
106
  # TODO: Add appropriate warnings when ctx is missing
32
107
  return {}
33
- return ctx.user_info
108
+ context_user_info = ctx.user_info.copy()
109
+
110
+ auth_section_exists = get_secrets_auth_section()
111
+ if "is_logged_in" not in context_user_info and auth_section_exists:
112
+ context_user_info["is_logged_in"] = False
113
+ return context_user_info
34
114
 
35
115
 
36
- class UserInfoProxy(Mapping[str, Union[str, None]]):
116
+ class UserInfoProxy(Mapping[str, Union[str, bool, None]]):
37
117
  """
38
118
  A read-only, dict-like object for accessing information about current user.
39
119
 
40
- ``st.experimental_user`` is dependant on the host platform running the
120
+ ``st.experimental_user`` is dependent on the host platform running the
41
121
  Streamlit app. If the host platform has not configured the function, it
42
122
  will behave as it does in a locally running app.
43
123
 
44
- Properties can by accessed via key or attribute notation. For example,
124
+ Properties can be accessed via key or attribute notation. For example,
45
125
  ``st.experimental_user["email"]`` or ``st.experimental_user.email``.
46
126
 
47
- Attributes
48
- ----------
49
- email : str
50
- If running locally, this property returns the string literal
51
- ``"test@example.com"``.
52
-
53
- If running on Streamlit Community Cloud, this
54
- property returns one of two values:
55
-
56
- - ``None`` if the user is not logged in or not a member of the app's\
57
- workspace. Such users appear under anonymous pseudonyms in the app's\
58
- analytics.
59
- - The user's email if the the user is logged in and a member of the\
60
- app's workspace. Such users are identified by their email in the app's\
61
- analytics.
62
-
63
127
  """
64
128
 
65
- def __getitem__(self, key: str) -> str | None:
66
- return _get_user_info()[key]
129
+ def __getitem__(self, key: str) -> str | bool | None:
130
+ try:
131
+ return _get_user_info()[key]
132
+ except KeyError:
133
+ raise KeyError(f'st.experimental_user has no key "{key}".')
67
134
 
68
- def __getattr__(self, key: str) -> str | None:
135
+ def __getattr__(self, key: str) -> str | bool | None:
69
136
  try:
70
137
  return _get_user_info()[key]
71
138
  except KeyError:
72
- raise AttributeError
139
+ raise AttributeError(f'st.experimental_user has no attribute "{key}".')
73
140
 
74
141
  def __setattr__(self, name: str, value: str | None) -> NoReturn:
75
142
  raise StreamlitAPIException("st.experimental_user cannot be modified")
streamlit/util.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
streamlit/version.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
streamlit/watcher/util.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
streamlit/web/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
streamlit/web/cli.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -0,0 +1,58 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING, Any, Sequence
18
+
19
+ from authlib.integrations.base_client import ( # type: ignore[import-untyped]
20
+ FrameworkIntegration,
21
+ )
22
+
23
+ from streamlit.runtime.secrets import AttrDict
24
+
25
+ if TYPE_CHECKING:
26
+ from streamlit.web.server.oidc_mixin import TornadoOAuth
27
+
28
+
29
+ class TornadoIntegration(FrameworkIntegration): # type: ignore[misc]
30
+ def update_token(self, token, refresh_token=None, access_token=None):
31
+ """We do not support access token refresh, since we obtain and operate only on
32
+ identity tokens. We override this method explicitly to implement all abstract
33
+ methods of base class.
34
+ """
35
+
36
+ @staticmethod
37
+ def load_config(
38
+ oauth: TornadoOAuth, name: str, params: Sequence[str]
39
+ ) -> dict[str, Any]:
40
+ """Configure Authlib integration with provider parameters
41
+ specified in secrets.toml
42
+ """
43
+
44
+ # oauth.config here is an auth section from secrets.toml
45
+ # We parse it here to transform nested AttrDict (for client_kwargs value)
46
+ # to dict so Authlib can work with it under the hood.
47
+ if not oauth.config:
48
+ return {}
49
+
50
+ prepared_config = {}
51
+ for key in params:
52
+ value = oauth.config.get(name, {}).get(key, None)
53
+ if isinstance(value, AttrDict):
54
+ # We want to modify client_kwargs further after loading server metadata
55
+ value = value.to_dict()
56
+ if value is not None:
57
+ prepared_config[key] = value
58
+ return prepared_config
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -16,14 +16,17 @@ from __future__ import annotations
16
16
 
17
17
  import base64
18
18
  import binascii
19
+ import hmac
19
20
  import json
20
21
  from typing import TYPE_CHECKING, Any, Awaitable, Final
22
+ from urllib.parse import urlparse
21
23
 
22
24
  import tornado.concurrent
23
25
  import tornado.locks
24
26
  import tornado.netutil
25
27
  import tornado.web
26
28
  import tornado.websocket
29
+ from tornado.escape import utf8
27
30
  from tornado.websocket import WebSocketHandler
28
31
 
29
32
  from streamlit import config
@@ -31,7 +34,11 @@ from streamlit.logger import get_logger
31
34
  from streamlit.proto.BackMsg_pb2 import BackMsg
32
35
  from streamlit.runtime import Runtime, SessionClient, SessionClientDisconnectedError
33
36
  from streamlit.runtime.runtime_util import serialize_forward_msg
34
- from streamlit.web.server.server_util import is_url_from_allowed_origins
37
+ from streamlit.web.server.server_util import (
38
+ AUTH_COOKIE_NAME,
39
+ is_url_from_allowed_origins,
40
+ is_xsrf_enabled,
41
+ )
35
42
 
36
43
  if TYPE_CHECKING:
37
44
  from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
@@ -50,13 +57,63 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
50
57
  # need to read the self.xsrf_token manually to set the cookie as a side
51
58
  # effect. See https://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection
52
59
  # for more details.
53
- if config.get_option("server.enableXsrfProtection"):
60
+ if is_xsrf_enabled():
54
61
  _ = self.xsrf_token
55
62
 
63
+ def get_signed_cookie(
64
+ self,
65
+ name: str,
66
+ value: str | None = None,
67
+ max_age_days: float = 31,
68
+ min_version: int | None = None,
69
+ ) -> bytes | None:
70
+ """Get a signed cookie from the request. Added for compatibility with
71
+ Tornado < 6.3.0.
72
+ See release notes: https://www.tornadoweb.org/en/stable/releases/v6.3.0.html#deprecation-notices
73
+ """
74
+ try:
75
+ return super().get_signed_cookie(name, value, max_age_days, min_version)
76
+ except AttributeError:
77
+ return super().get_secure_cookie(name, value, max_age_days, min_version)
78
+
56
79
  def check_origin(self, origin: str) -> bool:
57
80
  """Set up CORS."""
58
81
  return super().check_origin(origin) or is_url_from_allowed_origins(origin)
59
82
 
83
+ def _validate_xsrf_token(self, supplied_token: str) -> bool:
84
+ """Inspired by tornado.web.RequestHandler.check_xsrf_cookie method,
85
+ to check the XSRF token passed in Websocket connection header.
86
+ """
87
+ _, token, _ = self._decode_xsrf_token(supplied_token)
88
+ _, expected_token, _ = self._get_raw_xsrf_token()
89
+
90
+ decoded_token = utf8(token)
91
+ decoded_expected_token = utf8(expected_token)
92
+
93
+ if not decoded_token or not decoded_expected_token:
94
+ return False
95
+ return hmac.compare_digest(decoded_token, decoded_expected_token)
96
+
97
+ def _parse_user_cookie(self, raw_cookie_value: bytes, email: str) -> dict[str, Any]:
98
+ """Process the user cookie and extract the user info after
99
+ validating the origin. Origin is validated for security reasons.
100
+ """
101
+ cookie_value = json.loads(raw_cookie_value)
102
+ user_info = {}
103
+
104
+ cookie_value_origin = cookie_value.get("origin", None)
105
+ parsed_origin_from_header = urlparse(self.request.headers["Origin"])
106
+ expected_origin_value = (
107
+ parsed_origin_from_header.scheme + "://" + parsed_origin_from_header.netloc
108
+ )
109
+ if cookie_value_origin == expected_origin_value:
110
+ user_info["is_logged_in"] = cookie_value.get("is_logged_in", False)
111
+ del cookie_value["origin"]
112
+ del cookie_value["is_logged_in"]
113
+ user_info.update(cookie_value)
114
+
115
+ return user_info
116
+
60
117
  def write_forward_msg(self, msg: ForwardMsg) -> None:
61
118
  """Send a ForwardMsg to the browser."""
62
119
  try:
@@ -102,11 +159,13 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
102
159
  is_public_cloud_app = user_obj["isPublicCloudApp"]
103
160
  except (KeyError, binascii.Error, json.decoder.JSONDecodeError):
104
161
  email = "test@example.com"
105
-
106
- user_info: dict[str, str | None] = {
107
- "email": None if is_public_cloud_app else email
162
+ user_info: dict[str, str | bool | None] = {
163
+ "email": None if is_public_cloud_app else email,
108
164
  }
109
165
 
166
+ if is_public_cloud_app or email == "test@example.com":
167
+ user_info.pop("email", None)
168
+
110
169
  existing_session_id = None
111
170
  try:
112
171
  ws_protocols = [
@@ -114,6 +173,13 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
114
173
  for p in self.request.headers["Sec-Websocket-Protocol"].split(",")
115
174
  ]
116
175
 
176
+ raw_cookie_value = self.get_signed_cookie(AUTH_COOKIE_NAME)
177
+ if is_xsrf_enabled() and raw_cookie_value:
178
+ csrf_protocol_value = ws_protocols[1]
179
+
180
+ if self._validate_xsrf_token(csrf_protocol_value):
181
+ user_info.update(self._parse_user_cookie(raw_cookie_value, email))
182
+
117
183
  if len(ws_protocols) >= 3:
118
184
  # See the NOTE in the docstring of the `select_subprotocol` method above
119
185
  # for a detailed explanation of why this is done.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -0,0 +1,176 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ from typing import Any
18
+ from urllib.parse import urlparse
19
+
20
+ import tornado.web
21
+
22
+ from streamlit.auth_util import (
23
+ AuthCache,
24
+ decode_provider_token,
25
+ generate_default_provider_section,
26
+ get_secrets_auth_section,
27
+ )
28
+ from streamlit.errors import StreamlitAuthError
29
+ from streamlit.url_util import make_url_path
30
+ from streamlit.web.server.oidc_mixin import TornadoOAuth, TornadoOAuth2App
31
+ from streamlit.web.server.server_util import AUTH_COOKIE_NAME
32
+
33
+ auth_cache = AuthCache()
34
+
35
+
36
+ def create_oauth_client(provider: str) -> tuple[TornadoOAuth2App, str]:
37
+ """Create an OAuth client for the given provider based on secrets.toml configuration."""
38
+ auth_section = get_secrets_auth_section()
39
+ if auth_section:
40
+ redirect_uri = auth_section.get("redirect_uri", None)
41
+ config = auth_section.to_dict()
42
+ else:
43
+ config = {}
44
+ redirect_uri = "/"
45
+
46
+ provider_section = config.setdefault(provider, {})
47
+
48
+ if not provider_section and provider == "default":
49
+ provider_section = generate_default_provider_section(auth_section)
50
+ config["default"] = provider_section
51
+
52
+ provider_client_kwargs = provider_section.setdefault("client_kwargs", {})
53
+ if "scope" not in provider_client_kwargs:
54
+ provider_client_kwargs["scope"] = "openid email profile"
55
+ if "prompt" not in provider_client_kwargs:
56
+ provider_client_kwargs["prompt"] = "select_account"
57
+
58
+ oauth = TornadoOAuth(config, cache=auth_cache)
59
+ oauth.register(provider)
60
+ return oauth.create_client(provider), redirect_uri
61
+
62
+
63
+ class AuthHandlerMixin(tornado.web.RequestHandler):
64
+ """Mixin for handling auth cookies. Added for compatibility with Tornado < 6.3.0."""
65
+
66
+ def initialize(self, base_url: str) -> None:
67
+ self.base_url = base_url
68
+
69
+ def redirect_to_base(self) -> None:
70
+ self.redirect(make_url_path(self.base_url, "/"))
71
+
72
+ def set_auth_cookie(self, user_info: dict[str, Any]) -> None:
73
+ serialized_cookie_value = json.dumps(user_info)
74
+ try:
75
+ # We don't specify Tornado secure flag here because it leads to missing cookie on Safari.
76
+ # The OIDC flow should work only on secure context anyway (localhost or HTTPS),
77
+ # so specifying the secure flag here will not add anything in terms of security.
78
+ self.set_signed_cookie(
79
+ AUTH_COOKIE_NAME,
80
+ serialized_cookie_value,
81
+ httpOnly=True,
82
+ )
83
+ except AttributeError:
84
+ self.set_secure_cookie(
85
+ AUTH_COOKIE_NAME,
86
+ serialized_cookie_value,
87
+ httponly=True,
88
+ )
89
+
90
+ def clear_auth_cookie(self) -> None:
91
+ self.clear_cookie(AUTH_COOKIE_NAME)
92
+
93
+
94
+ class AuthLoginHandler(AuthHandlerMixin, tornado.web.RequestHandler):
95
+ async def get(self):
96
+ """Redirect to the OAuth provider login page."""
97
+ provider = self._parse_provider_token()
98
+ if provider is None:
99
+ self.redirect_to_base()
100
+ return
101
+
102
+ client, redirect_uri = create_oauth_client(provider)
103
+ try:
104
+ client.authorize_redirect(self, redirect_uri)
105
+ except Exception as e:
106
+ self.send_error(400, reason=str(e))
107
+
108
+ def _parse_provider_token(self) -> str | None:
109
+ provider_token = self.get_argument("provider", None)
110
+ try:
111
+ if provider_token is None:
112
+ raise StreamlitAuthError("Missing provider token")
113
+ payload = decode_provider_token(provider_token)
114
+ except StreamlitAuthError:
115
+ return None
116
+
117
+ return payload["provider"]
118
+
119
+
120
+ class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
121
+ def get(self):
122
+ self.clear_auth_cookie()
123
+ self.redirect_to_base()
124
+
125
+
126
+ class AuthCallbackHandler(AuthHandlerMixin, tornado.web.RequestHandler):
127
+ async def get(self):
128
+ provider = self._get_provider_by_state()
129
+ origin = self._get_origin_from_secrets()
130
+ if origin is None:
131
+ self.redirect_to_base()
132
+ return
133
+
134
+ error = self.get_argument("error", None)
135
+ if error:
136
+ self.redirect_to_base()
137
+ return
138
+
139
+ if provider is None:
140
+ self.redirect_to_base()
141
+ return
142
+
143
+ client, _ = create_oauth_client(provider)
144
+ token = client.authorize_access_token(self)
145
+ user = token.get("userinfo")
146
+
147
+ cookie_value = dict(user, origin=origin, is_logged_in=True)
148
+ if user:
149
+ self.set_auth_cookie(cookie_value)
150
+ self.redirect_to_base()
151
+
152
+ def _get_provider_by_state(self) -> str | None:
153
+ state_code_from_url = self.get_argument("state")
154
+ current_cache_keys = list(auth_cache.get_dict().keys())
155
+ state_provider_mapping = {}
156
+ for key in current_cache_keys:
157
+ _, _, recorded_provider, code = key.split("_")
158
+ state_provider_mapping[code] = recorded_provider
159
+
160
+ provider: str | None = state_provider_mapping.get(state_code_from_url, None)
161
+ return provider
162
+
163
+ def _get_origin_from_secrets(self) -> str | None:
164
+ redirect_uri = None
165
+ auth_section = get_secrets_auth_section()
166
+ if auth_section:
167
+ redirect_uri = auth_section.get("redirect_uri", None)
168
+
169
+ if not redirect_uri:
170
+ return None
171
+
172
+ redirect_uri_parsed = urlparse(redirect_uri)
173
+ origin_from_redirect_uri: str = (
174
+ redirect_uri_parsed.scheme + "://" + redirect_uri_parsed.netloc
175
+ )
176
+ return origin_from_redirect_uri