Twilio Plugin - Keeping a user logged in

Good day. For the Twilio plugin, is there a way to keep the user logged in like the “stayLoggedIn” parameter we could set in the Login API: Backendless Login API - Backendless SDK for Flutter API Documentation ?

I am struggling to get the API docs of the deployed Twilio service. What does checkVerificationCode do? Or is it just something that gets called as part of registration and authentication of a User? Registering a User and Login a user works perfect with Twilio.

I see in your Flutter API, when we call the login() function of the UserService, it provides the “stayLoggedIn” parameter that gets sent to the Backendless.UserService.login server side function.

Is there something similar for the Twilio plugin? If not, how could I try and implement it by myself?

At this stage it will get costly to send an sms as verification every time someone opens the app. Even in testing it. I can bypass authentication but will probably then not be able to access tables. I can maybe just use normal authentication with username and password until there is an option to keep the user logged in.

Regards

Hello @johan-jurrius

Thanks for your question!

When you log in using the Twilio plugin you should receive a user from the API Service that contains a user-token property, and then you assign the user to the Flutter SDK, is that correct?

So, the stayLoggedIn option is just for storing the toking on the client-side and after return the token will be used by the SDK

how do you assign the user to the SDK? like this way:

Backendless.userService.setUserToken(userToken)

or

Backendless.userService.setCurrentUser(user)

Regards, Vlad

I am using the Twilio SDK function loginWithPhoneNumberAndPassword. From that function I get the transactionId property and the sms arrives at the phone. I use a dialog to get the sms code from the user. I then run the Twilio SDK function called loginWithCode, sending the transactionId and code I received from the user using the app. loginWithCode returns the User object as json. I then use BackendlessUser.fromJson() to get the user logged in. This is how it is explained in the doc: Backendless Login API - Backendless SDK for Flutter API Documentation . Am I doing something wrong?

With normal Flutter SDK, I just call Backendless.UserService.login(username, password, true), The true value keeps the user logged in. If I then want to check whether the user is authenticated when the app restarts, I first call Backendless.UserService.loggedInUser(). If the objectId returned is not null, I call Backendless.data.of(“Users”).findById(objectId). If that is also not null I know the user can be taken to the first screen.

I assume I need to assign the User to the SDK, which I never used before. Please guide me on the correct way to do this.

Thank you

you can try both methods I mentioned above

So after getting the authenticated User Object from loginWithCode(), I can then save the objectId of the user using SharedPreferences physically on the phone. Next time the app opens, I read the value from SharedPreferences, call Backendless.data.of(’‘Users’).findById(objectId) to get the logged in user. Then call Backendless.UserService.setCurrentUser(user)? Should I not check if the token has not expired online?

Also, if I have a custom security role on the Users table, will I be able to call Backendless.data.of(‘Users’)… at all if not authenticated?

So after getting the authenticated User Object from loginWithCode(), I can then save the objectId of the user using SharedPreferences physically on the phone. Next time the app opens, I read the value from SharedPreferences, call Backendless.data.of(’‘Users’).findById(objectId) to get the logged in user. Then call Backendless.UserService.setCurrentUser(user)? Should I not check if the token has not expired online?

Actually, you do not need to handle it on your own, the token and the user has to be stored locally by the SDK. Have you tried to use:

Backendless.userService.setCurrentUser(user)

Also, if I have a custom security role on the Users table, will I be able to call Backendless.data.of(‘Users’)… at all if not authenticated?

In order to become an authenticated user, you need to set up your current user token

Backendless.userService.setUserToken(userToken)

Ok, so what you are saying is that after I authenticate the user with loginWithCode(), I can get the User with Backendless.data.of(‘Users’).findById(objectId). I then call Backendless.userService.setCurrentUser(user). When I close the app again, how do I keep the User logged in then so he does not need to login again and get an sms with a code?

Almost, you do not need to load the user by using Backendless.data.of(‘Users’).findById(objectId) because the loginWithCode should return the one.

Oh yes, thats what I meant. I did that as you mentioned. Call loginWithPhoneNumberAndPassword(phone, password); This gives me the transactionId. I get the code via sms and then call loginWithCode(transactionId, code); This gets me the User object as JSON. I call BackendlessUser.fromJson(json) to get the User object and then call Backendless.userService.setCurrentUser(user).

So how do I now keep the User logged in so he/she does not need to login every time?

This is what I do in the Loading screen of the app:
Currently I call Backendless.userService.isValidLogin(). If the value returned is true, I get the current loggedInUser by calling Backendless.userService.loggedInUser(). That gives me the objectId of the User. Then I call Backendless.data.of(‘Users’).findById(objectId) to get the current user that is logged in. This gives me a Map of the Current User. If that map is not null I take the user to the first screen. If anything previously failed, I take the User to the Login screen. But this does not work. I always end up at the Login screen.

So how do I now keep the User logged in so he/she does not need to login every time?

Have you tried to stop/run the app?

…This gives me a Map of the Current User. If that map is not null I take the user to the first screen. If anything previously failed, I take the User to the Login screen. But this does not work. I always end up at the Login screen.

