Building OTP login in react-native with auto read from SMS
We at Goibibo always strive to deliver best user experience to our end users. I am the part of InGo-MMT team which is a B2B platform where end users are hoteliers. We have a desktop platform (Extranet) as well as mobile app for our hotel partners where they can manage their bookings, rates & inventory, promotions etc. One crucial thing we were missing in our app was login with OTP. Looking at our analytics data of login failure events and forgot password clicks, we knew that this has been a pain point for most of the hoteliers. On an average, around 200 hoteliers were daily visiting Forgot Password screen. We decided to implement OTP login in our app (and the best part was tech team took this initiative, thanks to Om) and I got chance to work on its Frontend.

We divided this feature into 3 modules - Frontend, Backend and Caching layer (redis to store and track OTPs). Implementing Frontend was straight forward which included consuming token based authentication APIs, calling APIs for generating and verifying OTP etc but still we had few technical challenges while building OTP Verification screen in react-native and this is where things got interesting for me :D

On successful generation of OTP, next screen was Verification which had 4 text-input boxes for 4 digits OTP, a resend-OTP link and a submit OTP button. For this particular screen we had to build the following features-
- Auto focusing of TextInput boxes (auto focus to next TextInput box on entering an OTP digit)
- Timer for Resend OTP link (a resend OTP link which would be visible after 30 secs so we had to show a 30 secs timer)
- Clearing TextInput boxes in reverse order on pressing Backspace (auto clearing of previous TextInput boxes on pressing of Backspace key)
- Auto read OTP from SMS
- Auto submission of OTP (within 3 secs of OTP detection from SMS)
Let’s render some UI
UI was pretty much straight forward having four TextInput boxes and one submit button at the bottom.

Auto focusing of TextInput boxes
While entering OTP, no one wants to type a digit, manually click on next input box, type 2nd digit and so on. Hence this was the very basic and very crucial step for a good UX.
To programmatically focus the next TextInput box while entering OTP digits, I assigned one reference to each TextInput box using useRef hook and handled the behaviour in onChangeText callback.
// TextInput refs to focus programmatically while entering OTP
const firstTextInputRef = useRef(null);
const secondTextInputRef = useRef(null);
const thirdTextInputRef = useRef(null);
const fourthTextInputRef = useRef(null);
const onOtpChange = index => {
return value => {
if (isNaN(Number(value))) {
// do nothing when a non digit is pressed
return;
}
const otpArrayCopy = otpArray.concat();
otpArrayCopy[index] = value;
setOtpArray(otpArrayCopy);
// auto focus to next InputText if value is not blank
if (value !== '') {
if (index === 0) {
secondTextInputRef.current.focus();
} else if (index === 1) {
thirdTextInputRef.current.focus();
} else if (index === 2) {
fourthTextInputRef.current.focus();
}
}
};
};

Timer for Resend OTP link
Users may or may not receive OTP instantly. There can be numerous reasons for this e.g. network provider problem so it is important to provide user a Resend OTP option. We decided to show this link after 30 secs and to keep user engaged we had to show a timer.

To implement this timer I used a state variable resendButtonDisabledTime
// in secs, if value is greater than 0 then button will be disabled
const [resendButtonDisabledTime, setResendButtonDisabledTime] = useState(
RESEND_OTP_TIME_LIMIT,
);

Next I defined a function startResendOtpTimer which keeps decrementing value of resendButtonDisabledTime.
const startResendOtpTimer = () => {
if (resendOtpTimerInterval) {
clearInterval(resendOtpTimerInterval);
}
resendOtpTimerInterval = setInterval(() => {
if (resendButtonDisabledTime <= 0) {
clearInterval(resendOtpTimerInterval);
} else {
setResendButtonDisabledTime(resendButtonDisabledTime - 1);
}
}, 1000);
};
I called this function from componentDidUpdate
useEffect(() => {
startResendOtpTimer();
return () => {
if (resendOtpTimerInterval) {
clearInterval(resendOtpTimerInterval);
}
};
}, [resendButtonDisabledTime]);
Clearing TextInput boxes in reverse order on pressing Backspace
Since we are automatically focusing on next input box while entering OTP, it also make sense to clear previous OTP digits on pressing Backspace, besides it is also a good user experience. In react-native onChangeTextevent in TextInput will be fired only when there is an actual change in text, which means that it won’t be triggered on pressing BackSpace if text is already blank. This was the first challenge that I faced while implementing OTP Login. To achieve this functionality I had to register a listener on onKeyPress — onKeyPress={onOtpKeyPress(index)}

One important thing to note here that this onKeyPress on Android will only work for soft keyboard.
Auto read OTP from SMS
This is a feature that is not critical for OTP Login but gives a wow effect if implemented. Initially we thought that we’ll have to read device messages for it which means we’ll have to ask for android.permission.READ_SMS permission from user which is a sensitive information. Thankfully SMS Retriever API exists in Android which expects SMS message to have a particular format and in return Google Play services will forward this message to our app. OTP Message must contain app’s hash for this feature to work, something like this-
<#> Dear User,
1091 is your OTP for logging into Ingo-MMT. (Remaining Time: 10 minutes and 0 seconds)
uTT+hcwZdg9
I used react-native-otp-verify package for this which internally uses Google SMS Retriver API.

Auto submission of OTP
The last piece of this feature was to automatically submit OTP after 3 secs once OTP was read successfully from SMS. Similar to resend OTP link, I had to show a timer for 3 secs to keep user engaged. This was the most challenging part because I had to deal here the caching of variables in React hooks due to closure.
I used a state variable autoSubmitOtpTime for this.
{autoSubmitOtpTime > 0 &&
autoSubmitOtpTime < AUTO_SUBMIT_OTP_TIME_LIMIT ? (
<TimerText text={'Submitting OTP in'} time={autoSubmitOtpTime} />
) : null}
I also defined a function startAutoSubmitOtpTimer to show timer which I called once OTP was detected successfully.
const startAutoSubmitOtpTimer = () => {
if (autoSubmitOtpTimerInterval) {
clearInterval(autoSubmitOtpTimerInterval);
}
autoSubmitOtpTimerInterval = setInterval(() => {
autoSubmitOtpTimerIntervalCallbackReference.current();
}, 1000);
};
One catch here is that I am using a reference autoSubmitOtpTimerIntervalCallbackReference callback created from useRef hook inside setInterval function because I required updated value of autoSubmitOtpTime state variable. Also I had to update reference variable in componentDidUpdate-

Conclusion

OTP Login is a must have feature for apps. Currently we rolled out this feature to android and mweb (powered by react-native-web) while ios release is pending. Within a week of this release, we saw a 50% reduction in no of hoteliers visiting Forgot Password screen. We are sure that this feature will further boost adoption of our app.

Source code for this feature is available at
https://github.com/varunon9/react-native-otp-verification.
Building OTP verification component in react-native with auto read from SMS was originally published in Backstage on Medium, where people are continuing the conversation by highlighting and responding to this story.