Repository navigation
fix: clear the web sign-in tokens before the app loads; auto-connect only to a saved server - #596
Merged
Merged
Conversation
An inline script at the top of `web/index.html` moves the `#/auth/callback?…` query into `window.soliplexCallbackQuery` and rewrites the URL before any other resource loads. `captureNow` reads that stash, and falls back to the URL with a value-free error log when the script is missing, so a fork without it still signs in. The script follows `<title>`, so the history entry Chrome records for the callback URL shows the title rather than the tokens; the entry still holds them, and only a backend change keeps them out of the URL. `clearCallbackUrl` keeps main's page-load clear: it drops `location.search` and every hash query and keeps the route, so no outside link can hand the app a query. It and the script build the URL as origin + pathname + route, so a pathname such as `//evil.example/` can't make `replaceState` cross-origin. Query-string callbacks and the `access_token` key are no longer read; only fragment-form backends are supported. `docs/authoring-a-flavor.md` documents the script for forks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`docs/developer-setup.md` lists what the backend's OIDC config and the identity provider's client must allow: `openid` in the scope, the frontend origin policy and its consent page, `accepted_azp_list` for native sign-in, and the client's Web Origins for web token refresh. The web entry page note says the history list, not the entry, shows the page title. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A callback query with malformed percent-encoding or UTF-8 (`%FF`) threw from `Uri.splitQueryString` at boot and stopped the app from starting. It now yields `WebCallbackMalformed`, which the callback screen shows as "The sign-in response could not be read", and logs a warning that carries the failure's type, not the query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The home route's `?url=` can come from outside the app: iOS deep linking is on by default and the app registers `ai.soliplex.client`, and on web a same-tab hash change reaches the route without a reload. Only a saved server is filled in and connected, at the address it was added with; any other address is ignored, with a warning that carries no value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`CallbackParams`, the type of `standardFlavor`'s `callbackParams`, is exported, so the fork example in `docs/authoring-a-flavor.md` can capture, clear and pass the callback. The docs say where `scope` and `accepted_azp_list` live in the backend's OIDC config, which `http://` origins the backend refuses, and that iOS deep links reach a declared path; comments name every case they claim. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
After a web sign-in, the callback's tokens leave the address bar as the page
starts parsing, before the app downloads, instead of once Flutter has booted.
The page-load clear stays
main's: every query is dropped, so no outside linkcan hand the app one. A callback that can't be decoded shows an error instead
of stopping the app, and an auto-connect address connects only to a saved
server.
What changed
2163bd0f). An inline script after<title>inweb/index.htmlmoves the#/auth/callback?…query intowindow.soliplexCallbackQueryand rewrites the URL.captureNowreads thatstash. When the script is missing, it reads the URL and logs one error with
no values, so a fork without the script still signs in.
clearCallbackUrlkeepsmain's behaviour: it dropslocation.searchand every hash query and keeps the route.
pathname such as
//evil.example/can't makereplaceStatecross-origin.
urlWithoutQueries, a pure function with tests.v0.82.2,v0.83.2,v0.84+).4279dda3).docs/developer-setup.mdlists whatthe backend's OIDC config and the IdP client must allow:
openidinscope;accepted_azp_listfor native sign-in;d044822e). A query with bad percent-encoding orUTF-8 (
%FF) used to throw at boot. It now yieldsWebCallbackMalformed,and the callback screen shows "The sign-in response could not be read". The
warning carries the failure's type, not the query.
0aea2202). The home route's?url=can come fromoutside the app: iOS deep linking is on by default and the app registers
ai.soliplex.client, and on web a same-tab hash change doesn't reload.added with.
140bc0fa).CallbackParamsis exported, and the fork example captures, clears andpasses it.
scopeandaccepted_azp_listlive, whichhttp://origins are refused, and that an iOS deep link reaches a declared path.
Library consumers:
CallbackParamsis exported. Forks with their ownweb/index.htmlneed the script indocs/authoring-a-flavor.md; without it,sign-in still works and the app logs an error.
Behaviour worth knowing
main: the lobby's?server=,diagnostics'
?run=and the quiz's?from=. Reload-stable parameters are afollow-up.
history. Placing the script after
<title>makes Chrome's history listshow the title rather than the tokens. Only a backend change keeps the
tokens out of the URL.
(before
v0.82.2/v0.83.2) no longer works.returnTokeepsmain's handling. Binding it to the server it wasissued for is the next PR, with the rest of the route-input hardening.
Tests
urlWithoutQueries: search, hash queries, the callback route, and a//hostpathname.carries no part of the query.
@userinfo and scheme-less forms that read as the saved host, isneither filled in nor connected, and its warning carries no part of it. An ignored address
with an empty list falls through to the default URL.
main's auto-connect test now registers its server first.fail under the gate: auto-connect needs a saved server, and the default URL
only fills an empty list.
Test Plan
thread_view_state_testdisposed-signal trace.flutter analyzereports no issues,dart formatis clean,markdownlint is clean, and
flutter build websucceeds.test/modules/auth.values.
#/?url=…doesn't connect.#/lobby?server=xdrops the query.http://localhost:9001//evil.example/#/auth/callback?x=1launches.%FFshows its error message.🤖 Generated with Claude Code