7

I am new to flutter and want to make an app which only authenticated users (via filling email and password) can use. So I am thinking of making a root widget called Authentication where I will check if the user is authenticated or not. If they are then take them to Home() Widget (which is basically app home page) else to SignIn() Widget (which is signin page) .

  • I have to check for user authentication in 2 senerios:
    • When app opens so that previously signed in user does not have to signin again
    • After filling up Email-Id and Password user clicks on Sign In button

I am facing the following problem and don't know how do I solve them ( Assume that there is no signup option and user data is entered to db directly not through some UI ):

  • If a user is already signed in how to take them directly to Home() Widget ?

    This is code of what I have currently I am not sure if is the right way of doing this type of app so if there is a better way to do this please tell :


    import 'package:flutter/material.dart';
    import 'package:myapp/home.dart';
    import 'package:myapp/signIn.dart';


    void main() => runApp(Authenticate());

    class Authenticate extends StatelessWidget {


      final String authenticateEmail;
      final String authenticatePassword;

      Authenticate({this.authenticateEmail, this.authenticatePassword}); 




      // Make a call to db to check if the data provided is correct or not 
      // If it is correct we then navigate the user to Home() Widget 
      // Else to SignIn() Widget

  @override
  Widget build(BuildContext context) {

     if ( make some call to db to check if data is there) {
       return Home();
     }
     else {
       return SignIn();
     }

  }
}

The issue with above code is when the app is opened for the first time how to take user directly to SignIn() Widget should I initilize the fields with empty string and in method check for condition if string is empty or not ?

Karan Mehta
  • 1,442
  • 13
  • 32
sid597
  • 999
  • 3
  • 16
  • 31
  • First thing will the user be only on mobile app or should be saved to a backend server? If only on device why you need a user after all? By the way basic concept is ok about the screen as per user authenticated or not. Just check that wether an authenticated row exists in db or not. You need not to check full authentication code again – Dev Feb 07 '20 at 11:59
  • 1
    user will be saved to backend – sid597 Feb 07 '20 at 12:13

4 Answers4

5

Use user sessions instead. Check out FlutterSession. The package adds user session support in Flutter and is easy to use.

bool isAuth = Authenticate({this.authenticateEmail, this.authenticatePassword});

// Store value to session
await FlutterSession().set("isAuth", isAuth);

// Retrieve item from session
dynamic token = await FlutterSession().get("isAuth");
Jhourlad Estrella
  • 3,545
  • 4
  • 37
  • 66
  • You may wanna update the link to https://pub.dev/packages/flutter_session, that one doesn't work – illright Dec 04 '20 at 19:14
  • Thanks for pointing it out. Link is now working. – Jhourlad Estrella Dec 05 '20 at 08:51
  • Thanks for the info. But what happens if the user changes their password via another device. The authentication on the first device won't know that the password has been changed and will remain authorised? Should it therefore always perform a periodic check against the online password? If so, won't it have to store the user's inputted password/username somewhere in the session? – Delmontee Dec 11 '20 at 11:21
  • It's a bad idea to store sensitive information in the session. How it is often done is to save the token to your session storage and use that to identify the user on the server side. – Jhourlad Estrella Dec 11 '20 at 16:25
4

Create a sqlite table and then when authenticating user is successful from server save required details to local table (delete any previous row saved before saving).

Now just check on app start that whether user table is empty or not and decide where the flow should be.

On first run table will be empty by default so it will show login page

APP RUN -> Check if row exists in user table

if YES ->  return Home();
else -> return SignIn();
Dev
  • 6,628
  • 2
  • 25
  • 34
3

The root widget which you are calling Authentication is the app.

In pubspec.yaml add two dependencies

provider: ^4.0.3
shared_preferences: ^0.5.6+1

Similar to the example, I wrote on Flutter provider state management, logout concept.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

// If successful, returns a token. Otherwise throws an error.
typedef Future<String> SignIn({
  @required final String username,
  @required final String password,
});