I assume it’s more related to UI issues, I can recommend you to debug your code and check why it stays on the login screen

This is my whole function:

Future<String> checkIfUserLoggedIn() async {
    String result = 'OK';

    bool? validLogin = await Backendless.userService
        .isValidLogin()
        .onError((error, stackTrace) {
      result = error.toString();
    });

    if (validLogin != null && validLogin) {
      String? currentUserObjectId = await Backendless.userService
          .loggedInUser()
          .onError((error, stackTrace) {
        result = error.toString();
      });
      if (currentUserObjectId != null) {
        Map<dynamic, dynamic>? mapOfCurrentUser = await Backendless.data
            .of("Users")
            .findById(currentUserObjectId)
            .onError((error, stackTrace) {
          result = error.toString();
        });
        if (mapOfCurrentUser != null) {
          _currentUser = BackendlessUser.fromJson(mapOfCurrentUser);
          notifyListeners();
        } else {
          result = 'MAP IS NULL';
        }
      } else {
        result = 'USER OBJECT ID IS NULL';
      }
    } else {
      result = 'NOT VALID LOGIN';
    }

    return result;
  }

It returns MAP IS NULL…narrowed it down…validLogin is true but currentUserObjectId is empty.

sorry but according to our support policy we do not debug customer’s code, could you please minimize your sample to show problem with the Backendless API

If I login a user, my app takes me to the first screen successfully.

If I run the app again…

This code:

bool? validLogin = await Backendless.userService
        .isValidLogin()
        .onError((error, stackTrace) {
      result = error.toString();
    });

returns true, 100% as it should.

If I login the user again, and use the logout call, and run the app again, the function above returns false. Again 100% correct.

My problem comes in at the next step.

This code then:

String? currentUserObjectId = await Backendless.userService
          .loggedInUser()
          .onError((error, stackTrace) {
        result = error.toString();
      });

returns an empty currentUserObjectId if isValidLogin() is true. That is my problem. And it does not go into the error section. Just returns an empty value. I have used this exact function many times before using the normal Flutter SDK that uses the true value to stay logged in. Twilio SDK is not keeping the user logged in or something. But, still seems it picks up there is a valid login. Just cannot get the one logged in?

Ok, I see

I’ve created an internal ticket BKNDLSS-27495 to investigate this case, our Flutter engineer will take a look as soon as it only possible

Thank you. I have tried both:
Backendless.userService.setCurrentUser(user)
and
Backendless.userService.setUserToken(userToken)

and both gives the same value of validLogin as true, but returns empty currentUserObjectId (in my function)

Future loginUser(String phone, String password,
      TextEditingController codeController) async {
    String result = 'OK';
    FocusManager.instance.primaryFocus?.unfocus();
    _showUserProgress = true;
    _userProgressText = 'Busy logging you in...please wait...';
    notifyListeners();
    phone = phone.replaceRange(0, 1, '+27');
    print(phone);
    Twilio.loginWithPhoneNumberAndPassword(phone, password).then((value) async {
      String transactionId = value["transactionId"];
      String? code = await showVerificationDialog(codeController);
      if (code != null && code.compareTo('CANCEL') != 0) {
        Twilio.loginWithCode(transactionId, code).then((value) {
          if (value != null) {
            print(value);
            _currentUser = BackendlessUser.fromJson(value);
            //Backendless.userService.setCurrentUser(_currentUser!);
            print(value['user-token']);
            Backendless.userService.setUserToken(value['user-token']);
          }
          _showUserProgress = false;
          notifyListeners();
          Get.off(() => const HomeAdmin());
          showSnackBar(
              title: 'User Authentication',
              message:
                  '${_currentUser == null ? 'User' : _currentUser!.email} successfully authenticated!');
        }).onError((error, stackTrace) {
          print(getHumanReadableError(error.toString()));
          showSnackBar(title: 'User Authentication', message: error.toString());
          _showUserProgress = false;
          notifyListeners();
        });
      } else {
        showSnackBar(
            title: 'Authenticate User',
            message: 'Sorry, could not verify your phone number.');
        _showUserProgress = false;
        notifyListeners();
      }
    }).onError((error, stackTrace) {
      print(getHumanReadableError(error.toString()));
      showSnackBar(title: 'Here Authenticate User', message: error.toString());
      _showUserProgress = false;
      notifyListeners();
    });
  }

My login function. Like I said, login works perfectly, but the User does not stay logged in when using the normal Flutter SDK to check.

yeah, seems like the issue is on our side in the FlutterSDK

Thank you

for now, as a workaround, I can propose you use your solution after login keep the token in the storage on your device and on app start set the value to the SDK

Will try that thanks. Also, to be clear, the Flutter SDK works 100% as it is. The Twilio Flutter SDK needs tweaking. So I was using the Twilio Flutter SDK to login a user but the normal Flutter SDK to check the login. Without Twilio, normal login and keeping a user logged in works 100%. Would be nice to just set a stayLoggedIn field as part of one of the login functions of the Twilio Flutter SDK. Also, the Twilio deployed service under Cloud Code does not have that option either. So it could be wider than Flutter? Otherwise I could just invoke the cloud code?

Regards