Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docs/auth/phone-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,39 @@ export default function PhoneVerification() {
}
}
```

# Disabling automatic OTP verification on Android devices

Firebase Auth automagically verifies the received OTP code from the SMS and in some certain devices it might throw `The SMS code has expired` error.

Disable auto verify by adding this on `index.js` or `App.tsx`:

```jsx
(async function () {
try {
await firebase.auth().settings.setAutoOTPVerify(false);
} catch (error) {
console.error(error);
}
})();
```

To manually handle OTP verification code:

```jsx
// set code and phone from textinputs
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');

const handlePhoneLogin = async () => {
try {
const confirmResult = await auth().signInWithPhoneNumber(phone);

await confirmResult.confirm(code).then(user => {
console.log(user);
});
} catch (e) {
console.log(e);
}
};
```
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,11 @@ private void signInWithProvider(String appName, ReadableMap provider, final Prom
*/
@ReactMethod
public void signInWithPhoneNumber(
String appName, final String phoneNumber, final boolean forceResend, final Promise promise) {
String appName,
final String phoneNumber,
final boolean forceResend,
final boolean autoOTPVerify,
final Promise promise) {
Log.d(TAG, "signInWithPhoneNumber");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
Expand Down Expand Up @@ -1080,10 +1084,16 @@ public void onCodeAutoRetrievalTimeOut(String verificationId) {
if (forceResend && mForceResendingToken != null) {
PhoneAuthProvider.getInstance(firebaseAuth)
.verifyPhoneNumber(
phoneNumber, 60, TimeUnit.SECONDS, activity, callbacks, mForceResendingToken);
phoneNumber,
autoOTPVerify ? 60 : 0,
TimeUnit.SECONDS,
activity,
callbacks,
mForceResendingToken);
} else {
PhoneAuthProvider.getInstance(firebaseAuth)
.verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS, activity, callbacks);
.verifyPhoneNumber(
phoneNumber, autoOTPVerify ? 60 : 0, TimeUnit.SECONDS, activity, callbacks);
}
}
}
Expand Down Expand Up @@ -1113,7 +1123,12 @@ public void getSession(final String appName, final Promise promise) {

@ReactMethod
public void verifyPhoneNumberWithMultiFactorInfo(
final String appName, final String hintUid, final String sessionKey, final Promise promise) {
final String appName,
final String hintUid,
final String sessionKey,
final boolean autoOTPVerify,
final Promise promise
) {
final MultiFactorResolver resolver = mCachedResolvers.get(sessionKey);
if (resolver == null) {
// See https://firebase.google.com/docs/reference/node/firebase.auth.multifactorresolver for
Expand Down Expand Up @@ -1153,7 +1168,7 @@ public void verifyPhoneNumberWithMultiFactorInfo(
PhoneAuthOptions.newBuilder(firebaseAuth)
.setActivity(activity)
.setMultiFactorHint((PhoneMultiFactorInfo) selectedHint)
.setTimeout(30L, TimeUnit.SECONDS)
.setTimeout(autoOTPVerify ? 30L : 0L, TimeUnit.SECONDS)
.setMultiFactorSession(resolver.getSession())
.setCallbacks(
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
Expand Down Expand Up @@ -1185,7 +1200,9 @@ public void verifyPhoneNumberForMultiFactor(
final String appName,
final String phoneNumber,
final String sessionKey,
final Promise promise) {
final boolean autoOTPVerify,
final Promise promise
) {
final MultiFactorSession multiFactorSession = mMultiFactorSessions.get(sessionKey);
if (multiFactorSession == null) {
rejectPromiseWithCodeAndMessage(
Expand All @@ -1198,7 +1215,7 @@ public void verifyPhoneNumberForMultiFactor(
PhoneAuthOptions.newBuilder(firebaseAuth)
.setPhoneNumber(phoneNumber)
.setActivity(getCurrentActivity())
.setTimeout(30L, TimeUnit.SECONDS)
.setTimeout(autoOTPVerify ? 30L : 0L, TimeUnit.SECONDS)
.setMultiFactorSession(multiFactorSession)
.requireSmsValidation(true)
.setCallbacks(
Expand Down
9 changes: 9 additions & 0 deletions packages/auth/lib/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,21 @@ export default class Settings {
this._auth = auth;
this._forceRecaptchaFlowForTesting = false;
this._appVerificationDisabledForTesting = false;
this._autoOTPVerify = true; /* Android only */
}

get forceRecaptchaFlowForTesting() {
return this._forceRecaptchaFlowForTesting;
}

get autoOTPVerify() {
return this._autoOTPVerify;
}

setAutoOTPVerify(autoOTPVerify) {
this._autoOTPVerify = autoOTPVerify;
}

set forceRecaptchaFlowForTesting(forceRecaptchaFlow) {
if (isAndroid) {
this._forceRecaptchaFlowForTesting = forceRecaptchaFlow;
Expand Down
33 changes: 33 additions & 0 deletions packages/auth/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,39 @@ export namespace FirebaseAuthTypes {
* @param smsCode The pre-set SMS code.
*/
setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber: string, smsCode: string): Promise<null>;

/**
* Flag to disable automatic retrieval of OTP codes for the given phone number.
* When receiving an OTP verification code Android automagically digests it and
* in some cases throws this error: `The SMS code has expired`
*
* @android
*/
autoOTPVerify: boolean;

/**
* Sets whether the automatic OTP (One-Time Password) verification should be disabled on Android devices.
*
* This method allows you to control the behavior of OTP verification by disabling the automatic retrieval
* and verification of SMS codes. This can be useful in testing scenarios or when you want to handle OTP
* verification manually if your users encounter `The SMS code has expired` error on some devices.
*
* @example
* ```js
* (async function () {
* try {
* await firebase.auth().settings.setAutoOTPVerify(false);
* } catch (error) {
* console.error(error);
* }
* })();
*
* ```
*
* @android
* @param enabled whether auto OTP verify should be disabled, defaults to false
*/
setAutoOTPVerify(enabled: boolean): Promise<null>;
}

/**
Expand Down
14 changes: 11 additions & 3 deletions packages/auth/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ class FirebaseAuthModule extends FirebaseModule {
signInWithPhoneNumber(phoneNumber, forceResend) {
if (isAndroid) {
return this.native
.signInWithPhoneNumber(phoneNumber, forceResend || false)
.signInWithPhoneNumber(phoneNumber, forceResend || false, this._settings.autoOTPVerify)
.then(result => new ConfirmationResult(this, result.verificationId));
}

Expand All @@ -305,12 +305,20 @@ class FirebaseAuthModule extends FirebaseModule {
}

verifyPhoneNumberWithMultiFactorInfo(multiFactorHint, session) {
return this.native.verifyPhoneNumberWithMultiFactorInfo(multiFactorHint.uid, session);
return this.native.verifyPhoneNumberWithMultiFactorInfo(
multiFactorHint.uid,
session,
this._settings.autoOTPVerify,
);
}

verifyPhoneNumberForMultiFactor(phoneInfoOptions) {
const { phoneNumber, session } = phoneInfoOptions;
return this.native.verifyPhoneNumberForMultiFactor(phoneNumber, session);
return this.native.verifyPhoneNumberForMultiFactor(
phoneNumber,
session,
this._settings.autoOTPVerify,
);
}

resolveMultiFactorSignIn(session, verificationId, verificationCode) {
Expand Down
Loading