// If successful, returns void. Otherwise throws an error.
typedef Future<void> SignOut({
  @required final String token,
});

// ----------------------------------------------------------------------------

class Session with ChangeNotifier {
  String _token; // can be null
  final SignIn _signIn;
  final SignOut _signOut;

  Session({
    @required String token,
    @required SignIn signIn,
    @required SignOut signOut,
  })  : assert(signIn != null),
        assert(signOut != null),
        this._token = token,
        this._signIn = signIn,
        this._signOut = signOut;

  Future<void> signIn({
    @required final String username,
    @required final String password,
  }) async {
    assert(username != null);
    assert(password != null);

    final String token = await this._signIn(
      username: username,
      password: password,
    );
    this._token = token;
    this.notifyListeners();
  }

  Future<void> signOut() async {
    await this._signOut(token: this._token);
    this._token = null;
    this.notifyListeners();
  }

  bool get isAuthenticated {
    return (this._token != null);
  }
}

// ----------------------------------------------------------------------------

Future<void> main() async {
  // The app would load and present an error message on screen to add the following line.
  // https://github.com/flutter/flutter/issues/39620
  // https://api.flutter.dev/flutter/widgets/WidgetsFlutterBinding/ensureInitialized.html
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize some local persistence.
  // Could be SQLite or somewhere encrypted.
  SharedPreferences prefs = await SharedPreferences.getInstance();

  final Session session = Session(
    token: prefs.getString('token'),
    signIn: ({
      @required final String username,
      @required final String password,
    }) async {
      assert(username != null);
      assert(password != null);

      // Contact the server to validate credentials and get token.
      await Future.delayed(Duration(milliseconds: 500)); // network delay
      if ((username != 'johnsmith') || (password != 'opensesame')) {
        throw new Exception("bad username or password");
      } else {
        final String token = '9djdhy89032jfdhdf70912';
        // Store the token locally.
        prefs.setString('token', token);
        return token;
      }
    },
    signOut: ({@required final String token}) async {
      assert(token != null);

      // Contact the server to sign out.
      await Future.delayed(Duration(milliseconds: 500)); // network delay
      // Update the local storage.
      prefs.setString('token', null);
    },
  );

  return runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<Session>.value(value: session),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return Consumer<Session>(
      builder: (final BuildContext context, final Session session, final Widget child) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(primarySwatch: Colors.blue),
          home: session.isAuthenticated ? MyHomePage() : MySignInPage(),
        );
      },
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Home [Auth Protected]")),
      body: Center(
        child: RaisedButton(
          child: const Text("Sign Out"),
          onPressed: () {
            final Session session = Provider.of<Session>(context, listen: false);
            session.signOut();
          },
        ),
      ),
    );
  }
}

// Of course this would have username and password inputs and a submit button.
class MySignInPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Sign In")),
      body: Center(
        child: RaisedButton(
          child: const Text("Sign In with johnsmith, opensesame"),
          onPressed: () {
            final Session session = Provider.of<Session>(context, listen: false);
            session.signIn(username: 'johnsmith', password: 'opensesame');
          },
        ),
      ),
    );
  }
}
Ted Henry
  • 1,442
  • 1
  • 14
  • 34
0

You can use

shared_preferences: xxx (version)

package to store user response.

Example (Using DIO for HTTP Request) :

Future<bool> saveUser(String user) async {
final prefs = await SharedPreferences.getInstance();
return prefs.setString('', user);
}

Future<dynamic> login(String username, String password) async {


try {
  var res = await dio.post("LOGIN-URL",
      data: {"user": username, "pass": password});

  if (res.statusCode==200) {
        
    final userIsStored =
        await saveUser(jsonEncode(res.data));
       
    if (userIsStored) {
      print('USER HAS BE STORED TO SHAREDPREFERENCES');
    }
    User.fromJson(res.data);
  } else {
  }
} catch (e) {
  throw e.response;
}