Skip to content

feat(routing): re-run redirects on auth-state change#105

Open
anilcancakir wants to merge 4 commits into
masterfrom
feat/routing-redirect-on-auth-change
Open

feat(routing): re-run redirects on auth-state change#105
anilcancakir wants to merge 4 commits into
masterfrom
feat/routing-redirect-on-auth-change

Conversation

@anilcancakir

Copy link
Copy Markdown
Contributor

What

MagicRouter evaluated 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 routerConfig on 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 bound auth guard are unaffected (notifier absent, listener is a no-op).

Notes

  • lib/src/routing/magic_router.dart; covered by test/routing/router_auth_refresh_test.dart.
  • CHANGELOG updated under [Unreleased] > Added.

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.
Copilot AI review requested due to automatic review settings July 17, 2026 23:23

Copilot AI left a comment

Copy link
Copy Markdown

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 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: ...) to Auth.stateNotifier so 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

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants