Automatic SMS OTP verification for React Native Android apps using Google's SMS Retriever and User Consent APIs β zero runtime SMS permissions required.
Key points: Zero permissions β’ Google Play approved β’ Easy to integrate β’ TypeScript support
- Features
- Installation
- Quick Start
- API Reference
- Examples
- Backend integration
- Troubleshooting
- Contributing
- License
- π Two verification methods: Automatic (silent) and Consent-based (with user approval)
- π« Zero permissions: No
READ_SMSorRECEIVE_SMSpermissions needed - β Google Play approved: Uses official Google Play Services APIs
- π― Easy to use: Simple API with TypeScript support
- π± Cross-platform support: Android (fully functional) and iOS (graceful error handling)
- π Event-driven: Listen for SMS events with modern EventEmitter pattern
- π οΈ Built with New Architecture: Supports React Native's new architecture (TurboModules)
- π Thread-Safe: Concurrent-safe receiver management with locks and atomic operations
- π Enhanced Logging: Comprehensive debug logging for easier troubleshooting
npm install @pushpendersingh/react-native-otp-verifyor
yarn add @pushpendersingh/react-native-otp-verify- React Native >= 0.76
- Android API Level >= 24
- Google Play Services
- Expo prebuild is supported.
import {
startSmsRetriever,
addSmsListener,
getAppSignature,
extractOtp,
} from '@pushpendersingh/react-native-otp-verify';
// Get app signature (needed for SMS format)
const signature = await getAppSignature();
console.log('App Signature:', signature);
// Start listening for SMS
await startSmsRetriever();
// Add listener for incoming SMS
const removeListener = addSmsListener((message) => {
if (message.status === 'success' && message.message) {
const otp = extractOtp(message.message);
console.log('OTP:', otp);
}
});
// Clean up when done
removeListener();This library provides two different approaches for SMS OTP verification:
| Method | User Action | SMS Format | Use Case |
|---|---|---|---|
| SMS Retriever | None (Automatic) | Requires app hash | Best for your own backend |
| SMS User Consent | User taps "Allow" | Any format | Works with any SMS sender |
Starts the SMS Retriever API to automatically capture SMS messages containing your app's signature.
- Returns:
Promise<string> - Throws: Error if failed to start
- Duration: Listens for up to 5 minutes
import { startSmsRetriever } from '@pushpendersingh/react-native-otp-verify';
try {
await startSmsRetriever();
console.log('SMS Retriever started');
} catch (error) {
console.error('Failed to start:', error);
}Gets your app's 11-character hash signature that must be included in SMS messages.
- Returns:
Promise<string>- The app signature (e.g.,FA+9qCX9VSu) - Throws: Error if signature cannot be generated
import { getAppSignature } from '@pushpendersingh/react-native-otp-verify';
try {
const signature = await getAppSignature();
console.log('App Signature:', signature);
// Send this signature to your backend
} catch (error) {
console.error('Failed to get signature:', error);
}Important: Send this signature to your backend so it can be included in SMS messages.
Adds a listener for incoming SMS events.
- Parameters:
callback: (message: SmsMessage) => void- Function called when SMS is received
- Returns:
() => void- Function to remove the listener
SmsMessage Interface:
interface SmsMessage {
message: string | null; // The full SMS message content
status: 'success' | 'timeout' | 'error';
senderAddress?: string; // Sender's phone number (if available)
}Example:
import { addSmsListener } from '@pushpendersingh/react-native-otp-verify';
const removeListener = addSmsListener((message) => {
console.log('SMS Status:', message.status);
if (message.status === 'success') {
console.log('SMS:', message.message);
console.log('From:', message.senderAddress);
} else if (message.status === 'timeout') {
console.log('SMS Retriever timed out after 5 minutes');
} else if (message.status === 'error') {
console.error('SMS Retriever error');
}
});
// Don't forget to clean up
removeListener();Removes the SMS receiver and stops listening for messages.
- Returns:
Promise<string>
import { removeSmsListener } from '@pushpendersingh/react-native-otp-verify';
try {
await removeSmsListener();
console.log('SMS Listener removed');
} catch (error) {
console.error('Failed to remove listener:', error);
}Extracts OTP code from SMS message using regex pattern.
- Parameters:
message: string- The SMS message textpattern?: RegExp- Custom regex pattern (optional)
- Returns:
string | null- The extracted OTP or null if not found - Default Pattern: Matches 4-8 digit codes
Example:
import { extractOtp } from '@pushpendersingh/react-native-otp-verify';
const sms = "Your OTP is: 123456\\n\\nFA+9qCX9VSu";
const otp = extractOtp(sms);
console.log('OTP:', otp); // "123456"
// Custom pattern for alphanumeric codes
const customPattern = /[A-Z0-9]{6}/;
const code = extractOtp("Your code: ABC123", customPattern);
console.log('Code:', code); // "ABC123"Starts the SMS User Consent API which shows a consent dialog when SMS arrives.
- Returns:
Promise<string> - Throws: Error if failed to start
How it works:
- Call this function to start listening
- When SMS arrives, Google shows a consent dialog
- User taps "Allow" or "Deny"
- SMS is delivered through
addSmsListenercallback
Example:
import { requestPhoneNumber, addSmsListener, extractOtp } from '@pushpendersingh/react-native-otp-verify';
// Add listener first
const removeListener = addSmsListener((message) => {
if (message.status === 'success' && message.message) {
console.log('User approved SMS:', message.message);
const otp = extractOtp(message.message);
console.log('OTP:', otp);
} else if (message.status === 'error') {
console.log('User denied consent');
}
});
// Start SMS User Consent
try {
await requestPhoneNumber();
console.log('Waiting for SMS... User will see consent dialog');
} catch (error) {
console.error('Failed to start consent:', error);
}
// Clean up
removeListener();import React, { useEffect, useState } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import {
startSmsRetriever,
addSmsListener,
getAppSignature,
extractOtp,
removeSmsListener,
} from '@pushpendersingh/react-native-otp-verify';
export default function OtpScreen() {
const [otp, setOtp] = useState('');
const [signature, setSignature] = useState('');
useEffect(() => {
// Get app signature on mount
getAppSignature().then(setSignature);
// Add SMS listener
const removeListener = addSmsListener((message) => {
if (message.status === 'success' && message.message) {
const code = extractOtp(message.message);
if (code) {
setOtp(code);
Alert.alert('OTP Received', `Code: ${code}`);
}
} else if (message.status === 'timeout') {
Alert.alert('Timeout', 'No SMS received in 5 minutes');
}
});
// Cleanup on unmount
return () => {
removeListener();
removeSmsListener();
};
}, []);
const handleStartListening = async () => {
try {
await startSmsRetriever();
Alert.alert('Listening', 'Waiting for OTP SMS...');
} catch (error) {
Alert.alert('Error', error.message);
}
};
return (
<View style={{ padding: 20 }}>
<Text>App Signature: {signature}</Text>
<Text>OTP: {otp}</Text>
<Button title="Start Listening" onPress={handleStartListening} />
</View>
);
}SMS Format for your backend:
<#> Your OTP is: 123456
FA+9qCX9VSu
import React, { useEffect, useState } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import {
requestPhoneNumber,
addSmsListener,
extractOtp,
} from '@pushpendersingh/react-native-otp-verify';
export default function ConsentOtpScreen() {
const [otp, setOtp] = useState('');
useEffect(() => {
const removeListener = addSmsListener((message) => {
if (message.status === 'success' && message.message) {
const code = extractOtp(message.message);
if (code) {
setOtp(code);
Alert.alert('OTP Received', `Code: ${code}`);
}
} else if (message.status === 'error') {
Alert.alert('Denied', 'You denied SMS access');
}
});
return () => removeListener();
}, []);
const handleRequestConsent = async () => {
try {
await requestPhoneNumber();
Alert.alert('Waiting for SMS', 'You will see a consent dialog when SMS arrives');
} catch (error) {
Alert.alert('Error', error.message);
}
};
return (
<View style={{ padding: 20 }}>
<Text>OTP: {otp}</Text>
<Button title="Request SMS Consent" onPress={handleRequestConsent} />
</View>
);
}Works with any SMS format:
Your verification code is 123456
Your backend must send SMS in this exact format:
<#> Your message here
OTP: 123456
YOUR_APP_SIGNATURE
Rules:
- Must start with
<#> - Message can be up to 140 bytes
- Must end with your 11-character app signature
- One blank line before signature
const signature = 'FA+9qCX9VSu'; // Get this from getAppSignature()
const otp = '123456';
const sms = `<#> Your OTP is: ${otp}
${signature}`;
// Send SMS using your SMS provider (Twilio, AWS SNS, etc.)
await sendSMS(phoneNumber, sms);- β You control the backend/SMS format
- β You want fully automatic verification
- β You want the best user experience (no interaction)
- β You can't control SMS format (3rd party service)
- β You want to give users explicit control
- β SMS doesn't have your app signature
None required! π
Both APIs use Google Play Services and require zero permissions.
<!-- AndroidManifest.xml - NO SMS permissions needed! -->
<manifest>
<!-- No READ_SMS -->
<!-- No RECEIVE_SMS -->
</manifest>iOS is supported with graceful error handling. The library includes native iOS implementation that:
- β Links properly without build errors
- β Returns clear error messages when methods are called
- β Follows proper TurboModule protocol
β οΈ Does not provide OTP verification functionality (Android-only feature)
All methods will reject with error code PLATFORM_NOT_SUPPORTED and message explaining that the feature is Android-only.
Example:
try {
await startSmsRetriever();
} catch (error) {
// On iOS: "@pushpendersingh/react-native-otp-verify package only supports Android."
console.log(error.message);
}Best Practice:
import { Platform } from 'react-native';
if (Platform.OS === 'android') {
// Use OTP verification on Android only
await startSmsRetriever();
}- Check app signature: Make sure backend is using correct signature from
getAppSignature() - SMS format: Verify SMS follows exact format with
<#>and signature - Timeout: SMS Retriever only listens for 5 minutes
- Google Play Services: Make sure device has Google Play Services installed
- Check SMS arrives: Dialog only shows when SMS actually arrives
- Background app: Make sure app is in foreground when SMS arrives
cd android && ./gradlew clean
cd .. && npx react-native run-androidFull TypeScript definitions included:
import {
startSmsRetriever,
getAppSignature,
requestPhoneNumber,
addSmsListener,
removeSmsListener,
extractOtp,
SmsMessage,
} from '@pushpendersingh/react-native-otp-verify';
// All functions are fully typed
const signature: Promise<string> = getAppSignature();See CONTRIBUTING.md for development workflow and guidelines.
MIT
- Built with create-react-native-library
- Uses Google SMS Retriever API
- Uses Google SMS User Consent API