diff --git a/docs-devsite/auth.md b/docs-devsite/auth.md
index 308badfc946..914cd95bfb5 100644
--- a/docs-devsite/auth.md
+++ b/docs-devsite/auth.md
@@ -506,10 +506,14 @@ Loads the reCAPTCHA configuration into the `Auth` instance.
This will load the reCAPTCHA config, which indicates whether the reCAPTCHA verification flow should be triggered for each auth provider, into the current Auth session.
-If initializeRecaptchaConfig() is not invoked, the auth flow will always start without reCAPTCHA verification. If the provider is configured to require reCAPTCHA verification, the SDK will transparently load the reCAPTCHA config and restart the auth flows.
+For email auth, if initializeRecaptchaConfig() is not invoked, the auth flow will always start without reCAPTCHA verification. If the provider is configured to require reCAPTCHA verification, the SDK will transparently load the reCAPTCHA config and restart the auth flows.
Thus, by calling this optional method, you will reduce the latency of future auth flows. Loading the reCAPTCHA config early will also enhance the signal collected by reCAPTCHA.
+For phone auth, if initializeRecaptchaConfig() is not invoked, the auth flow will always use reCAPTCHA v2 verification. If the provider is configured to require reCAPTCHA Enterprise verification, the phone verification will fail.
+
+Thus, calling this method early is required for reCAPTCHA Enterprise verification in phone auth flows.
+
This method does not work in a Node.js environment.
Signature:
@@ -923,7 +927,9 @@ Asynchronously signs in using a phone number.
This method sends a code via SMS to the given phone number, and returns a [ConfirmationResult](./auth.confirmationresult.md#confirmationresult_interface). After the user provides the code sent to their phone, call [ConfirmationResult.confirm()](./auth.confirmationresult.md#confirmationresultconfirm) with the code to sign the user in.
-For abuse prevention, this method also requires a [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface). This SDK includes a reCAPTCHA-based implementation, [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class). This function can work on other platforms that do not support the [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class) (like React Native), but you need to use a third-party [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) implementation.
+For abuse prevention with reCAPTCHA v2, this method also requires a [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface). This SDK includes a reCAPTCHA-v2-based implementation, [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class). This function can work on other platforms that do not support the [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class) (like React Native), but you need to use a third-party [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) implementation.
+
+For abuse prevention with reCAPTCHA Enterprise, [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) is not required, depending on the enforcement state. However, [initializeRecaptchaConfig()](./auth.md#initializerecaptchaconfig_2a61ea7) must be called once before initiating reCAPTCHA Enterprise verification.
This method does not work in a Node.js environment or with [Auth](./auth.auth.md#auth_interface) instances created with a [FirebaseServerApp](./app.firebaseserverapp.md#firebaseserverapp_interface).
diff --git a/docs-devsite/auth.phoneauthprovider.md b/docs-devsite/auth.phoneauthprovider.md
index e64f869303b..71dfd1bbfa6 100644
--- a/docs-devsite/auth.phoneauthprovider.md
+++ b/docs-devsite/auth.phoneauthprovider.md
@@ -211,7 +211,7 @@ verifyPhoneNumber(phoneOptions: PhoneInfoOptions | string, applicationVerifier?:
| Parameter | Type | Description |
| --- | --- | --- |
| phoneOptions | [PhoneInfoOptions](./auth.md#phoneinfooptions) \| string | |
-| applicationVerifier | [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) | For abuse prevention, this method also requires a [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface). This SDK includes a reCAPTCHA-based implementation, [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class). |
+| applicationVerifier | [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) | For abuse prevention with reCAPTCHA v2, this method also requires a [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface). This SDK includes a reCAPTCHA-based implementation, [RecaptchaVerifier](./auth.recaptchaverifier.md#recaptchaverifier_class). For abuse prevention with reCAPTCHA Enterprise, [ApplicationVerifier](./auth.applicationverifier.md#applicationverifier_interface) is not required, depending on the enforcement state. However, [initializeRecaptchaConfig()](./auth.md#initializerecaptchaconfig_2a61ea7) must be called once before initiating reCAPTCHA Enterprise verification. |
Returns:
diff --git a/packages/auth/src/core/index.ts b/packages/auth/src/core/index.ts
index 43b1adb4bb9..fc2452157be 100644
--- a/packages/auth/src/core/index.ts
+++ b/packages/auth/src/core/index.ts
@@ -75,7 +75,7 @@ export function setPersistence(
* verification flow should be triggered for each auth provider, into the
* current Auth session.
*
- * If initializeRecaptchaConfig() is not invoked, the auth flow will always start
+ * For email auth, if initializeRecaptchaConfig() is not invoked, the auth flow will always start
* without reCAPTCHA verification. If the provider is configured to require reCAPTCHA
* verification, the SDK will transparently load the reCAPTCHA config and restart the
* auth flows.
@@ -83,6 +83,13 @@ export function setPersistence(
* Thus, by calling this optional method, you will reduce the latency of future auth flows.
* Loading the reCAPTCHA config early will also enhance the signal collected by reCAPTCHA.
*
+ * For phone auth, if initializeRecaptchaConfig() is not invoked, the auth flow will always use
+ * reCAPTCHA v2 verification. If the provider is configured to require reCAPTCHA Enterprise
+ * verification, the phone verification will fail.
+ *
+ * Thus, calling this method early is required for reCAPTCHA Enterprise verification in phone auth
+ * flows.
+ *
* This method does not work in a Node.js environment.
*
* @example
diff --git a/packages/auth/src/platform_browser/providers/phone.test.ts b/packages/auth/src/platform_browser/providers/phone.test.ts
index 752aa2a892d..2dae2d641b4 100644
--- a/packages/auth/src/platform_browser/providers/phone.test.ts
+++ b/packages/auth/src/platform_browser/providers/phone.test.ts
@@ -34,9 +34,13 @@ import {
RecaptchaAuthProvider,
EnforcementState
} from '../../api';
+import { ServerError } from '../../api/errors';
import { RecaptchaVerifier } from '../../platform_browser/recaptcha/recaptcha_verifier';
import { PhoneAuthProvider } from './phone';
-import { FAKE_TOKEN } from '../recaptcha/recaptcha_enterprise_verifier';
+import {
+ FAKE_TOKEN,
+ _initializeRecaptchaConfig
+} from '../recaptcha/recaptcha_enterprise_verifier';
import { MockGreCAPTCHATopLevel } from '../recaptcha/recaptcha_mock';
import { ApplicationVerifierInternal } from '../../model/application_verifier';
@@ -46,6 +50,36 @@ describe('platform_browser/providers/phone', () => {
let auth: TestAuth;
let v2Verifier: ApplicationVerifierInternal;
+ const recaptchaConfigResponseEnforce = {
+ recaptchaKey: 'foo/bar/to/site-key',
+ recaptchaEnforcementState: [
+ {
+ provider: RecaptchaAuthProvider.PHONE_PROVIDER,
+ enforcementState: EnforcementState.ENFORCE
+ }
+ ]
+ };
+
+ const recaptchaConfigResponseAudit = {
+ recaptchaKey: 'foo/bar/to/site-key',
+ recaptchaEnforcementState: [
+ {
+ provider: RecaptchaAuthProvider.PHONE_PROVIDER,
+ enforcementState: EnforcementState.AUDIT
+ }
+ ]
+ };
+
+ const recaptchaConfigResponseOff = {
+ // no recaptcha key if no rCE provider is enabled
+ recaptchaEnforcementState: [
+ {
+ provider: RecaptchaAuthProvider.PHONE_PROVIDER,
+ enforcementState: EnforcementState.OFF
+ }
+ ]
+ };
+
beforeEach(async () => {
fetch.setUp();
auth = await testAuth();
@@ -63,15 +97,6 @@ describe('platform_browser/providers/phone', () => {
context('#verifyPhoneNumber', () => {
it('calls verify on the appVerifier and then calls the server when recaptcha enterprise is disabled', async () => {
- const recaptchaConfigResponseOff = {
- // no recaptcha key if no rCE provider is enabled
- recaptchaEnforcementState: [
- {
- provider: RecaptchaAuthProvider.PHONE_PROVIDER,
- enforcementState: EnforcementState.OFF
- }
- ]
- };
const recaptcha = new MockGreCAPTCHATopLevel();
if (typeof window === 'undefined') {
return;
@@ -110,15 +135,6 @@ describe('platform_browser/providers/phone', () => {
});
it('throws an error if verify without appVerifier when recaptcha enterprise is disabled', async () => {
- const recaptchaConfigResponseOff = {
- // no recaptcha key if no rCE provider is enabled
- recaptchaEnforcementState: [
- {
- provider: RecaptchaAuthProvider.PHONE_PROVIDER,
- enforcementState: EnforcementState.OFF
- }
- ]
- };
const recaptcha = new MockGreCAPTCHATopLevel();
if (typeof window === 'undefined') {
return;
@@ -143,16 +159,7 @@ describe('platform_browser/providers/phone', () => {
).to.be.rejectedWith(FirebaseError, 'auth/argument-error');
});
- it('calls the server without appVerifier when recaptcha enterprise is enabled', async () => {
- const recaptchaConfigResponseEnforce = {
- recaptchaKey: 'foo/bar/to/site-key',
- recaptchaEnforcementState: [
- {
- provider: RecaptchaAuthProvider.PHONE_PROVIDER,
- enforcementState: EnforcementState.ENFORCE
- }
- ]
- };
+ it('calls the server without appVerifier when recaptcha enterprise is enforced', async () => {
const recaptcha = new MockGreCAPTCHATopLevel();
if (typeof window === 'undefined') {
return;
@@ -170,6 +177,7 @@ describe('platform_browser/providers/phone', () => {
},
recaptchaConfigResponseEnforce
);
+ await _initializeRecaptchaConfig(auth);
const route = mockEndpoint(Endpoint.SEND_VERIFICATION_CODE, {
sessionInfo: 'verification-id'
@@ -186,16 +194,7 @@ describe('platform_browser/providers/phone', () => {
});
});
- it('calls the server when recaptcha enterprise is enabled', async () => {
- const recaptchaConfigResponseEnforce = {
- recaptchaKey: 'foo/bar/to/site-key',
- recaptchaEnforcementState: [
- {
- provider: RecaptchaAuthProvider.PHONE_PROVIDER,
- enforcementState: EnforcementState.ENFORCE
- }
- ]
- };
+ it('calls the server when recaptcha enterprise is enforced', async () => {
const recaptcha = new MockGreCAPTCHATopLevel();
if (typeof window === 'undefined') {
return;
@@ -213,6 +212,7 @@ describe('platform_browser/providers/phone', () => {
},
recaptchaConfigResponseEnforce
);
+ await _initializeRecaptchaConfig(auth);
const route = mockEndpoint(Endpoint.SEND_VERIFICATION_CODE, {
sessionInfo: 'verification-id'
@@ -231,6 +231,183 @@ describe('platform_browser/providers/phone', () => {
recaptchaVersion: RecaptchaVersion.ENTERPRISE
});
});
+
+ /* Test cases when initializeRecaptchaConfig is not called before phone verification */
+ it('throws invalid-recaptcha-token when recaptcha enterprise is enforced, but initializeRecaptchaConfig was not called', async () => {
+ const recaptcha = new MockGreCAPTCHATopLevel();
+ if (typeof window === 'undefined') {
+ return;
+ }
+ window.grecaptcha = recaptcha;
+ sinon
+ .stub(recaptcha.enterprise, 'execute')
+ .returns(Promise.resolve('enterprise-token'));
+
+ mockEndpointWithParams(
+ Endpoint.GET_RECAPTCHA_CONFIG,
+ {
+ clientType: RecaptchaClientType.WEB,
+ version: RecaptchaVersion.ENTERPRISE
+ },
+ recaptchaConfigResponseEnforce
+ );
+ // Not call initializeRecaptchaConfig
+
+ const failureMock = mockEndpoint(
+ Endpoint.SEND_VERIFICATION_CODE,
+ {
+ error: {
+ code: 400,
+ message: ServerError.INVALID_RECAPTCHA_TOKEN
+ }
+ },
+ 400
+ );
+
+ const provider = new PhoneAuthProvider(auth);
+ await expect(
+ provider.verifyPhoneNumber('+15105550000', v2Verifier)
+ ).to.be.rejectedWith(FirebaseError, 'auth/invalid-recaptcha-token');
+ expect(failureMock.calls[0].request).to.eql({
+ phoneNumber: '+15105550000',
+ captchaResponse: FAKE_TOKEN,
+ clientType: RecaptchaClientType.WEB,
+ recaptchaToken: 'verification-code',
+ recaptchaVersion: RecaptchaVersion.ENTERPRISE
+ });
+ });
+
+ it('throws argument-error when recaptcha enterprise is enforced, but initializeRecaptchaConfig was not called', async () => {
+ const recaptcha = new MockGreCAPTCHATopLevel();
+ if (typeof window === 'undefined') {
+ return;
+ }
+ window.grecaptcha = recaptcha;
+ sinon
+ .stub(recaptcha.enterprise, 'execute')
+ .returns(Promise.resolve('enterprise-token'));
+
+ mockEndpointWithParams(
+ Endpoint.GET_RECAPTCHA_CONFIG,
+ {
+ clientType: RecaptchaClientType.WEB,
+ version: RecaptchaVersion.ENTERPRISE
+ },
+ recaptchaConfigResponseEnforce
+ );
+ // Not call initializeRecaptchaConfig
+
+ const provider = new PhoneAuthProvider(auth);
+ await expect(
+ provider.verifyPhoneNumber('+15105550000')
+ ).to.be.rejectedWith(FirebaseError, 'auth/argument-error');
+ });
+
+ it('does recaptcha v2 verification when recaptcha enterprise is disabled, but initializeRecaptchaConfig was not called', async () => {
+ const recaptcha = new MockGreCAPTCHATopLevel();
+ if (typeof window === 'undefined') {
+ return;
+ }
+ window.grecaptcha = recaptcha;
+ sinon
+ .stub(recaptcha.enterprise, 'execute')
+ .returns(Promise.resolve('enterprise-token'));
+
+ mockEndpointWithParams(
+ Endpoint.GET_RECAPTCHA_CONFIG,
+ {
+ clientType: RecaptchaClientType.WEB,
+ version: RecaptchaVersion.ENTERPRISE
+ },
+ recaptchaConfigResponseOff
+ );
+ // Not call initializeRecaptchaConfig
+
+ const route = mockEndpoint(Endpoint.SEND_VERIFICATION_CODE, {
+ sessionInfo: 'verification-id'
+ });
+
+ const provider = new PhoneAuthProvider(auth);
+ const result = await provider.verifyPhoneNumber(
+ '+15105550000',
+ v2Verifier
+ );
+
+ expect(result).to.eq('verification-id');
+ expect(route.calls[0].request).to.eql({
+ phoneNumber: '+15105550000',
+ recaptchaToken: 'verification-code',
+ captchaResponse: FAKE_TOKEN,
+ clientType: RecaptchaClientType.WEB,
+ recaptchaVersion: RecaptchaVersion.ENTERPRISE
+ });
+ });
+
+ it('does recaptcha v2 verification when recaptcha enterprise is audit, but initializeRecaptchaConfig was not called', async () => {
+ const recaptcha = new MockGreCAPTCHATopLevel();
+ if (typeof window === 'undefined') {
+ return;
+ }
+ window.grecaptcha = recaptcha;
+ sinon
+ .stub(recaptcha.enterprise, 'execute')
+ .returns(Promise.resolve('enterprise-token'));
+
+ mockEndpointWithParams(
+ Endpoint.GET_RECAPTCHA_CONFIG,
+ {
+ clientType: RecaptchaClientType.WEB,
+ version: RecaptchaVersion.ENTERPRISE
+ },
+ recaptchaConfigResponseAudit
+ );
+ // Not call initializeRecaptchaConfig
+
+ const route = mockEndpoint(Endpoint.SEND_VERIFICATION_CODE, {
+ sessionInfo: 'verification-id'
+ });
+
+ const provider = new PhoneAuthProvider(auth);
+ const result = await provider.verifyPhoneNumber(
+ '+15105550000',
+ v2Verifier
+ );
+
+ expect(result).to.eq('verification-id');
+ expect(route.calls[0].request).to.eql({
+ phoneNumber: '+15105550000',
+ recaptchaToken: 'verification-code',
+ captchaResponse: FAKE_TOKEN,
+ clientType: RecaptchaClientType.WEB,
+ recaptchaVersion: RecaptchaVersion.ENTERPRISE
+ });
+ });
+
+ it('throws argument-error when recaptcha enterprise is audit, but initializeRecaptchaConfig was not called', async () => {
+ const recaptcha = new MockGreCAPTCHATopLevel();
+ if (typeof window === 'undefined') {
+ return;
+ }
+ window.grecaptcha = recaptcha;
+ sinon
+ .stub(recaptcha.enterprise, 'execute')
+ .returns(Promise.resolve('enterprise-token'));
+
+ mockEndpointWithParams(
+ Endpoint.GET_RECAPTCHA_CONFIG,
+ {
+ clientType: RecaptchaClientType.WEB,
+ version: RecaptchaVersion.ENTERPRISE
+ },
+ recaptchaConfigResponseAudit
+ );
+ // Not call initializeRecaptchaConfig
+
+ const provider = new PhoneAuthProvider(auth);
+ await expect(
+ provider.verifyPhoneNumber('+15105550000')
+ ).to.be.rejectedWith(FirebaseError, 'auth/argument-error');
+ });
});
context('.credential', () => {
diff --git a/packages/auth/src/platform_browser/providers/phone.ts b/packages/auth/src/platform_browser/providers/phone.ts
index a9b2f253f8a..eb90dfdf375 100644
--- a/packages/auth/src/platform_browser/providers/phone.ts
+++ b/packages/auth/src/platform_browser/providers/phone.ts
@@ -95,9 +95,11 @@ export class PhoneAuthProvider {
*
* @param phoneInfoOptions - The user's {@link PhoneInfoOptions}. The phone number should be in
* E.164 format (e.g. +16505550101).
- * @param applicationVerifier - For abuse prevention, this method also requires a
- * {@link ApplicationVerifier}. This SDK includes a reCAPTCHA-based implementation,
- * {@link RecaptchaVerifier}.
+ * @param applicationVerifier - For abuse prevention with reCAPTCHA v2, this method also requires
+ * a {@link ApplicationVerifier}. This SDK includes a reCAPTCHA-based implementation,
+ * {@link RecaptchaVerifier}. For abuse prevention with reCAPTCHA Enterprise, {@link ApplicationVerifier}
+ * is not required, depending on the enforcement state. However, {@link initializeRecaptchaConfig}
+ * must be called once before initiating reCAPTCHA Enterprise verification.
*
* @returns A Promise for a verification ID that can be passed to
* {@link PhoneAuthProvider.credential} to identify this flow.
diff --git a/packages/auth/src/platform_browser/strategies/phone.test.ts b/packages/auth/src/platform_browser/strategies/phone.test.ts
index 58a5bdaf6ad..5c26115bd38 100644
--- a/packages/auth/src/platform_browser/strategies/phone.test.ts
+++ b/packages/auth/src/platform_browser/strategies/phone.test.ts
@@ -46,7 +46,10 @@ import { IdTokenResponse, IdTokenResponseKind } from '../../model/id_token';
import { UserInternal } from '../../model/user';
import { RecaptchaVerifier } from '../../platform_browser/recaptcha/recaptcha_verifier';
import { PhoneAuthCredential } from '../../core/credentials/phone';
-import { FAKE_TOKEN } from '../recaptcha/recaptcha_enterprise_verifier';
+import {
+ FAKE_TOKEN,
+ _initializeRecaptchaConfig
+} from '../recaptcha/recaptcha_enterprise_verifier';
import { MockGreCAPTCHATopLevel } from '../../platform_browser/recaptcha/recaptcha_mock';
import {
@@ -171,6 +174,8 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.ENFORCE);
+ await _initializeRecaptchaConfig(auth);
+
await signInWithPhoneNumber(auth, '+15105550000', v2Verifier);
expect(sendCodeEndpoint.calls[0].request).to.eql({
@@ -186,6 +191,8 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.ENFORCE);
+ await _initializeRecaptchaConfig(auth);
+
await signInWithPhoneNumber(auth, '+15105550000');
expect(sendCodeEndpoint.calls[0].request).to.eql({
@@ -303,6 +310,8 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.ENFORCE);
+ await _initializeRecaptchaConfig(auth);
+
await linkWithPhoneNumber(user, '+15105550000', v2Verifier);
expect(sendCodeEndpoint.calls[0].request).to.eql({
@@ -390,6 +399,8 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.ENFORCE);
+ await _initializeRecaptchaConfig(auth);
+
await reauthenticateWithPhoneNumber(user, '+15105550000', v2Verifier);
expect(sendCodeEndpoint.calls[0].request).to.eql({
@@ -520,7 +531,10 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.AUDIT);
+ await _initializeRecaptchaConfig(auth);
+
const sessionInfo = await _verifyPhoneNumber(auth, 'number', v2Verifier);
+
expect(sessionInfo).to.eq('session-info');
expect(sendCodeEndpoint.calls[0].request).to.eql({
phoneNumber: 'number',
@@ -535,7 +549,10 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.ENFORCE);
+ await _initializeRecaptchaConfig(auth);
+
const sessionInfo = await _verifyPhoneNumber(auth, 'number');
+
expect(sessionInfo).to.eq('session-info');
expect(sendCodeEndpoint.calls[0].request).to.eql({
phoneNumber: 'number',
@@ -562,6 +579,7 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.AUDIT);
+ await _initializeRecaptchaConfig(auth);
const failureMock = mockEndpoint(
Endpoint.SEND_VERIFICATION_CODE,
{
@@ -572,6 +590,7 @@ describe('platform_browser/strategies/phone', () => {
},
400
);
+
await expect(
_verifyPhoneNumber(auth, 'number', v2Verifier)
).to.be.rejectedWith(
@@ -600,6 +619,7 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.AUDIT);
+ await _initializeRecaptchaConfig(auth);
const failureMock = mockEndpoint(
Endpoint.SEND_VERIFICATION_CODE,
{
@@ -610,6 +630,7 @@ describe('platform_browser/strategies/phone', () => {
},
400
);
+
await expect(
_verifyPhoneNumber(auth, 'number', v2Verifier)
).to.be.rejectedWith(
@@ -638,6 +659,7 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.AUDIT);
+ await _initializeRecaptchaConfig(auth);
const failureMock = mockEndpoint(
Endpoint.SEND_VERIFICATION_CODE,
{
@@ -648,6 +670,7 @@ describe('platform_browser/strategies/phone', () => {
},
400
);
+
await expect(
_verifyPhoneNumber(auth, 'number', v2Verifier)
).to.be.rejectedWith(
@@ -710,17 +733,20 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.ENFORCE);
+ await _initializeRecaptchaConfig(auth);
const endpoint = mockEndpoint(Endpoint.START_MFA_ENROLLMENT, {
phoneSessionInfo: {
sessionInfo: 'session-info'
}
});
const session = (await mfaUser.getSession()) as MultiFactorSessionImpl;
+
const sessionInfo = await _verifyPhoneNumber(
auth,
{ phoneNumber: 'number', session },
v2Verifier
);
+
expect(sessionInfo).to.eq('session-info');
expect(endpoint.calls[0].request).to.eql({
idToken: session.credential,
@@ -776,6 +802,7 @@ describe('platform_browser/strategies/phone', () => {
return;
}
mockRecaptchaEnterpriseEnablement(EnforcementState.ENFORCE);
+ await _initializeRecaptchaConfig(auth);
const endpoint = mockEndpoint(Endpoint.START_MFA_SIGN_IN, {
phoneResponseInfo: {
sessionInfo: 'session-info'
@@ -789,6 +816,7 @@ describe('platform_browser/strategies/phone', () => {
enrolledAt: Date.now(),
phoneInfo: 'phone-number-from-enrollment'
});
+
const sessionInfo = await _verifyPhoneNumber(
auth,
{
@@ -797,6 +825,7 @@ describe('platform_browser/strategies/phone', () => {
},
v2Verifier
);
+
expect(sessionInfo).to.eq('session-info');
expect(endpoint.calls[0].request).to.eql({
mfaPendingCredential: 'mfa-pending-credential',
diff --git a/packages/auth/src/platform_browser/strategies/phone.ts b/packages/auth/src/platform_browser/strategies/phone.ts
index bf4c1a35ee4..80ff9001fbe 100644
--- a/packages/auth/src/platform_browser/strategies/phone.ts
+++ b/packages/auth/src/platform_browser/strategies/phone.ts
@@ -69,8 +69,7 @@ import { getModularInstance } from '@firebase/util';
import { ProviderId } from '../../model/enums';
import {
FAKE_TOKEN,
- handleRecaptchaFlow,
- _initializeRecaptchaConfig
+ handleRecaptchaFlow
} from '../recaptcha/recaptcha_enterprise_verifier';
import { _isFirebaseServerApp } from '@firebase/app';
@@ -102,12 +101,17 @@ class ConfirmationResultImpl implements ConfirmationResult {
* provides the code sent to their phone, call {@link ConfirmationResult.confirm}
* with the code to sign the user in.
*
- * For abuse prevention, this method also requires a {@link ApplicationVerifier}.
- * This SDK includes a reCAPTCHA-based implementation, {@link RecaptchaVerifier}.
+ * For abuse prevention with reCAPTCHA v2, this method also requires a {@link ApplicationVerifier}.
+ * This SDK includes a reCAPTCHA-v2-based implementation, {@link RecaptchaVerifier}.
* This function can work on other platforms that do not support the
* {@link RecaptchaVerifier} (like React Native), but you need to use a
* third-party {@link ApplicationVerifier} implementation.
*
+ * For abuse prevention with reCAPTCHA Enterprise, {@link ApplicationVerifier} is not required,
+ * depending on the enforcement state.
+ * However, {@link initializeRecaptchaConfig} must be called once before initiating reCAPTCHA
+ * Enterprise verification.
+ *
* This method does not work in a Node.js environment or with {@link Auth} instances created with a
* {@link @firebase/app#FirebaseServerApp}.
*
@@ -226,20 +230,6 @@ export async function _verifyPhoneNumber(
options: PhoneInfoOptions | string,
verifier?: ApplicationVerifierInternal
): Promise {
- if (!auth._getRecaptchaConfig()) {
- try {
- await _initializeRecaptchaConfig(auth);
- } catch (error) {
- // If an error occurs while fetching the config, there is no way to know the enablement state
- // of Phone provider, so we proceed with recaptcha V2 verification.
- // The error is likely "recaptchaKey undefined", as reCAPTCHA Enterprise is not
- // enabled for any provider.
- console.log(
- 'Failed to initialize reCAPTCHA Enterprise config. Triggering the reCAPTCHA v2 verification.'
- );
- }
- }
-
try {
let phoneInfoOptions: PhoneInfoOptions;