From 066421af6283423cf9087e3edc38b3b46e9dc858 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 9 Oct 2026 18:20:27 +0200 Subject: [PATCH 1/6] Return 404 for unknown paths with the hash route URL strategy With hash routing the client never asks the server for route paths, but FletStaticFiles still served index.html with 200 OK for any missing path. Skip the SPA fallback in hash mode, so unknown paths get a real 404 (the assets dir's 404.html if there is one). --- CHANGELOG.md | 4 ++++ .../src/flet_web/fastapi/flet_static_files.py | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05403a6120..0330a07ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ **Compatibility:** Apps built with `flet build` and the macOS desktop client now require macOS 12 or later, and iOS apps require iOS 15 or later. Custom build templates keep building, because Flutter 3.47 raises the deployment targets during the build; apply the same `Podfile` and Xcode project changes to skip that step. See the [macOS 12 and iOS 15 minimum](/docs/updates/breaking-changes/v1-1-0/minimum-macos-12-ios-15) guide. +### Bug fixes + +* Fix Flet web apps using the `hash` route URL strategy returning `index.html` with a `200 OK` for unknown paths instead of a `404` ([#2796](https://github.com/flet-dev/flet/issues/2796)) by @ndonkoHenri. + ### Documentation * Group the Reference > Controls section of the docs into Core, Material, Cupertino, Shadcn and Extensions ([#6918](https://github.com/flet-dev/flet/pull/6918)) by @InesaFitsner. diff --git a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py index 8adde9f207..bd4b29416a 100644 --- a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py +++ b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py @@ -126,9 +126,16 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: def lookup_path(self, path: str) -> tuple[str, Optional[os.stat_result]]: """Resolve a static file path, with SPA fallback for client-side routes. - Route-like paths (no extension, or `.html`) that don't match a file - fall back to `index.html` so the Flutter client can handle routing. - Asset-like paths (any other extension) resolve to a real `404`. + With the `path` route URL strategy, route-like paths (no extension, or + `.html`) that don't match a file fall back to `index.html` so the + Flutter client can handle routing. Asset-like paths (any other + extension) resolve to a real `404`. + + With the `hash` strategy, routes live in the URL fragment and never + reach the server, so there is no fallback: any path that doesn't match + a file resolves to a real `404` (with a `404.html` from the assets + directory as its body, if there is one), while the mount root still + serves `index.html`. Args: path: Requested path, relative to the mounted static root @@ -141,7 +148,10 @@ def lookup_path(self, path: str) -> tuple[str, Optional[os.stat_result]]: logger.debug(f"StaticFiles.lookup_path: {self.__app_mount_path} {path}") full_path, stat_result = super().lookup_path(path) - if stat_result is not None: + if ( + stat_result is not None + or self.__route_url_strategy == RouteUrlStrategy.HASH + ): return full_path, stat_result # Not found: SPA fallback only for route-like paths. From 8fa4416060841033aa300d61842830d45f6f6bfd Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sat, 10 Oct 2026 23:39:53 +0200 Subject: [PATCH 2/6] Accept string route_url_strategy and web_renderer in the ASGI app FletStaticFiles stored route_url_strategy and web_renderer as given, and patch_index_html reads their .value, so a string such as "hash" made every request fail with a 500. flet.fastapi.app() passes the arguments through unchanged, and ft.run(..., export_asgi_app=True) returns before it converts strings. Convert them to the enums in FletStaticFiles, which covers both entry points. The routing guide's link text named the environment variable FLET_ROUTE_URL_STRATEGY; it is FLET_WEB_ROUTE_URL_STRATEGY. --- CHANGELOG.md | 1 + .../flet-web/src/flet_web/fastapi/flet_static_files.py | 10 ++++++---- website/docs/cookbook/navigation-and-routing.md | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0330a07ab1..991b394382 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ ### Bug fixes * Fix Flet web apps using the `hash` route URL strategy returning `index.html` with a `200 OK` for unknown paths instead of a `404` ([#2796](https://github.com/flet-dev/flet/issues/2796)) by @ndonkoHenri. +* Fix `flet.fastapi.app()` and `ft.run(..., export_asgi_app=True)` failing every request with a `500` error when `route_url_strategy` or `web_renderer` is passed as a string, such as `"hash"` by @ndonkoHenri. ### Documentation diff --git a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py index bd4b29416a..55e5a2aea8 100644 --- a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py +++ b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py @@ -69,8 +69,10 @@ class FletStaticFiles(StaticFiles): app_name: PWA application name. app_short_name: PWA application short name. app_description: PWA application description. - web_renderer: Type of web renderer. - route_url_strategy: Routing URL strategy. + web_renderer: Type of web renderer, as a `WebRenderer` or its value, e.g. + `"canvaskit"`. + route_url_strategy: Routing URL strategy, as a `RouteUrlStrategy` or its + value, e.g. `"hash"`. no_cdn: Whether not load CanvasKit, Pyodide, and fonts from CDN. websocket_endpoint_path: Path of Flet app WebSocket handler. A path without a leading `/` is resolved against the app mount path. @@ -97,8 +99,8 @@ def __init__( self.__app_name = app_name self.__app_short_name = app_short_name self.__app_description = app_description - self.__web_renderer = web_renderer - self.__route_url_strategy = route_url_strategy + self.__web_renderer = WebRenderer(web_renderer) + self.__route_url_strategy = RouteUrlStrategy(route_url_strategy) self.__no_cdn = no_cdn self.__websocket_endpoint_path = websocket_endpoint_path self.__once = Once() diff --git a/website/docs/cookbook/navigation-and-routing.md b/website/docs/cookbook/navigation-and-routing.md index 1b1e44c970..d880e921c5 100644 --- a/website/docs/cookbook/navigation-and-routing.md +++ b/website/docs/cookbook/navigation-and-routing.md @@ -122,7 +122,7 @@ import flet as ft ft.run(main, route_url_strategy="hash") ``` -For Flet server deployments, you can also set the [`FLET_ROUTE_URL_STRATEGY`](../reference/environment-variables.md#flet_web_route_url_strategy) +For Flet server deployments, you can also set the [`FLET_WEB_ROUTE_URL_STRATEGY`](../reference/environment-variables.md#flet_web_route_url_strategy) environment variable. ## Declarative Router From 73fdc1cff0d6124317036183cb87c06956cb1e53 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 11 Oct 2026 00:58:44 +0200 Subject: [PATCH 3/6] Move the hash routing entries into the 1.1.0 Bug fixes section --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 991b394382..b4766ba12a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,6 @@ **Compatibility:** Apps built with `flet build` and the macOS desktop client now require macOS 12 or later, and iOS apps require iOS 15 or later. Custom build templates keep building, because Flutter 3.47 raises the deployment targets during the build; apply the same `Podfile` and Xcode project changes to skip that step. See the [macOS 12 and iOS 15 minimum](/docs/updates/breaking-changes/v1-1-0/minimum-macos-12-ios-15) guide. -### Bug fixes - * Fix Flet web apps using the `hash` route URL strategy returning `index.html` with a `200 OK` for unknown paths instead of a `404` ([#2796](https://github.com/flet-dev/flet/issues/2796)) by @ndonkoHenri. * Fix `flet.fastapi.app()` and `ft.run(..., export_asgi_app=True)` failing every request with a `500` error when `route_url_strategy` or `web_renderer` is passed as a string, such as `"hash"` by @ndonkoHenri. From 097a6d0dc25dc85c9f7490548342e0d9af38bb2e Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 11 Oct 2026 01:00:29 +0200 Subject: [PATCH 4/6] Link #6954 in the 1.1.0 changelog entries --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4766ba12a..a34199df06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,8 @@ **Compatibility:** Apps built with `flet build` and the macOS desktop client now require macOS 12 or later, and iOS apps require iOS 15 or later. Custom build templates keep building, because Flutter 3.47 raises the deployment targets during the build; apply the same `Podfile` and Xcode project changes to skip that step. See the [macOS 12 and iOS 15 minimum](/docs/updates/breaking-changes/v1-1-0/minimum-macos-12-ios-15) guide. -* Fix Flet web apps using the `hash` route URL strategy returning `index.html` with a `200 OK` for unknown paths instead of a `404` ([#2796](https://github.com/flet-dev/flet/issues/2796)) by @ndonkoHenri. -* Fix `flet.fastapi.app()` and `ft.run(..., export_asgi_app=True)` failing every request with a `500` error when `route_url_strategy` or `web_renderer` is passed as a string, such as `"hash"` by @ndonkoHenri. +* Fix Flet web apps using the `hash` route URL strategy returning `index.html` with a `200 OK` for unknown paths instead of a `404` ([#2796](https://github.com/flet-dev/flet/issues/2796), [#6954](https://github.com/flet-dev/flet/pull/6954)) by @ndonkoHenri. +* Fix `flet.fastapi.app()` and `ft.run(..., export_asgi_app=True)` failing every request with a `500` error when `route_url_strategy` or `web_renderer` is passed as a string, such as `"hash"` ([#6954](https://github.com/flet-dev/flet/pull/6954)) by @ndonkoHenri. ### Documentation From 7688de38886b9212a5e899e9d102f3c18929dba3 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 11 Oct 2026 01:48:19 +0200 Subject: [PATCH 5/6] Return to the page's route after OAuth login with redirect_to_page page.login(redirect_to_page=True) built the return URL as a path URL from the WebSocket path: `/store` even with the hash route URL strategy, where the route belongs in the fragment and an unknown path is a 404. The WebSocket path also misses a prefix that a proxy strips (proxy_path) and picks up the folder of a nested WebSocket endpoint. FletApp now takes the route URL strategy that FletStaticFiles writes into index.html, environment override included, and the page's base path, computed like the served from proxy_path and the ASGI root path. login() returns to `#` with the hash strategy and to `` otherwise. --- CHANGELOG.md | 1 + .../flet-web/src/flet_web/fastapi/app.py | 29 ++++++++++--------- .../flet-web/src/flet_web/fastapi/flet_app.py | 13 +++++++++ .../src/flet_web/fastapi/flet_static_files.py | 8 +++++ .../packages/flet/src/flet/controls/page.py | 17 ++++++++--- .../flet/src/flet/messaging/connection.py | 7 +++++ 6 files changed, 57 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a34199df06..80afa5e3cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ * Fix Flet web apps using the `hash` route URL strategy returning `index.html` with a `200 OK` for unknown paths instead of a `404` ([#2796](https://github.com/flet-dev/flet/issues/2796), [#6954](https://github.com/flet-dev/flet/pull/6954)) by @ndonkoHenri. * Fix `flet.fastapi.app()` and `ft.run(..., export_asgi_app=True)` failing every request with a `500` error when `route_url_strategy` or `web_renderer` is passed as a string, such as `"hash"` ([#6954](https://github.com/flet-dev/flet/pull/6954)) by @ndonkoHenri. +* Fix `page.login(redirect_to_page=True)` not returning to the page's route with the `hash` route URL strategy or under a `proxy_path` ([#6954](https://github.com/flet-dev/flet/pull/6954)) by @ndonkoHenri. ### Documentation diff --git a/sdk/python/packages/flet-web/src/flet_web/fastapi/app.py b/sdk/python/packages/flet-web/src/flet_web/fastapi/app.py index d532a890e3..dcc931f671 100644 --- a/sdk/python/packages/flet-web/src/flet_web/fastapi/app.py +++ b/sdk/python/packages/flet-web/src/flet_web/fastapi/app.py @@ -92,6 +92,18 @@ def app( fastapi_app = FastAPI() + static_files = FletStaticFiles( + proxy_path=proxy_path, + assets_dir=assets_dir, + app_name=app_name, + app_short_name=app_short_name, + app_description=app_description, + web_renderer=web_renderer, + route_url_strategy=route_url_strategy, + websocket_endpoint_path=websocket_endpoint, + no_cdn=no_cdn, + ) + @fastapi_app.websocket(f"/{websocket_endpoint}") async def app_handler(websocket: WebSocket): """ @@ -109,6 +121,8 @@ async def app_handler(websocket: WebSocket): oauth_state_timeout_seconds=oauth_state_timeout_seconds, upload_endpoint_path=upload_endpoint_path, secret_key=secret_key, + route_url_strategy=static_files.route_url_strategy, + proxy_path=proxy_path, ).handle(websocket) if upload_dir: @@ -133,20 +147,7 @@ async def oauth_redirect_handler(request: Request): """ return await FletOAuth().handle(request) - fastapi_app.mount( - path="/", - app=FletStaticFiles( - proxy_path=proxy_path, - assets_dir=assets_dir, - app_name=app_name, - app_short_name=app_short_name, - app_description=app_description, - web_renderer=web_renderer, - route_url_strategy=route_url_strategy, - websocket_endpoint_path=websocket_endpoint, - no_cdn=no_cdn, - ), - ) + fastapi_app.mount(path="/", app=static_files) # Add middleware for custom headers class CustomHeadersMiddleware(BaseHTTPMiddleware): diff --git a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py index d81b806a67..9367394c28 100644 --- a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py +++ b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py @@ -16,6 +16,7 @@ from flet.controls.base_control import BaseControl from flet.controls.context import _context_page, context from flet.controls.exceptions import FletPageDisconnectedException +from flet.controls.types import RouteUrlStrategy from flet.messaging.connection import Connection from flet.messaging.protocol import ( ControlEventBody, @@ -59,6 +60,10 @@ class FletApp(Connection): and redirecting to OAuth callback URL. upload_endpoint_path: Absolute URL of upload endpoint, e.g. `/upload`. secret_key: Secret key to sign upload requests. + route_url_strategy: Route URL strategy the client uses, so that URLs built + for it, such as the page to return to after an OAuth redirect, match. + proxy_path: URL prefix that a proxy in front of the app strips, as given + to `flet_web.fastapi.app()`; part of the page's base path. """ def __init__( @@ -71,8 +76,12 @@ def __init__( oauth_state_timeout_seconds: int = DEFAULT_FLET_OAUTH_STATE_TIMEOUT, upload_endpoint_path: Optional[str] = None, secret_key: Optional[str] = None, + route_url_strategy: RouteUrlStrategy = RouteUrlStrategy.PATH, + proxy_path: Optional[str] = None, ): super().__init__() + self.route_url_strategy = route_url_strategy + self.__proxy_path = proxy_path self.__id = random_string(8) logger.info(f"New FletApp: {self.__id}") @@ -124,6 +133,10 @@ async def handle(self, websocket: WebSocket): self.pubsubhub = app_manager.get_pubsubhub(self.__main, loop=self.loop) self.page_url = str(websocket.url).rsplit("/", 1)[0] self.page_name = websocket.url.path.rsplit("/", 1)[0].lstrip("/") + mount_path = ( + (self.__proxy_path or "") + websocket.scope.get("root_path", "") + ).strip("/") + self.page_base_path = f"/{mount_path}/" if mount_path else "/" if not self.__upload_endpoint_path: self.__upload_endpoint_path = ( diff --git a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py index 55e5a2aea8..ea0fe5fd02 100644 --- a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py +++ b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py @@ -121,6 +121,14 @@ def __init__( logger.info(f"Route URL strategy configured: {self.__route_url_strategy}") logger.info(f"No CDN configured: {self.__no_cdn}") + @property + def route_url_strategy(self) -> RouteUrlStrategy: + """ + The route URL strategy written into the served `index.html`: the + `route_url_strategy` argument, or `FLET_WEB_ROUTE_URL_STRATEGY` if set. + """ + return self.__route_url_strategy + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: await self.__once.do(self.__config, scope["root_path"]) await super().__call__(scope, receive, send) diff --git a/sdk/python/packages/flet/src/flet/controls/page.py b/sdk/python/packages/flet/src/flet/controls/page.py index eabbce590a..47b8d41486 100644 --- a/sdk/python/packages/flet/src/flet/controls/page.py +++ b/sdk/python/packages/flet/src/flet/controls/page.py @@ -53,6 +53,7 @@ DeviceOrientation, Locale, PagePlatform, + RouteUrlStrategy, Wrapper, ) from flet.utils import is_pyodide @@ -1224,10 +1225,18 @@ async def login( if complete_page_html: auth_attrs["completePageHtml"] = complete_page_html if redirect_to_page: - up = urlparse(provider.redirect_url) - auth_attrs["completePageUrl"] = up._replace( - path=f"{self.session.connection.page_name}{self.route}" - ).geturl() + connection = self.session.connection + base_path = connection.page_base_path + redirect_url = urlparse(provider.redirect_url) + if connection.route_url_strategy == RouteUrlStrategy.HASH: + complete_page_url = redirect_url._replace( + path=base_path, query="", fragment=self.route + ) + else: + complete_page_url = redirect_url._replace( + path=base_path.rstrip("/") + self.route + ) + auth_attrs["completePageUrl"] = complete_page_url.geturl() self.session.connection.oauth_authorize(auth_attrs) if on_open_authorization_url: await on_open_authorization_url(authorization_url) diff --git a/sdk/python/packages/flet/src/flet/messaging/connection.py b/sdk/python/packages/flet/src/flet/messaging/connection.py index fd7a1e7584..b8f3383219 100644 --- a/sdk/python/packages/flet/src/flet/messaging/connection.py +++ b/sdk/python/packages/flet/src/flet/messaging/connection.py @@ -3,6 +3,7 @@ from concurrent.futures import ThreadPoolExecutor from typing import Any, Optional +from flet.controls.types import RouteUrlStrategy from flet.messaging.protocol import Message from flet.pubsub.pubsub_hub import PubSubHub @@ -21,6 +22,12 @@ class Connection: def __init__(self): self.page_name: str = "" self.page_url: Optional[str] = None + # Base path of the page in the browser URL, e.g. `/sub-app/`, which is + # the `` of the served web client. + self.page_base_path: str = "/" + # How the client represents routes in its URL, for URLs built for it, + # such as the page to return to after an OAuth redirect. + self.route_url_strategy: RouteUrlStrategy = RouteUrlStrategy.PATH # True when the connected client runs on the same machine (or in the # same process), so DataChannel bytes move at local-IPC / memcpy # speed. Widgets may use this to prefer uncompressed payloads over From 00926c1356c7a188a8b363f73e726a332f89da48 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sun, 11 Oct 2026 10:48:39 +0200 Subject: [PATCH 6/6] Document Page.login() and the Connection page attributes Page.login() only said "Starts OAuth flow." It now describes the authorization code flow and the saved-token restore, every argument and the return value, with short comments on the steps in its body. Connection's page_base_path, route_url_strategy and local_data_transport get attribute docstrings instead of comments, and the route URL strategy docs on Connection and FletApp no longer single out OAuth. --- .../flet-web/src/flet_web/fastapi/flet_app.py | 3 +- .../packages/flet/src/flet/controls/page.py | 55 ++++++++++++++++++- .../flet/src/flet/messaging/connection.py | 15 +++-- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py index 9367394c28..a3c32aae94 100644 --- a/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py +++ b/sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app.py @@ -60,8 +60,7 @@ class FletApp(Connection): and redirecting to OAuth callback URL. upload_endpoint_path: Absolute URL of upload endpoint, e.g. `/upload`. secret_key: Secret key to sign upload requests. - route_url_strategy: Route URL strategy the client uses, so that URLs built - for it, such as the page to return to after an OAuth redirect, match. + route_url_strategy: Route URL strategy of the served web client. proxy_path: URL prefix that a proxy in front of the app strips, as given to `flet_web.fastapi.app()`; part of the page's base path. """ diff --git a/sdk/python/packages/flet/src/flet/controls/page.py b/sdk/python/packages/flet/src/flet/controls/page.py index 47b8d41486..e851e3b105 100644 --- a/sdk/python/packages/flet/src/flet/controls/page.py +++ b/sdk/python/packages/flet/src/flet/controls/page.py @@ -1206,10 +1206,56 @@ async def login( authorization: "Optional[type[AT]]" = None, ) -> "AT": """ - Starts OAuth flow. + Signs the user in with an OAuth provider. - See [Authentication](https://flet.dev/docs/cookbook/authentication) + Without `saved_token`, this starts the OAuth authorization code flow: it + opens the provider's authorization page and returns without waiting for + the user to sign in. The provider then sends the browser to its + `redirect_url`, and Flet exchanges the authorization code for a token, + fetches the user if requested and calls [`on_login`][flet.Page.on_login]. + Handling that redirect needs the app to be served by Flet's web server. + + With `saved_token`, this restores an earlier sign-in instead, without + opening any page: it refreshes the token if it has expired and can be + refreshed, fetches the user if requested and calls `on_login`. + + See the [Authentication](https://flet.dev/docs/cookbook/authentication) guide for more information and examples. + + Args: + provider: The OAuth provider to sign in with. + fetch_user: Whether to fetch the signed-in user's details, available + as the authorization's `user`. + fetch_groups: Whether to also fetch the user's groups into + `user.groups`. Groups are fetched only together with the user. + Of the built-in providers, only GitHub provides them; with the + others, `groups` is empty. + scope: Scopes to request in addition to the provider's default ones. + saved_token: A token from an earlier sign-in to restore, as returned + by [`OAuthToken.to_json()`][flet.auth.OAuthToken.to_json]. + on_open_authorization_url: Async function that opens the + authorization URL it receives. By default, a web app opens it in + a popup window, and other apps open it with + [`UrlLauncher.launch_url()`][flet.UrlLauncher.launch_url]. + complete_page_html: HTML of the page that the browser shows when the + provider redirects back, if `redirect_to_page` is `False`. By + default, the page tries to close itself and asks the user to + close it. + redirect_to_page: Whether to send the browser back to the app's + current page and route when the provider redirects back, instead + of showing `complete_page_html`. Use it in web apps that open the + authorization page in the same browser tab, for example with + `UrlLauncher().launch_url(url, web_only_window_name="_self")` in + `on_open_authorization_url`. + authorization: Class that performs the authorization. It is created + as `authorization(provider, fetch_user=..., fetch_groups=..., + scope=...)`. Defaults to + [`AuthorizationService`][flet.auth.AuthorizationService]. + + Returns: + The authorization, also available as [`auth`][flet.Page.auth]. + Without `saved_token`, it has no token or user until `on_login` + is called. """ if authorization is None: authorization = _default_authorization_impl() @@ -1220,11 +1266,15 @@ async def login( scope=scope, ) if saved_token is None: + # The server keeps the state and completion options until the + # provider redirects back, then calls _authorize_callback(). authorization_url, state = self.__authorization.get_authorization_data() auth_attrs = {"state": state} if complete_page_html: auth_attrs["completePageHtml"] = complete_page_html if redirect_to_page: + # The current page on the redirect URL's host, with the route + # in the URL path or after `#`, as the client expects it. connection = self.session.connection base_path = connection.page_base_path redirect_url = urlparse(provider.redirect_url) @@ -1248,6 +1298,7 @@ async def login( else: await UrlLauncher().launch_url(authorization_url) else: + # Restore the saved sign-in; there is no redirect to wait for. await self.__authorization.dehydrate_token(saved_token) e = LoginEvent(name="login", control=self, error="", error_description="") diff --git a/sdk/python/packages/flet/src/flet/messaging/connection.py b/sdk/python/packages/flet/src/flet/messaging/connection.py index b8f3383219..4fc87b8139 100644 --- a/sdk/python/packages/flet/src/flet/messaging/connection.py +++ b/sdk/python/packages/flet/src/flet/messaging/connection.py @@ -22,17 +22,16 @@ class Connection: def __init__(self): self.page_name: str = "" self.page_url: Optional[str] = None - # Base path of the page in the browser URL, e.g. `/sub-app/`, which is - # the `` of the served web client. self.page_base_path: str = "/" - # How the client represents routes in its URL, for URLs built for it, - # such as the page to return to after an OAuth redirect. + """Base path of the page in the browser URL, e.g. `/sub-app/`, which is the + `` of the served web client.""" self.route_url_strategy: RouteUrlStrategy = RouteUrlStrategy.PATH - # True when the connected client runs on the same machine (or in the - # same process), so DataChannel bytes move at local-IPC / memcpy - # speed. Widgets may use this to prefer uncompressed payloads over - # encoded ones. + """Whether the client keeps routes in the URL path (`/store`) + or after `#` (`/#/store`).""" self.local_data_transport: bool = False + """`True` when the connected client runs on the same machine (or in the + same process), so DataChannel bytes move at local-IPC / memcpy speed. + Widgets may use this to prefer uncompressed payloads over encoded ones.""" self.__pubsubhub = None self.__loop: Optional[AbstractEventLoop] = None self.__executor: Optional[ThreadPoolExecutor] = None