Manage social user details in Google Flutter application
Asked Answered
C

7

7

I am a beginner in developing Flutter application, and try to create a sample application for educational purpose. Last few weeks I decided to do a sample application in flutter that have no inbuilt login or register section because it's have social login options like Facebook and Google. I searched the web and got many code examples for how to implement Facebook and Google authentication in Flutter application.

I have a doubt about this social login implementation maybe it's happening because of lack of working experience in mobile application architecture level. At this point I am asking a question to myself "How to manage social login users in applications"

I have a solution like if the user can login ed successfully, store the user details into our (Firebase) db check the user email exists in database if there is no matching entries in db it will create a new user in database or if exists update the last login date time of that particular user. Because this application shows some user related data in other forms, it's work on the basis of logined userid, so I need to store the logined user details in the database.

Corves answered 27/9, 2019 at 3:50 Comment(0)
V
3

By using the built-in sign in using Firebase Authentication SDK (Official Guide), you will be able to obtain login information for different platforms like Google, Twitter, Facebook and even Github.

You do not need to code your own login/signup, everything is handle hassle free by using the right function from Firebase.

After that you will have the login information like so (this is from email login but Facebook, Google, Twitter and Github is available):

enter image description here

You can then link either the identifier or the User UID to the information in your database.

enter image description here

Oh and last but not least, do remember to set the persistence setting for the login provider to keep user login after they close their apps.

Vishinsky answered 8/10, 2019 at 8:22 Comment(0)
C
5

Your proposed solution is good enough. The returned id is unique for your app.

Use the id returned to your app as the identifier for users in your app.

Checkout this answer on unique id

Just leaving the social_login plugin here

// Import package
import 'package:social_login/social_login.dart';

// Instantiate it
 final socialLogin = SocialLogin();

//Before calling any methods, set the configuration
socialLogin.setConfig(SocialConfig(
      facebookAppId: FACEBOOK_APP_ID,
      googleWebClientId: GOOGLE_WEB_CLIENT_ID, /*In case a Google tokenId is needed*/
      twitterConsumer: TWITTER_CONSUMER_KEY,
      twitterSecret: TWITTER_CONSUMER_SECRET,
    ));

// Get current logged user
 final FacebookUser facebookUser = await socialLogin.getCurrentFacebookUser();
 final GoogleUser googleUser = await socialLogin.getCurrentGoogleUser();
 final TwitterUser twitterUser = await socialLogin.getCurrentTwitterUser();

//Log in social networks
 final FacebookUser facebookUser = await socialLogin.logInFacebookWithPermissions(FacebookPermissions.DEFAULT);
 final GoogleUser googleUser = await socialLogin.logInGoogle();
 final TwitterUser twitterUser = await socialLogin.logInTwitter();

//Log out from social networks
 await socialLogin.logOutFacebook();
 await socialLogin.logOutGoogle();
 await socialLogin.logOutTwitter();
Clue answered 7/10, 2019 at 8:1 Comment(0)
V
3

By using the built-in sign in using Firebase Authentication SDK (Official Guide), you will be able to obtain login information for different platforms like Google, Twitter, Facebook and even Github.

You do not need to code your own login/signup, everything is handle hassle free by using the right function from Firebase.

After that you will have the login information like so (this is from email login but Facebook, Google, Twitter and Github is available):

enter image description here

You can then link either the identifier or the User UID to the information in your database.

enter image description here

Oh and last but not least, do remember to set the persistence setting for the login provider to keep user login after they close their apps.

Vishinsky answered 8/10, 2019 at 8:22 Comment(0)
J
2

You should split Flutter app into 2 parts:

  1. The "main" part of the app. It shows user related data.
  2. Sign in/Sign up part.

The logic is quite simple. On the app start you check if the user is authenticated. If yes, navigate him to the main screen. If not, present him a sign up screen. Here's how it can be achieved using Firebase as a backend:

