Skip to content

Commit

Permalink
Revert "Create handleRecaptchaFlow helper method"
Browse files Browse the repository at this point in the history
This reverts commit 6b934c9.
  • Loading branch information
renkelvin committed Oct 11, 2023
1 parent 6b934c9 commit 53c51dc
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 166 deletions.
5 changes: 0 additions & 5 deletions .changeset/lucky-dragons-juggle.md

This file was deleted.

34 changes: 27 additions & 7 deletions packages/auth/src/core/credentials/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { IdTokenResponse } from '../../model/id_token';
import { AuthErrorCode } from '../errors';
import { _fail } from '../util/assert';
import { AuthCredential } from './auth_credential';
import { handleRecaptchaFlow } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { RecaptchaActionName, RecaptchaClientType } from '../../api';
/**
* Interface that represents the credentials returned by {@link EmailAuthProvider} for
Expand Down Expand Up @@ -123,12 +123,32 @@ export class EmailAuthCredential extends AuthCredential {
password: this._password,
clientType: RecaptchaClientType.WEB
};
return handleRecaptchaFlow(
auth,
request,
RecaptchaActionName.SIGN_IN_WITH_PASSWORD,
signInWithPassword
);
if (auth._getRecaptchaConfig()?.emailPasswordEnabled) {
const requestWithRecaptcha = await injectRecaptchaFields(
auth,
request,
RecaptchaActionName.SIGN_IN_WITH_PASSWORD
);
return signInWithPassword(auth, requestWithRecaptcha);
} else {
return signInWithPassword(auth, request).catch(async error => {
if (
error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`
) {
console.log(
'Sign-in with email address and password is protected by reCAPTCHA for this project. Automatically triggering the reCAPTCHA flow and restarting the sign-in flow.'
);
const requestWithRecaptcha = await injectRecaptchaFields(
auth,
request,
RecaptchaActionName.SIGN_IN_WITH_PASSWORD
);
return signInWithPassword(auth, requestWithRecaptcha);
} else {
return Promise.reject(error);
}
});
}
case SignInMethod.EMAIL_LINK:
return signInWithEmailLink(auth, {
email: this._email,
Expand Down
96 changes: 81 additions & 15 deletions packages/auth/src/core/strategies/email_and_password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { _castAuth } from '../auth/auth_impl';
import { AuthErrorCode } from '../errors';
import { getModularInstance } from '@firebase/util';
import { OperationType } from '../../model/enums';
import { handleRecaptchaFlow } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { IdTokenResponse } from '../../model/id_token';
import { RecaptchaActionName, RecaptchaClientType } from '../../api';

Expand Down Expand Up @@ -103,15 +103,61 @@ export async function sendPasswordResetEmail(
email,
clientType: RecaptchaClientType.WEB
};
if (actionCodeSettings) {
_setActionCodeSettingsOnRequest(authInternal, request, actionCodeSettings);
if (authInternal._getRecaptchaConfig()?.emailPasswordEnabled) {
const requestWithRecaptcha = await injectRecaptchaFields(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
true
);
if (actionCodeSettings) {
_setActionCodeSettingsOnRequest(
authInternal,
requestWithRecaptcha,
actionCodeSettings
);
}
await authentication.sendPasswordResetEmail(
authInternal,
requestWithRecaptcha
);
} else {
if (actionCodeSettings) {
_setActionCodeSettingsOnRequest(
authInternal,
request,
actionCodeSettings
);
}
await authentication
.sendPasswordResetEmail(authInternal, request)
.catch(async error => {
if (error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`) {
console.log(
'Password resets are protected by reCAPTCHA for this project. Automatically triggering the reCAPTCHA flow and restarting the password reset flow.'
);
const requestWithRecaptcha = await injectRecaptchaFields(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
true
);
if (actionCodeSettings) {
_setActionCodeSettingsOnRequest(
authInternal,
requestWithRecaptcha,
actionCodeSettings
);
}
await authentication.sendPasswordResetEmail(
authInternal,
requestWithRecaptcha
);
} else {
return Promise.reject(error);
}
});
}
await handleRecaptchaFlow(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
authentication.sendPasswordResetEmail
);
}

/**
Expand Down Expand Up @@ -272,12 +318,32 @@ export async function createUserWithEmailAndPassword(
password,
clientType: RecaptchaClientType.WEB
};
const signUpResponse: Promise<IdTokenResponse> = handleRecaptchaFlow(
authInternal,
request,
RecaptchaActionName.SIGN_UP_PASSWORD,
signUp
);
let signUpResponse: Promise<IdTokenResponse>;
if (authInternal._getRecaptchaConfig()?.emailPasswordEnabled) {
const requestWithRecaptcha = await injectRecaptchaFields(
authInternal,
request,
RecaptchaActionName.SIGN_UP_PASSWORD
);
signUpResponse = signUp(authInternal, requestWithRecaptcha);
} else {
signUpResponse = signUp(authInternal, request).catch(async error => {
if (error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`) {
console.log(
'Sign-up is protected by reCAPTCHA for this project. Automatically triggering the reCAPTCHA flow and restarting the sign-up flow.'
);
const requestWithRecaptcha = await injectRecaptchaFields(
authInternal,
request,
RecaptchaActionName.SIGN_UP_PASSWORD
);
return signUp(authInternal, requestWithRecaptcha);
}

throw error;
});
}

const response = await signUpResponse.catch(error => {
if (
error.code === `auth/${AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS}`
Expand Down
40 changes: 32 additions & 8 deletions packages/auth/src/core/strategies/email_link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { AuthErrorCode } from '../errors';
import { _assert } from '../util/assert';
import { getModularInstance } from '@firebase/util';
import { _castAuth } from '../auth/auth_impl';
import { handleRecaptchaFlow } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { RecaptchaActionName, RecaptchaClientType } from '../../api';

/**
Expand Down Expand Up @@ -101,13 +101,37 @@ export async function sendSignInLinkToEmail(
);
}
}
setActionCodeSettings(request, actionCodeSettings);
await handleRecaptchaFlow(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
api.sendSignInLinkToEmail
);
if (authInternal._getRecaptchaConfig()?.emailPasswordEnabled) {
const requestWithRecaptcha = await injectRecaptchaFields(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
true
);
setActionCodeSettings(requestWithRecaptcha, actionCodeSettings);
await api.sendSignInLinkToEmail(authInternal, requestWithRecaptcha);
} else {
setActionCodeSettings(request, actionCodeSettings);
await api
.sendSignInLinkToEmail(authInternal, request)
.catch(async error => {
if (error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`) {
console.log(
'Email link sign-in is protected by reCAPTCHA for this project. Automatically triggering the reCAPTCHA flow and restarting the sign-in flow.'
);
const requestWithRecaptcha = await injectRecaptchaFields(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
true
);
setActionCodeSettings(requestWithRecaptcha, actionCodeSettings);
await api.sendSignInLinkToEmail(authInternal, requestWithRecaptcha);
} else {
return Promise.reject(error);
}
});
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,16 @@ import chaiAsPromised from 'chai-as-promised';
import * as sinon from 'sinon';
import sinonChai from 'sinon-chai';

import {
Endpoint,
RecaptchaClientType,
RecaptchaVersion,
RecaptchaActionName
} from '../../api';
import { Endpoint, RecaptchaClientType, RecaptchaVersion } from '../../api';
import { mockEndpointWithParams } from '../../../test/helpers/api/helper';
import { testAuth, TestAuth } from '../../../test/helpers/mock_auth';
import * as mockFetch from '../../../test/helpers/mock_fetch';
import { ServerError } from '../../api/errors';
import { AuthInternal } from '../../model/auth';

import { MockGreCAPTCHATopLevel } from './recaptcha_mock';
import {
RecaptchaEnterpriseVerifier,
FAKE_TOKEN,
handleRecaptchaFlow
FAKE_TOKEN
} from './recaptcha_enterprise_verifier';

use(chaiAsPromised);
Expand Down Expand Up @@ -124,86 +117,4 @@ describe('platform_browser/recaptcha/recaptcha_enterprise_verifier', () => {
expect(await verifier.verify()).to.eq(FAKE_TOKEN);
});
});

context('handleRecaptchaFlow', () => {
let mockAuthInstance: AuthInternal;
let mockRequest: any;
let mockActionMethod: sinon.SinonStub;

beforeEach(async () => {
mockAuthInstance = await testAuth();
mockRequest = {};
mockActionMethod = sinon.stub();
});

afterEach(() => {
sinon.restore();
});

it('should handle recaptcha when emailPasswordEnabled is true', async () => {
if (typeof window === 'undefined') {
return;
}
sinon.stub(mockAuthInstance, '_getRecaptchaConfig').returns({
emailPasswordEnabled: true,
siteKey: 'mock_site_key'
});
mockActionMethod.resolves('success');

const result = await handleRecaptchaFlow(
mockAuthInstance,
mockRequest,
RecaptchaActionName.GET_OOB_CODE,
mockActionMethod
);

expect(result).to.equal('success');
expect(mockActionMethod).to.have.been.calledOnce;
});

it('should handle action without recaptcha when emailPasswordEnabled is false and no error', async () => {
if (typeof window === 'undefined') {
return;
}
sinon.stub(mockAuthInstance, '_getRecaptchaConfig').returns({
emailPasswordEnabled: false,
siteKey: 'mock_site_key'
});
mockActionMethod.resolves('success');

const result = await handleRecaptchaFlow(
mockAuthInstance,
mockRequest,
RecaptchaActionName.GET_OOB_CODE,
mockActionMethod
);

expect(result).to.equal('success');
expect(mockActionMethod).to.have.been.calledOnce;
});

it('should handle MISSING_RECAPTCHA_TOKEN error when emailPasswordEnabled is false', async () => {
if (typeof window === 'undefined') {
return;
}
sinon.stub(mockAuthInstance, '_getRecaptchaConfig').returns({
emailPasswordEnabled: false,
siteKey: 'mock_site_key'
});
mockActionMethod.onFirstCall().rejects({
code: 'auth/MISSING_RECAPTCHA_TOKEN'
});
mockActionMethod.onSecondCall().resolves('success-after-recaptcha');

const result = await handleRecaptchaFlow(
mockAuthInstance,
mockRequest,
RecaptchaActionName.GET_OOB_CODE,
mockActionMethod
);

expect(result).to.equal('success-after-recaptcha');
expect(mockActionMethod).to.have.been.calledTwice;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { Auth } from '../../model/public_types';
import { AuthInternal } from '../../model/auth';
import { _castAuth } from '../../core/auth/auth_impl';
import * as jsHelpers from '../load_js';
import { AuthErrorCode } from '../../core/errors';

const RECAPTCHA_ENTERPRISE_URL =
'https://www.google.com/recaptcha/enterprise.js?render=';
Expand Down Expand Up @@ -176,45 +175,6 @@ export async function injectRecaptchaFields<T>(
return newRequest;
}

type ActionMethod<TRequest, TResponse> = (
auth: Auth,
request: TRequest
) => Promise<TResponse>;

export async function handleRecaptchaFlow<TRequest, TResponse>(
authInstance: AuthInternal,
request: TRequest,
actionName: RecaptchaActionName,
actionMethod: ActionMethod<TRequest, TResponse>
): Promise<TResponse> {
if (authInstance._getRecaptchaConfig()?.emailPasswordEnabled) {
const requestWithRecaptcha = await injectRecaptchaFields(
authInstance,
request,
actionName,
actionName === RecaptchaActionName.GET_OOB_CODE
);
return actionMethod(authInstance, requestWithRecaptcha);
} else {
return actionMethod(authInstance, request).catch(async error => {
if (error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`) {
console.log(
`${actionName} is protected by reCAPTCHA Enterprise for this project. Automatically triggering the reCAPTCHA flow and restarting the flow.`
);
const requestWithRecaptcha = await injectRecaptchaFields(
authInstance,
request,
actionName,
actionName === RecaptchaActionName.GET_OOB_CODE
);
return actionMethod(authInstance, requestWithRecaptcha);
} else {
return Promise.reject(error);
}
});
}
}

export async function _initializeRecaptchaConfig(auth: Auth): Promise<void> {
const authInternal = _castAuth(auth);

Expand Down

0 comments on commit 53c51dc

Please sign in to comment.