feat(routing): re-run redirects on auth-state change#105
Open
anilcancakir wants to merge 4 commits into
Open
Conversation
Wire the GoRouter refreshListenable to the auth guard stateNotifier (resolved defensively, null when auth is unbound) so a passive 401 -> logout ejects the user from a protected screen to login. Adds router_auth_refresh_test covering the eject + the resting-on-login no-loop case.
There was a problem hiding this comment.
Pull request overview
This PR updates MagicRouter so GoRouter re-evaluates redirect guards (auth / guest) immediately when auth state changes (e.g., passive logout on token expiry, or login while sitting on /login), rather than only during explicit navigation. It does this by wiring GoRouter’s refreshListenable to the auth guard’s stateNotifier, and adds a dedicated routing test plus an [Unreleased] changelog entry.
Changes:
- Wire
GoRouter(refreshListenable: ...)toAuth.stateNotifierso redirects re-run on auth transitions. - Add widget tests validating redirect re-evaluation on passive logout and ensuring no redirect loops while resting on
/login. - Document the behavior under
CHANGELOG.md→[Unreleased]→Added.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
lib/src/routing/magic_router.dart |
Adds refreshListenable resolution from Auth.stateNotifier to trigger redirect re-evaluation on auth state changes. |
test/routing/router_auth_refresh_test.dart |
Adds widget tests covering passive logout redirect refresh and a no-loop scenario on /login. |
CHANGELOG.md |
Documents the new redirect refresh behavior under [Unreleased] > Added. |
Comment on lines
204
to
216
| /// Build the GoRouter from registered definitions. | ||
| GoRouter _buildRouter() { | ||
| return GoRouter( | ||
| navigatorKey: navigatorKey, | ||
| initialLocation: _initialLocation, | ||
| observers: _observers, | ||
| routes: _buildRoutes(), | ||
| redirect: _handleRedirect, | ||
| refreshListenable: _resolveAuthRefreshListenable(), | ||
| onException: (context, state, router) { | ||
| Log.warning('Route not found: ${state.uri}'); | ||
| }, | ||
| ); |
Comment on lines
+238
to
+253
| Listenable? _resolveAuthRefreshListenable() { | ||
| try { | ||
| return Auth.stateNotifier; | ||
| } catch (e) { | ||
| // Use debugPrint, not the Log facade: this runs at router-build time, | ||
| // which can precede the container binding of the log service (and of | ||
| // auth itself). Resolving Log here would throw the very "service not | ||
| // registered" error we are guarding against, mirroring the | ||
| // container-free warning in [setInitialLocation]. | ||
| debugPrint( | ||
| 'MagicRouter: auth state notifier unavailable; redirects will not ' | ||
| 're-run on auth-state changes ($e).', | ||
| ); | ||
| return null; | ||
| } | ||
| } |
Comment on lines
+69
to
+73
| testWidgets( | ||
| 'a logged-out user resting on /login does not loop', | ||
| (tester) async { | ||
| // Guest (no user) sitting on the guest route: the guest middleware must | ||
| // return null (allow), so the refresh-driven re-evaluation stays put and |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…stener Address PR review: reset() dropped _router without disposing it, leaking the GoRouter's internal subscription to the auth refreshListenable (the orphaned router kept reacting to auth changes). Dispose it before nulling the reference.
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.
What
MagicRouterevaluated its redirect guards ('auth'/'guest') only while resolving a route. An auth transition that happened while the user was already on a page (token expiry, background sign-out, a successful login on the auth screen) did not move them off a now-forbidden route until the next manual navigation.Change
The router now listens to the auth guard's state notifier and refreshes
routerConfigon a change, so an auth transition re-evaluates redirects immediately: an expired session bounces to login, a login leaves the guest-only auth screen. Consumers with no boundauthguard are unaffected (notifier absent, listener is a no-op).Notes
lib/src/routing/magic_router.dart; covered bytest/routing/router_auth_refresh_test.dart.[Unreleased] > Added.