final currentUser = await FirebaseAuth.instance.currentUser();
if (currentUser != null){
  // We're good: the user is authenticated.
  // Show him the main screen.
  Navigator.of(context).pushReplacement(MaterialPageRoute(
    builder: (context) => HomeScreen()
  ));
} else {
  // The user is not logged in.
  // Show him the sign in/sign up screen.
  Navigator.of(context).pushReplacement(MaterialPageRoute(
    builder: (context) => SignInScreen()
  ));
}
January answered 1/10, 2019 at 14:5 Comment(2)
If I understand the things wrongly, please advice me, I need the external login provider only such as Google or Facebook, I mean i have no signup for and in built user login in my application.Corves
The external auth provider (Google, Facebook etc.) is not required. But you will definitely need a backendJanuary
S
2

Maybe this example meet your needs:

https://github.com/instaflutter/flutter-login-screen-firebase-auth-facebook-login

If you need some help to understand, i can help you!

Steersman answered 2/10, 2019 at 16:13 Comment(0)
T
2

you should go with the token logic (which is used by google and facebook).

you should generate a token to each user and store it in your data base . through this token you can mange every thing about the user . when you call facebook authtinicating it will give you back a token for specific user . this token when you send it again to facebook or google they will respond back with the details of the user . so you can work like them .. create a token for the user and recall it for the info from your data base .. and you can store the token responded from facebook and match it in the future .. the purpose of my answer to go and search for token concept cause it will give you a lot of benefits .

Twoseater answered 5/10, 2019 at 11:48 Comment(2)
is there any user size limitation in JWT, i mean if we have a flutter application with 1 million users, can i manage this user in this JWT mechanism in realtime, actually i am creating a mobile application for a existing website that have 5 lakh users so i am aware to number of new user that are incrementing day by day.Corves
chick this #26034483Twoseater
A
1

The idea is simple. The users get logged in with facebook/google/instagram and get an accessToken from the services mentioned above. This token has to be sent with each request that need authentication.

Use flutter storage to save the persist the token: https://pub.dev/packages/flutter_secure_storage

I will post you an example of one of my flutter app where I implemented the login with instagram.

Resuming the code below:

  • loginService.dart is responsible for login api calls and saving the token into the storage
  • main.dart is the first view of the app. Here if the user is not logged in the Login widget is shown ( the LoginWidget is in login.dart ).
  • login.dart is the view of the login page. There is a button that calls functions from LoginService class

Here's the code:

~/lib/service/loginService.dart

import 'dart:async';
import 'dart:convert' as JSON;
import 'package:http/http.dart' as http;
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:choose/model/User.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:choose/config.dart' as config;

String apiBaseUrl = config.apiBase;
String apiPort = config.port;

class LoginService {

  String clientID;
  String secretID;
  String redirectURI;
  final FlutterSecureStorage storage = new FlutterSecureStorage();
  final String authKey = "token";

  LoginService(this.clientID, this.secretID, this.redirectURI);

  String get authenticationURI {
    return "https://api.instagram.com/oauth/authorize/?client_id=$clientID&redirect_uri=$redirectURI&response_type=code";
  }

  String get authorizationURI {
    return "https://api.instagram.com/oauth/access_token";
  }

  Future<User> authenticate() async {
    clean();
    FlutterWebviewPlugin _flutterWebviewPlugin = FlutterWebviewPlugin();

    // Stream<String> onCode = await _openServer();
    _flutterWebviewPlugin.launch(
        authenticationURI
    );

    Completer<User> loginCompleter = Completer();

    _flutterWebviewPlugin.onStateChanged.listen((viewState) async {
      String url = viewState.url;
      int indexOfCode = url.lastIndexOf("?code=");

      if(url != null && indexOfCode >= 0 && viewState.type == WebViewState.finishLoad) {
        String code = url.substring(url.indexOf('?code=') + 6, url.length);

        http.Response userResponse = await http.get(
          "${config.endpoints['getUserByCode']}?code=$code",
        );
        if(userResponse.statusCode == 200) {
          User user = User.fromMap(JSON.jsonDecode(userResponse.body));
          saveToken(user.token);
          loginCompleter.complete(user);
        } else {
          loginCompleter.completeError("User not found");
        }
        _flutterWebviewPlugin.close();
      }
    });



    return loginCompleter.future;
  }

  void saveToken(token) async {
    storage.write(key: authKey, value: token);
  }

  void clean() async {
    storage.delete(key: authKey);
  }

  Future<String> getToken() async {
    return storage.read(key: authKey);
  }

