Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: autoOTPVerify for Android devices #8145

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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 @@
this._auth = auth;
this._forceRecaptchaFlowForTesting = false;
this._appVerificationDisabledForTesting = false;
this._autoOTPVerify = true; /* Android only */

Check warning on line 25 in packages/auth/lib/Settings.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/Settings.js#L25

Added line #L25 was not covered by tests
}

get forceRecaptchaFlowForTesting() {
return this._forceRecaptchaFlowForTesting;
}

get autoOTPVerify() {
return this._autoOTPVerify;

Check warning on line 33 in packages/auth/lib/Settings.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/Settings.js#L32-L33

Added lines #L32 - L33 were not covered by tests
}

setAutoOTPVerify(autoOTPVerify) {
this._autoOTPVerify = autoOTPVerify;

Check warning on line 37 in packages/auth/lib/Settings.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/Settings.js#L36-L37

Added lines #L36 - L37 were not covered by tests
}

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 @@
signInWithPhoneNumber(phoneNumber, forceResend) {
if (isAndroid) {
return this.native
.signInWithPhoneNumber(phoneNumber, forceResend || false)
.signInWithPhoneNumber(phoneNumber, forceResend || false, this._settings.autoOTPVerify)

Check warning on line 285 in packages/auth/lib/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/index.js#L285

Added line #L285 was not covered by tests
.then(result => new ConfirmationResult(this, result.verificationId));
}

Expand All @@ -305,12 +305,20 @@
}

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

Check warning on line 308 in packages/auth/lib/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/index.js#L308

Added line #L308 was not covered by tests
multiFactorHint.uid,
session,
this._settings.autoOTPVerify,
);
}

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

Check warning on line 317 in packages/auth/lib/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/index.js#L317

Added line #L317 was not covered by tests
phoneNumber,
session,
this._settings.autoOTPVerify,
);
}

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