Chapmacl
Chapmacl

Reputation: 181

Prevent unauthenticated users from navigating to a route using a URL in a flutter web app?

I'm attempting to create a web app in Flutter that would ideally work like a website. Users would log in and be able to see their dashboards with private information, but there are certain screens with public information that anyone could navigate to using a dynamic route URL like: myflutterapp.com/feed/usersPublicInfo, where this link could be posted to another website, for example, and the user could go straight into this route.

Clearly using named Routes is a solution I would need, but then what is preventing someone from going directly to a route where private information is? Or at least preventing a user from going to a page with null information and causing the widgets to crash?

Solution idea 1

Use named Routes and after the login screen, pass the user variable to each screen, and have a nullcheck before the route is generated. If it's null, that means someone skipped some routes and they are sent back to login

Solution idea 2

If possible, use a mix of named routes and "unnamed" routes, such as the feed, dashboard, and login screen. Once the user makes it to the dashboard, all routes are handled using Navigator.push(), preventing them from being reached from a URL

Other Solutions?

I'm fairly new at flutter and flutter web, so I could be going at this problem all wrong. I would appreciate some advice. Thanks!

Upvotes: 7

Views: 5310

Answers (2)

小鹿-
小鹿-

Reputation: 61

static Future<void> init() async {
    WidgetsFlutterBinding.ensureInitialized();
    if(kIsWeb) {
      final String defaultRouteName = WidgetsBinding.instance.window.defaultRouteName;
      if(!(defaultRouteName == 'home' || defaultRouteName == 'login' || defaultRouteName == '/')) {
        SystemNavigator.routeUpdated(
           routeName: '/',
           previousRouteName: null
        );
      }
    }
    ...
}

Upvotes: 6

Aldy Yuan
Aldy Yuan

Reputation: 2055

Solution 1 looks good to me.

For my suggestion use Provider for state management. Here's my example base on my context :

    routes: {
      "/": (context) => MainPage(),
      "/detail": (context) => UserDetailPage(),
      "/other": (context) => OtherPage(),
    },
 builder: (context, child) {
          return Consumer<UsersProvider>(
            child: child,
            builder: (context, provider, child) {
              if (provider.isLoading) {
                return Scaffold(
                  body: Center(
                    child: CircularProgressIndicator(),
                  ),
                );
              }

              final value = provider.user;
              if (value == null) {
                return Navigator(
                  onGenerateRoute: (settings) => MaterialPageRoute(
                      settings: settings, builder: (context) => LoginPage()),
                );
              }

              return MultiProvider(
                providers: [
                  ChangeNotifierProvider(create: (context) => UsersProvider()),
                  ChangeNotifierProvider(
                      create: (context) => InvoicesProvider()),
                  ChangeNotifierProvider(create: (context) => EventsProvider()),
                ],
                child: child,
              );
            },
          );

Basically use builder in main.dart and defines routes, then inside builder use Consumer were child is the initial route MainPage() so if the user already login they will go there, and if not, base on flag which is in my context is isAdmin they will redirect to LoginPage(). So if they try to go to detail or other by using url they will redirect to LoginPage anyway.

Here's my UserProvider:

  getUser() async {
    FirebaseAuth.instance.onAuthStateChanged.listen((currentUser) async {
      _isLoading = false;
      if (currentUser == null) {
        _user = null;
        notifyListeners();
        return;
      }
      final data = await Firestore.instance
          .document("users/${currentUser.uid}")
          .snapshots()
          .first;
      if (data.exists) {
        print(data.data);
        if (data.data['is_admin'] == true) {
          _user = User.fromMap(data.data);
          notifyListeners();
        }
        return null;
      }
    });
  }

So if the user isAdmin it will return their value. I hope you can understand my explanation. I hope this is helpful for you too.

Upvotes: 3

Related Questions