DEV Community

Cover image for Passwordless login for React Native apps
Dmitry Amelchenko
Dmitry Amelchenko

Posted on • Updated on

Passwordless login for React Native apps

Little bit of a history

I've been a big proponent of anonymous user experience on the web. It's simple and bullet proof secure -- if you don't collect any user identity, you can't ever leak it to the bad guys. And most of the times, to deliver awesome user experience, you don't need to ask users to go through complexity of registering and then logging into the system. To prove the point I've built a completely anonymous Social Sharing App called WiSaw (What I Saw). Read more about it here --> https://www.echowaves.com/

Download the app and start sharing -- does not get simpler than that.

Eventually, I realized, that, the users, while caring about protecting their identity from the world, still want to expose it to friends they trust -- it just makes their experience a bit more interesting and engaging. So, I came up with the concept of "Peer For Peer (P4P) Identity Management in Social Networks". You can read more about it here --> https://dev.to/dmitryame/p4p-peer-4-peer-identity-management-in-social-networks-1c55

While this approach works fine for the simple case, I have to admit -- most of modern applications will require more reliable way of identifying a user. It all comes from common sense, and regulations, which require you to build reliable systems of blocking abusive behavior on the web. The app stores (iTunes, as well as Google Play) will simply not approve your application, unless you build features for reporting and blocking abusive users.

In the earlier days of mobile apps, you could obtain mobile phone number automagically from the device, and use it as a reliable identifier for all user interactions. These days are long go -- you have to ask the users about their identity explicitly, and you also must verify their identity in the app, to make sure they are not lying.

What are we building

In this exercise, we will build a user identity system, which will reliably identify the user, while not requiring typing any phone number, password or OTP (One Time Password), security or activation code.

Warning: this system will be sufficient compromise solution for applications which don't deal with user financial assets, or medical health records. For more advanced use cases the users will not mind providing email address, phone number, as well as secure password, perhaps even some secure questions in order to protect their Personal Data via 2 factor authentication.

The application we are building is called PlaceChatter https://www.echowaves.com/placechatter

It allows a small restaurant owners to interact with their customers in public chat rooms in real time.

It also offers a loyalty program, which incentives customers to return for a reward.

Needless to say, the restaurant owners, while not caring much about collecting Personal Identifiable Information (PII), will want a reliable way to ban abusive users.

At the same time, getting the users to download and install yet one more application on their device is a challenge of it's own. If the user has to go through setting up 2 factor authentication, coming up with secure password, even having to type their phone number in order to register, it will be a show stopper for most of users -- they will abandon this app and will never return again.

What we are building requires no typing for users at all -- it will rely on auto substitution features of modern mobile platforms (iOS, Android). Unfortunately, there is still no well established standard that works consistently. I've experimented with a lot of different options and settings -- finally found the best combination which works. Hope this post will save you some grief and time.

Let's start building.

The code

Here is the stack we are using in this example:

  • expo "47.0.0", managed workflow, but it should work for non managed React Native apps as well.
  • For buttons and input fields we use React Native Elements "4.0.0-rc.7". The <Input> from React Native Elements is fully compatible with the ReactNative's <TextInput> -- it uses the same props.
  • The backend is built on AWS Lambdas.

Ask user to submit phone number

On this screen we ask the user to provide their mobile phone number.

Ask user to submit phone number

As you can see, on the top of the keyboard there are 2 suggestions added by iOS (it will work similarly on Android).

All the user has to do -- tap on one of the suggestions, and it will substitute the number into the text input field on the screen.

auto substitute phone number

Here is code for the Input field: https://github.com/echowaves/placechatter/blob/856a40bf72c20643843147fd6699d15f9f0b1628/src/screens/PhoneCheck/index.js#L164

          <Input
            ref={input}
            label="Confirmation Code will be sent to this number"
            placeholder="your mobile phone number"
            leftIcon={{ type: 'font-awesome', name: 'mobile-phone' }}
            autoFocus={true}
            keyboardType="numeric"
            value={phoneNumber}
            errorStyle={{ color: 'red' }}
            errorMessage={phoneNumberError}
            // autoComplete="tel"
            textContentType="telephoneNumber"
            onChangeText={(value) => {
              setPhoneNumber(VALID.phoneNumberInput(value)) // filter non numbers
            }}
            onFocus={() => {
              valid()
            }}
          />
Enter fullscreen mode Exit fullscreen mode

I tried to experiment with various values for "autoComplete" prop, but nothing worked (that's why it's commented out in the final code).