  static Future<String> getStaticToken() async {
    FlutterSecureStorage storage = FlutterSecureStorage();
    return storage.read(key: "token");
  }

}

~lib/main.dart

import 'package:choose/friends.dart';
import 'package:choose/model/User.dart';
import 'package:flutter/material.dart';
import 'package:choose/login.dart';
import 'package:choose/service/loginService.dart';
import 'package:web_socket_channel/io.dart';

import 'feed.dart';
import 'notification.dart';
import 'newPost.dart';
import 'config.dart' as config;

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

LoginService loginService = new LoginService(
  "71b818abd18043fb8b7c1833912b62ae",
  "3d9d6f87d2354085a74534c13bdda51f",
  config.endpoints['authorize'],
);



class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
      routes: {
        '/feed': (context) => DefaultTabController(
          length: 2,
          child: FeedWidget()
        ),
        '/newpost': (context) => NewPost(),
        '/notifications': (context) => NotificationWidget(
          channel: IOWebSocketChannel.connect(config.websocketURI)
        ),
        '/friends': (context) => FriendsWidget(),
      }

    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  User user;
  String label = 'Continue with Instagram';

  void openLoginPage() async {

    try {
      User _user = await loginService.authenticate();
      this.openPostPage();
    } catch(e) {
      this.setState(() {
        label = 'Try Again';
      });
    }

  }

  void openPostPage() async {
    print(await loginService.getToken());
    print("token");

    Navigator.pushReplacementNamed(context, '/feed');
  }

  void actionDelegator() {
    if(user != null) {
      return openPostPage();
    } else {
      return openLoginPage();
    }
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Login(loginAction: actionDelegator, user: user, label: label);
  }
}

~/lib/service/login.dart

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:choose/model/User.dart';

class Login extends StatefulWidget {

  Login({this.loginAction, this.user, this.label});

  final loginAction;
  final User user;
  final String label;

  _Login createState() => _Login();

}

class _Login extends State<Login> {

  String imageURI = "https://www.travelcontinuously.com/wp-content/uploads/2018/04/empty-avatar.png";

  @override
  Widget build(BuildContext context) {

    if(widget.user != null && widget.user.profilePicture != '' ) {
      imageURI = widget.user.profilePicture;
    }

    return Container (
      color: Colors.green,
      child: Column (
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          CircleAvatar (
            backgroundColor: Colors.transparent,
            radius: 50.0,
            backgroundImage: NetworkImage(imageURI, scale: 2.0),
          ),

          Container(
            padding: EdgeInsets.all(0.0),
            margin: EdgeInsets.all(20.0),
            color: Colors.transparent,
            child: MaterialButton(
              elevation: 5.0,
              color: Color.fromRGBO(193, 53, 132, 1.0),
              child: FlatButton.icon(
                label: Text(widget.label, style: TextStyle(color: Color.fromRGBO(255,220,128, 1.0), fontWeight: FontWeight.w900, fontSize: 16.0)),
                icon: Icon(FontAwesomeIcons.instagram, color: Color.fromRGBO(255,220,128, 1.0)),
              ),
              onPressed: () async {
                widget.loginAction();
              },
            )
          )
        ],
      ),
    );
  }

}
Affirmative answered 8/10, 2019 at 12:21 Comment(0)
T
0

(Someone might find this helpful). For easy oauth in flutter, Try visa - https://github.com/e-oj/visa

Here's an example with Facebook auth (It also supports google, twitch, discord, and Github):

import 'package:visa/auth-data.dart';
import 'package:visa/fb.dart';

class AuthPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      /// Simply Provide all the necessary credentials
      body: FaceBookAuth().visa.authenticate(
          clientID: '139732240983759',
          redirectUri: 'https://www.e-oj.com/oauth',
          scope: 'public_profile,email',
          state: 'fbAuth',
          onDone: done
      )
    );
  }
}

and the "done" callback:

done(AuthData authData){
  print(authData);

  /// You can pass the [AuthData] object to a 
  /// post-authentication screen. It contaions 
  /// all the user and OAuth data collected during
  /// the authentication process. In this example,
  /// our post-authentication screen is "complete-profile".
  Navigator.pushReplacementNamed(
      context, '/complete-profile', arguments: authData
  );
}
Theis answered 1/2, 2021 at 4:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.