react-native-otp-auto-verify 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/android/src/main/java/com/otpautoverify/OtpAutoVerifyModule.kt +12 -5
- package/lib/module/NativeOtpAutoVerify.ts +25 -13
- package/lib/module/index.js +35 -22
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeOtpAutoVerify.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -5
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeOtpAutoVerify.ts +25 -13
- package/src/index.tsx +42 -23
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ Your backend **must** include the app hash at the **end** of the SMS.
|
|
|
82
82
|
Requirements:
|
|
83
83
|
|
|
84
84
|
- Message must be **≤ 140 bytes**
|
|
85
|
-
- Must contain a **4–
|
|
85
|
+
- Must contain a **4–8 digit** OTP
|
|
86
86
|
- Must **end with** the app hash from `getHash()`
|
|
87
87
|
|
|
88
88
|
Recommended format:
|
|
@@ -327,7 +327,7 @@ Use this on your OTP screen. It manages:
|
|
|
327
327
|
|
|
328
328
|
| Property | Type | Default | Description |
|
|
329
329
|
| ---------------- | --------------------- | ------- | ---------------------------------------- |
|
|
330
|
-
| `numberOfDigits` | `4 \| 5 \| 6`
|
|
330
|
+
| `numberOfDigits` | `4 \| 5 \| 6 \| 7 \| 8` | `6` | OTP length to extract |
|
|
331
331
|
| `hashCode` | `string` | `''` | App hash (send to backend) |
|
|
332
332
|
| `otp` | `string \| null` | `null` | Extracted OTP |
|
|
333
333
|
| `sms` | `string \| null` | `null` | Full SMS text |
|
|
@@ -348,7 +348,7 @@ Android only. Throws on iOS.
|
|
|
348
348
|
|
|
349
349
|
Android only. Removes native listeners.
|
|
350
350
|
|
|
351
|
-
### `extractOtp(sms: string, numberOfDigits?: 4 | 5 | 6): string | null`
|
|
351
|
+
### `extractOtp(sms: string, numberOfDigits?: 4 | 5 | 6 | 7 | 8): string | null`
|
|
352
352
|
|
|
353
353
|
Pure helper to extract the OTP from an SMS string.
|
|
354
354
|
|
|
@@ -100,18 +100,25 @@ class OtpAutoVerifyModule(reactContext: ReactApplicationContext) :
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
private fun unregisterReceiver() {
|
|
103
|
+
if (!isReceiverRegistered || smsReceiver == null) {
|
|
104
|
+
isListening = false
|
|
105
|
+
return
|
|
106
|
+
}
|
|
103
107
|
val activity = currentActivity
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
try {
|
|
109
|
+
if (activity != null) {
|
|
106
110
|
activity.unregisterReceiver(smsReceiver)
|
|
107
111
|
Log.d(TAG, "SMS receiver unregistered")
|
|
108
|
-
} catch (e: Exception) {
|
|
109
|
-
Log.e(TAG, "Failed to unregister SMS receiver", e)
|
|
110
112
|
}
|
|
113
|
+
} catch (e: IllegalArgumentException) {
|
|
114
|
+
Log.w(TAG, "Receiver already unregistered", e)
|
|
115
|
+
} catch (e: Exception) {
|
|
116
|
+
Log.e(TAG, "Failed to unregister SMS receiver", e)
|
|
117
|
+
} finally {
|
|
111
118
|
isReceiverRegistered = false
|
|
112
119
|
smsReceiver = null
|
|
120
|
+
isListening = false
|
|
113
121
|
}
|
|
114
|
-
isListening = false
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
override fun addListener(eventName: String) {
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
-
|
|
3
|
-
export interface OtpAutoVerifySpec extends TurboModule {
|
|
4
|
-
getConstants(): { OTP_RECEIVED_EVENT: string };
|
|
5
|
-
getHash(): Promise<ReadonlyArray<string>>;
|
|
6
|
-
startSmsRetriever(): Promise<boolean>;
|
|
7
|
-
addListener(eventName: string): void;
|
|
8
|
-
removeListeners(count: number): void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
);
|
|
1
|
+
import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface OtpAutoVerifySpec extends TurboModule {
|
|
4
|
+
getConstants(): { OTP_RECEIVED_EVENT: string };
|
|
5
|
+
getHash(): Promise<ReadonlyArray<string>>;
|
|
6
|
+
startSmsRetriever(): Promise<boolean>;
|
|
7
|
+
addListener(eventName: string): void;
|
|
8
|
+
removeListeners(count: number): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getNativeModule(): OtpAutoVerifySpec {
|
|
12
|
+
try {
|
|
13
|
+
return TurboModuleRegistry.getEnforcing<OtpAutoVerifySpec>('OtpAutoVerify');
|
|
14
|
+
} catch {
|
|
15
|
+
const legacy = NativeModules.OtpAutoVerify;
|
|
16
|
+
if (!legacy) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'OtpAutoVerify native module is not available. Ensure the library is properly linked.'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return legacy as OtpAutoVerifySpec;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default getNativeModule();
|
package/lib/module/index.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { NativeEventEmitter,
|
|
4
|
+
import { NativeEventEmitter, Platform } from 'react-native';
|
|
5
5
|
import NativeOtpAutoVerify from './NativeOtpAutoVerify';
|
|
6
|
-
const
|
|
7
|
-
OtpAutoVerify
|
|
8
|
-
} = NativeModules;
|
|
9
|
-
const eventEmitter = Platform.OS === 'android' && OtpAutoVerify ? new NativeEventEmitter(OtpAutoVerify) : null;
|
|
6
|
+
const eventEmitter = Platform.OS === 'android' && NativeOtpAutoVerify ? new NativeEventEmitter(NativeOtpAutoVerify) : null;
|
|
10
7
|
const OTP_RECEIVED_EVENT = NativeOtpAutoVerify.getConstants?.()?.OTP_RECEIVED_EVENT ?? 'otpReceived';
|
|
11
|
-
const TIMEOUT_MESSAGE = 'Timeout Error.';
|
|
8
|
+
export const TIMEOUT_MESSAGE = 'Timeout Error.';
|
|
12
9
|
const DEFAULT_DIGITS = 6;
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
10
|
+
const MIN_OTP_DIGITS = 4;
|
|
11
|
+
const MAX_OTP_DIGITS = 8;
|
|
12
|
+
function getOtpRegex(digits) {
|
|
13
|
+
if (digits < MIN_OTP_DIGITS || digits > MAX_OTP_DIGITS) {
|
|
14
|
+
return new RegExp(`\\b(\\d{${DEFAULT_DIGITS}})\\b`);
|
|
15
|
+
}
|
|
16
|
+
return new RegExp(`\\b(\\d{${digits}})\\b`);
|
|
17
|
+
}
|
|
18
18
|
/**
|
|
19
|
-
* Extracts a numeric OTP of 4
|
|
20
|
-
* Only these lengths are supported (numberOfDigits: 4 | 5 | 6).
|
|
19
|
+
* Extracts a numeric OTP of 4–8 digits from SMS text.
|
|
21
20
|
*/
|
|
22
21
|
export function extractOtp(sms, numberOfDigits = DEFAULT_DIGITS) {
|
|
23
22
|
if (!sms || typeof sms !== 'string') return null;
|
|
24
23
|
const trimmed = sms.trim();
|
|
25
24
|
if (!trimmed) return null;
|
|
26
|
-
const
|
|
25
|
+
const regex = getOtpRegex(numberOfDigits);
|
|
26
|
+
const match = trimmed.match(regex);
|
|
27
27
|
return match ? match[1] : null;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -34,10 +34,15 @@ export async function getHash() {
|
|
|
34
34
|
return Array.from(arr);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/**
|
|
37
|
+
/** No-op subscription for platforms where SMS Retriever is not supported (e.g. iOS). */
|
|
38
|
+
const NOOP_SUBSCRIPTION = {
|
|
39
|
+
remove: () => {}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). On iOS, returns no-op (call remove() safely). */
|
|
38
43
|
export async function activateOtpListener(handler, options) {
|
|
39
44
|
if (Platform.OS !== 'android' || !eventEmitter) {
|
|
40
|
-
|
|
45
|
+
return NOOP_SUBSCRIPTION;
|
|
41
46
|
}
|
|
42
47
|
const numberOfDigits = options?.numberOfDigits ?? DEFAULT_DIGITS;
|
|
43
48
|
const subscription = eventEmitter.addListener(OTP_RECEIVED_EVENT, (...args) => {
|
|
@@ -72,24 +77,30 @@ export function useOtpVerification(options = {}) {
|
|
|
72
77
|
const [timeoutError, setTimeoutError] = React.useState(false);
|
|
73
78
|
const [error, setError] = React.useState(null);
|
|
74
79
|
const subscriptionRef = React.useRef(null);
|
|
80
|
+
const isStartingRef = React.useRef(false);
|
|
75
81
|
const stopListening = React.useCallback(() => {
|
|
76
82
|
subscriptionRef.current?.remove();
|
|
77
83
|
subscriptionRef.current = null;
|
|
84
|
+
isStartingRef.current = false;
|
|
78
85
|
removeListener();
|
|
79
86
|
}, []);
|
|
80
87
|
const startListening = React.useCallback(async () => {
|
|
81
88
|
if (Platform.OS !== 'android') return;
|
|
89
|
+
if (isStartingRef.current) return;
|
|
90
|
+
isStartingRef.current = true;
|
|
91
|
+
subscriptionRef.current?.remove();
|
|
92
|
+
subscriptionRef.current = null;
|
|
82
93
|
setOtp(null);
|
|
83
94
|
setSms(null);
|
|
84
95
|
setTimeoutError(false);
|
|
85
96
|
setError(null);
|
|
86
97
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
try {
|
|
99
|
+
const hashes = await getHash();
|
|
100
|
+
setHashCode(hashes[0] ?? '');
|
|
101
|
+
} catch (err) {
|
|
102
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
103
|
+
}
|
|
93
104
|
const sub = await activateOtpListener((smsText, extractedOtp) => {
|
|
94
105
|
setSms(smsText);
|
|
95
106
|
if (smsText === TIMEOUT_MESSAGE) {
|
|
@@ -108,6 +119,8 @@ export function useOtpVerification(options = {}) {
|
|
|
108
119
|
});
|
|
109
120
|
setError(wrapped);
|
|
110
121
|
throw wrapped;
|
|
122
|
+
} finally {
|
|
123
|
+
isStartingRef.current = false;
|
|
111
124
|
}
|
|
112
125
|
}, [numberOfDigits]);
|
|
113
126
|
React.useEffect(() => () => stopListening(), [stopListening]);
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","NativeEventEmitter","
|
|
1
|
+
{"version":3,"names":["React","NativeEventEmitter","Platform","NativeOtpAutoVerify","eventEmitter","OS","OTP_RECEIVED_EVENT","getConstants","TIMEOUT_MESSAGE","DEFAULT_DIGITS","MIN_OTP_DIGITS","MAX_OTP_DIGITS","getOtpRegex","digits","RegExp","extractOtp","sms","numberOfDigits","trimmed","trim","regex","match","getHash","arr","Array","from","NOOP_SUBSCRIPTION","remove","activateOtpListener","handler","options","subscription","addListener","args","smsText","String","startSmsRetriever","removeListeners","removeListener","useOtpVerification","hashCode","setHashCode","useState","otp","setOtp","setSms","timeoutError","setTimeoutError","error","setError","subscriptionRef","useRef","isStartingRef","stopListening","useCallback","current","startListening","hashes","err","Error","sub","extractedOtp","wrapped","cause","useEffect","useMemo"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeOtpAutoVerify.d.ts","sourceRoot":"","sources":["../../../src/NativeOtpAutoVerify.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"NativeOtpAutoVerify.d.ts","sourceRoot":"","sources":["../../../src/NativeOtpAutoVerify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAEpF,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,YAAY,IAAI;QAAE,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAgBD,wBAAiC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
declare const OTP_RECEIVED_EVENT: string;
|
|
2
|
-
export
|
|
2
|
+
export declare const TIMEOUT_MESSAGE = "Timeout Error.";
|
|
3
|
+
export type OtpDigits = 4 | 5 | 6 | 7 | 8;
|
|
3
4
|
export interface UseOtpVerificationOptions {
|
|
4
|
-
/** Extract OTP with this many digits (4
|
|
5
|
+
/** Extract OTP with this many digits (4–8). OTP is set when SMS is received. */
|
|
5
6
|
numberOfDigits?: OtpDigits;
|
|
6
7
|
}
|
|
7
8
|
export interface UseOtpVerificationResult {
|
|
@@ -24,13 +25,12 @@ export interface OtpListenerSubscription {
|
|
|
24
25
|
remove: () => void;
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
|
-
* Extracts a numeric OTP of 4
|
|
28
|
-
* Only these lengths are supported (numberOfDigits: 4 | 5 | 6).
|
|
28
|
+
* Extracts a numeric OTP of 4–8 digits from SMS text.
|
|
29
29
|
*/
|
|
30
30
|
export declare function extractOtp(sms: string, numberOfDigits?: OtpDigits): string | null;
|
|
31
31
|
/** Returns app hash strings for the current app. Android only; iOS returns []. */
|
|
32
32
|
export declare function getHash(): Promise<string[]>;
|
|
33
|
-
/** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). */
|
|
33
|
+
/** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). On iOS, returns no-op (call remove() safely). */
|
|
34
34
|
export declare function activateOtpListener(handler: (sms: string, extractedOtp?: string | null) => void, options?: {
|
|
35
35
|
numberOfDigits?: OtpDigits;
|
|
36
36
|
}): Promise<OtpListenerSubscription>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AASA,QAAA,MAAM,kBAAkB,QAET,CAAC;AAEhB,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAMhD,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAS1C,MAAM,WAAW,yBAAyB;IACxC,gFAAgF;IAChF,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,mCAAmC;IACnC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,6DAA6D;IAC7D,YAAY,EAAE,OAAO,CAAC;IACtB,oHAAoH;IACpH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,mEAAmE;IACnE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,oDAAoD;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,cAAc,GAAE,SAA0B,GACzC,MAAM,GAAG,IAAI,CAOf;AAED,kFAAkF;AAClF,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAIjD;AAOD,2IAA2I;AAC3I,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,EAC5D,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,SAAS,CAAA;CAAE,GACvC,OAAO,CAAC,uBAAuB,CAAC,CAqBlC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAIrC;AAED,iGAAiG;AACjG,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CAyE1B;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-otp-auto-verify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "react-native-otp-auto-verify is a React Native library for automatic OTP verification on Android and iOS. It uses the Google SMS Retriever API to detect OTP codes automatically without requiring the READ_SMS permission. The library is fully Play Store compliant, improves user login experience, supports TurboModule, and works with the React Native New Architecture",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
-
|
|
3
|
-
export interface OtpAutoVerifySpec extends TurboModule {
|
|
4
|
-
getConstants(): { OTP_RECEIVED_EVENT: string };
|
|
5
|
-
getHash(): Promise<ReadonlyArray<string>>;
|
|
6
|
-
startSmsRetriever(): Promise<boolean>;
|
|
7
|
-
addListener(eventName: string): void;
|
|
8
|
-
removeListeners(count: number): void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
);
|
|
1
|
+
import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface OtpAutoVerifySpec extends TurboModule {
|
|
4
|
+
getConstants(): { OTP_RECEIVED_EVENT: string };
|
|
5
|
+
getHash(): Promise<ReadonlyArray<string>>;
|
|
6
|
+
startSmsRetriever(): Promise<boolean>;
|
|
7
|
+
addListener(eventName: string): void;
|
|
8
|
+
removeListeners(count: number): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getNativeModule(): OtpAutoVerifySpec {
|
|
12
|
+
try {
|
|
13
|
+
return TurboModuleRegistry.getEnforcing<OtpAutoVerifySpec>('OtpAutoVerify');
|
|
14
|
+
} catch {
|
|
15
|
+
const legacy = NativeModules.OtpAutoVerify;
|
|
16
|
+
if (!legacy) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'OtpAutoVerify native module is not available. Ensure the library is properly linked.'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return legacy as OtpAutoVerifySpec;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default getNativeModule();
|
package/src/index.tsx
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { NativeEventEmitter,
|
|
2
|
+
import { NativeEventEmitter, Platform } from 'react-native';
|
|
3
3
|
import NativeOtpAutoVerify from './NativeOtpAutoVerify';
|
|
4
4
|
|
|
5
|
-
const { OtpAutoVerify } = NativeModules;
|
|
6
5
|
const eventEmitter =
|
|
7
|
-
Platform.OS === 'android' &&
|
|
8
|
-
? new NativeEventEmitter(
|
|
6
|
+
Platform.OS === 'android' && NativeOtpAutoVerify
|
|
7
|
+
? new NativeEventEmitter(NativeOtpAutoVerify)
|
|
9
8
|
: null;
|
|
10
9
|
|
|
11
10
|
const OTP_RECEIVED_EVENT =
|
|
12
11
|
(NativeOtpAutoVerify.getConstants?.()?.OTP_RECEIVED_EVENT as string) ??
|
|
13
12
|
'otpReceived';
|
|
14
13
|
|
|
15
|
-
const TIMEOUT_MESSAGE = 'Timeout Error.';
|
|
14
|
+
export const TIMEOUT_MESSAGE = 'Timeout Error.';
|
|
16
15
|
const DEFAULT_DIGITS = 6;
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
const MIN_OTP_DIGITS = 4;
|
|
18
|
+
const MAX_OTP_DIGITS = 8;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
20
|
+
export type OtpDigits = 4 | 5 | 6 | 7 | 8;
|
|
21
|
+
|
|
22
|
+
function getOtpRegex(digits: number): RegExp {
|
|
23
|
+
if (digits < MIN_OTP_DIGITS || digits > MAX_OTP_DIGITS) {
|
|
24
|
+
return new RegExp(`\\b(\\d{${DEFAULT_DIGITS}})\\b`);
|
|
25
|
+
}
|
|
26
|
+
return new RegExp(`\\b(\\d{${digits}})\\b`);
|
|
27
|
+
}
|
|
25
28
|
|
|
26
29
|
export interface UseOtpVerificationOptions {
|
|
27
|
-
/** Extract OTP with this many digits (4
|
|
30
|
+
/** Extract OTP with this many digits (4–8). OTP is set when SMS is received. */
|
|
28
31
|
numberOfDigits?: OtpDigits;
|
|
29
32
|
}
|
|
30
33
|
|
|
@@ -50,8 +53,7 @@ export interface OtpListenerSubscription {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
/**
|
|
53
|
-
* Extracts a numeric OTP of 4
|
|
54
|
-
* Only these lengths are supported (numberOfDigits: 4 | 5 | 6).
|
|
56
|
+
* Extracts a numeric OTP of 4–8 digits from SMS text.
|
|
55
57
|
*/
|
|
56
58
|
export function extractOtp(
|
|
57
59
|
sms: string,
|
|
@@ -60,7 +62,8 @@ export function extractOtp(
|
|
|
60
62
|
if (!sms || typeof sms !== 'string') return null;
|
|
61
63
|
const trimmed = sms.trim();
|
|
62
64
|
if (!trimmed) return null;
|
|
63
|
-
const
|
|
65
|
+
const regex = getOtpRegex(numberOfDigits);
|
|
66
|
+
const match = trimmed.match(regex);
|
|
64
67
|
return match ? match[1]! : null;
|
|
65
68
|
}
|
|
66
69
|
|
|
@@ -71,13 +74,18 @@ export async function getHash(): Promise<string[]> {
|
|
|
71
74
|
return Array.from(arr);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
/**
|
|
77
|
+
/** No-op subscription for platforms where SMS Retriever is not supported (e.g. iOS). */
|
|
78
|
+
const NOOP_SUBSCRIPTION: OtpListenerSubscription = {
|
|
79
|
+
remove: () => {},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Starts SMS Retriever and subscribes to OTP events. Returns subscription with remove(). On iOS, returns no-op (call remove() safely). */
|
|
75
83
|
export async function activateOtpListener(
|
|
76
84
|
handler: (sms: string, extractedOtp?: string | null) => void,
|
|
77
85
|
options?: { numberOfDigits?: OtpDigits }
|
|
78
86
|
): Promise<OtpListenerSubscription> {
|
|
79
87
|
if (Platform.OS !== 'android' || !eventEmitter) {
|
|
80
|
-
|
|
88
|
+
return NOOP_SUBSCRIPTION;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
const numberOfDigits = options?.numberOfDigits ?? DEFAULT_DIGITS;
|
|
@@ -119,26 +127,35 @@ export function useOtpVerification(
|
|
|
119
127
|
const [timeoutError, setTimeoutError] = React.useState(false);
|
|
120
128
|
const [error, setError] = React.useState<Error | null>(null);
|
|
121
129
|
const subscriptionRef = React.useRef<OtpListenerSubscription | null>(null);
|
|
130
|
+
const isStartingRef = React.useRef(false);
|
|
122
131
|
|
|
123
132
|
const stopListening = React.useCallback(() => {
|
|
124
133
|
subscriptionRef.current?.remove();
|
|
125
134
|
subscriptionRef.current = null;
|
|
135
|
+
isStartingRef.current = false;
|
|
126
136
|
removeListener();
|
|
127
137
|
}, []);
|
|
128
138
|
|
|
129
139
|
const startListening = React.useCallback(async () => {
|
|
130
140
|
if (Platform.OS !== 'android') return;
|
|
141
|
+
if (isStartingRef.current) return;
|
|
142
|
+
|
|
143
|
+
isStartingRef.current = true;
|
|
144
|
+
subscriptionRef.current?.remove();
|
|
145
|
+
subscriptionRef.current = null;
|
|
131
146
|
setOtp(null);
|
|
132
147
|
setSms(null);
|
|
133
148
|
setTimeoutError(false);
|
|
134
149
|
setError(null);
|
|
150
|
+
|
|
135
151
|
try {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
try {
|
|
153
|
+
const hashes = await getHash();
|
|
154
|
+
setHashCode(hashes[0] ?? '');
|
|
155
|
+
} catch (err) {
|
|
156
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
157
|
+
}
|
|
158
|
+
|
|
142
159
|
const sub = await activateOtpListener(
|
|
143
160
|
(smsText, extractedOtp) => {
|
|
144
161
|
setSms(smsText);
|
|
@@ -156,6 +173,8 @@ export function useOtpVerification(
|
|
|
156
173
|
const wrapped = new Error('Failed to start OTP listener', { cause: err });
|
|
157
174
|
setError(wrapped);
|
|
158
175
|
throw wrapped;
|
|
176
|
+
} finally {
|
|
177
|
+
isStartingRef.current = false;
|
|
159
178
|
}
|
|
160
179
|
}, [numberOfDigits]);
|
|
161
180
|
|