Setting "textContentType" prop to "telephoneNumber" value is all you need to do. It works the same for iOS and Android.

In this example the input field is expected to be 10 digits, no special characters like "+" or "-", no alphabet characters, no preceding "1".

The data passed from the auto substitute most likely will not correspond to these expectations, as such "onChangeText" is invoking a function, which is doing data cleansing before entering it into the input field.

The implementation of that function looks like this: https://github.com/echowaves/placechatter/blob/856a40bf72c20643843147fd6699d15f9f0b1628/src/valid.js#L16

return 
    value.replace(/\D/g, '').replace(/^1/, '').slice(0, 10)
Enter fullscreen mode Exit fullscreen mode

The order of calls chaining makes a difference. First regex leaves only the digits, second call -- removes preceding "1" (we don't need country code, we are only supporting USA for now), and third -- takes only the first 10 characters.

In case something didn't work automagically as expected after auto substitution, the user still can make corrections to the field manually. Once the phone is entered, clicking on the "Submit" button will send the phone number to the backend and the request will be handled by AWS Lambda. https://github.com/echowaves/placechatter.cdk/blob/main/lambda-fns/controllers/phones/activationCodeGenerate.ts

Our Lambda function will generate One Time Password, and will send the OTP back to the submitted phone number via SMS.

On the next screen we will have to scrape the OTP from the SMS message received and insert it into the input field.

The format of the SMS message is crucial to make the auto-substitute work.

I initially experimented with 4 alpha numeric case sensitive characters. My thinking was that in case the auto-substitution fails, it will still be easy for the user to type the code manually. And, since 4 alpha-numeric characters give us 64^4 combinations, which is 16,777,216, it's a pretty hard to guess password.

iOS has new feature, which will parse the incoming message and will try to guess the OTP from its contents. It will be looking through the message for close proximity to some key words like "code", "security code", "password" etc... The only problem is that it will not recognize the alpha-numeric combination of characters as the code, which can be auto-substituted. It works with codes that are digits only .

As such, I'm using 10 digits and my message looks like this:

sms message

Yes, 10 digits code is not as easy for the user to type, but, the auto-substitute feature works very reliably on iOS and Android, so, hopefully, the user never has to type anything manually, and it gives us 10^10 (10,000,000,000) combinations, which is even stronger than 4 alpha numerics.

Here is how we generate the code on the backend: https://github.com/echowaves/placechatter.cdk/blob/628f80061a2e8d09c1152f683da02fb1f3fdb634/lambda-fns/controllers/phones/activationCodeGenerate.ts#L22

let smsCode = Math.random().toString().substring(2, 12)
Enter fullscreen mode Exit fullscreen mode

It will generate 10 digits long string, keeping preceding 0's.

And here is how it looks on the screen:

auto substitute code from SMS

The same idea -- tap the suggestion on top of the keyboard, and it will auto-substitute the value in the input field. Works equally well on iOS and Android.

The code for the <Input> field looks like the following: https://github.com/echowaves/placechatter/blob/856a40bf72c20643843147fd6699d15f9f0b1628/src/screens/PhoneCheck/SmsConfirm.js#L233

<Input
            label="Enter sms confirmation code"
            placeholder="10 letter code"
            leftIcon={{ type: 'material-icons', name: 'confirmation-num' }}
            autoFocus={true}
            keyboardType="numeric"
            value={smsCode}
            onChangeText={(value) =>
              setSmsCode(value.replace(/\D/g, '').slice(0, 10))
            }
            autoCapitalize="none"
            autoComplete={Platform.OS === 'ios' ? 'one-time-code' : 'sms-otp'}
            autoCorrect={false}
            onFocus={() => {
              valid()
            }}
          />
Enter fullscreen mode Exit fullscreen mode

Important points to make it work:

keyboardType="numeric"
Enter fullscreen mode Exit fullscreen mode

If the keyboardType is not specified -- it didn't work for me.

The next line is also very important:

autoComplete={Platform.OS === 'ios' ? 'one-time-code' : 'sms-otp'}
Enter fullscreen mode Exit fullscreen mode

Unfortunately, this seems to be the case where it does not work consistently for both platforms. Unless this conditional logic is added, 'one-time-code' value crashes Android, and 'sms-otp' does not work on iOS.

And that's all. Let me know in comments if I missed anything. Happy auto-substitution.

The complete source code for the mobile app: https://github.com/echowaves/placechatter
And for the backend: https://github.com/echowaves/placechatter.cdk


This article was originally posted here --> https://www.echowaves.com/post/minimalistic-login-for-react-native-applications

Top comments (0)