beamer
beamer copied to clipboard
How to work with guards?
Hm... I don't understand why my guard is not working.
MaterialApp.router(
...
routerDeletgate: BeamerDelagate(guards: BeamerRouter.guards),
...
)
class BeamerRouter {
static final guards = <BeamGuard>[
BeamGuard(
pathPatterns: ['/alerts'],
check: (context, location) => state.isSessionInitialized,
beamToNamed: (origin, target) {
return '/';
}
),
BeamGuard(
pathPatterns: ['/'],
check: (context, location) => state.isSessionInitialized,
beamToNamed: (origin, target) {
return target.state.routeInformation.location;
}
),
];
}
My initial route is /
and login route is also /
.
Then I type in browser 'http://127.0.0.1/alerts' after app initializing it tries to open /alerts
and redirects to /
route which shows login form. After successful authentication I execute
Beamer.of(context).update()
but it is not redirected to /alerts
page and still in login /
page. In both guards origin
and target
always return the same value. What is wrong or what I did wrong?
What I need is if I typed some secured (guarded) URL I need to be redirected to login route and after successful authentication to be redirected to requested URL.
And second one is, I need to check if user has permission to specific route it must be redirected there, if those route is denied he must be redirected to first from available routes and if there is no any permissions show user not found
page.
Schematically I need to implement some route dispatcher.
/alerts |-----------------------| yes
--------> | is /alerts permitted? | ------> show alerts page
| -------- | ---------|
| no
|-----------------------|
| are there available | yes
| routes? | -------> show first
|-----------------------|
|no
|--------------------> show "page not found"
Is it possible to implement this using guards?
Hey @bambinoua Thanks for creating a separate issue for this.
What I need is if I typed some secured (guarded) URL I need to be redirected to login route and after successful authentication to be redirected to requested URL.
The flow you are describing seems like a useful feature to have, but is not supported out of the box currently. Currently,
-
origin
is from where you are beaming, i.e. the route that the user is on whenbeamTo*
is called. -
target
is the route that the user tries to beam to, i.e. the input ofbeamTo*
.
There is no concept of "where the user tried to beam previously, but failed". This is something you would have to store in your state and return that (if present) instead of return target.state.routeInformation.location;
.
I'm interested in introducing that feature of "remembering failed routes" into Beamer. If you wish to work on it or have a good idea how it should look, please let me know.
and still in login / page
This seems correct from what I described above, but is the app still showing "login screen"? That shouldn't be the case and is probably an issue with state management. The page at /
will not rebuild after calling update
if its key
is the same as it were during the last build. Maybe a better approach would be to have a separate /login
route, but it is not impossible with "multipurpose /
", but then it needs to be handled carefully with state management and/or corresponding BeamPage.key
.
Schematically I need to implement some route dispatcher.
Generally, about the diagram you drew, I'm not sure about the "no" flow. I like to do the following:
BeamGuard(
guardNonMatching: true,
pathPatterns: ['/login', '/register'],
check: (context, _) {
final state = context.read<AuthenticationBloc>().state;
return state.status == AuthenticationStatus.authenticated;
},
beamToNamed: (_, __) => '/login',
),
which is a guard that will guard every route except /login
or /register
from unauthenticated user. Basically, there doesn't exist a case where there are "no available routes", but you still can accomplish that "not found fallback" if you define guards that will sequentially fail and redirect to each other until the last one returns some "not found" route, e.g.:
guards: [
BeamGuard(
pathPatterns: ['/admin'],
check: (_, __) => isAdmin(),
beamToNamed: (_, __) => '/employee',
),
BeamGuard(
pathPatterns: ['/employee'],
check: (_, __) => isEmployee(),
beamToNamed: (_, __) => '/user',
),
BeamGuard(
pathPatterns: ['/user'],
check: (_, __) => isUser(),
beamToNamed: (_, __) => '/not-found',
),
],
What I need is if I typed some secured (guarded) URL I need to be redirected to login route and after successful authentication to be redirected to requested URL.
I had the idea you suggested and below the way I implemented this:
class LoginRouteState {
const LoginRouteState({this.redirectTo});
final String redirectTo;
}
class LoginBeamLocation extends BeamLocation<BeamState> {
LoginBeamLocation({
RouteState? routeState,
BeamParameters? beamParameters,
}) : super(
routeState != null ?
RouteInformation(location: '/login', state: routeState) : null,
beamParameters);
...
}
class SecureBeamLocation extends BeamLocation<BeamState> {
SecureBeamLocation(
String routeName, {
RouteState? routeState,
BeamParameters? beamParameters,
}) : super(RouteInformation(location: routeName, state: routeState), beamParameters);
...
}
<BeamGuard>[
BeamGuard(
pathPatterns: ['/page1','/page2',...],
check: (context, location) => AppState.isAuthenticated,
beamTo: (context, origin, target) {
final routeState = LoginRouteState(
redirectTo: target.state.routeInformation.location!)
return LoginBeamLocation(routeState: routeState);
},
),
]
After successul login:
void onSuccess() {
final beamerDelegate = Beamer.of(context);
final routeState = beamerDelegate.configuration.state as LoginRouteState;
final routeName = routeState.redirectTo.isNotEmpty ? routeState.redirectTo : '/dashboard';
beamerDelegate.beamToReplacement(SecureBeamLocation(routeName: routeName));
}
The only problem I have is the title of page is not changed to title for beamed
page and left as for Login BeamPage
. It looks like there is a bug somewhere in switching routes.
but you still can accomplish that "not found fallback" if you define guards that will sequentially fail and redirect to each other until the last one returns some "not found" route, e.g
The problem is how to initiate this guards re-execution?
For example the current route is /login
. User clicks Login button.
LoginPage(onSuccess: () {
// Here it is required to restart guards
Beamer.of(context).update();
});
But you noticed that page is not refreshed because url /login
is not changed. So how can I restart guards so?
The only way I see is:
LoginPage(onSuccess: () {
final availableRoutes = securityBloc.getPermittedRoutes(userId);
if (availableRoutes.isNotEmpty)
Beamer.of(context).beamTo(SecureLocation(availableRoutes.first));
else
Beamer.of(context).beamTo(SecureLocation('/forbidden'));
});
But this approach does not use BeamGuard
s.
@bambinoua
Hm, the Beamer.of(context).update();
really should re-run the guards, regardless of any configuration :thinking: