Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/firebase/firebase-js-sdk
Browse files Browse the repository at this point in the history
…into tomandersen/namedDbImproveTest
  • Loading branch information
tom-andersen committed Oct 16, 2023
2 parents 341cb32 + 3533b32 commit cfcc5fe
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 141 deletions.
5 changes: 5 additions & 0 deletions .changeset/lucky-dragons-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@firebase/auth': patch
---

Create handleRecaptchaFlow helper method
34 changes: 7 additions & 27 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 { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { handleRecaptchaFlow } 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,32 +123,12 @@ export class EmailAuthCredential extends AuthCredential {
password: this._password,
clientType: RecaptchaClientType.WEB
};
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);
}
});
}
return handleRecaptchaFlow(
auth,
request,
RecaptchaActionName.SIGN_IN_WITH_PASSWORD,
signInWithPassword
);
case SignInMethod.EMAIL_LINK:
return signInWithEmailLink(auth, {
email: this._email,
Expand Down
96 changes: 15 additions & 81 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 { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { handleRecaptchaFlow } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { IdTokenResponse } from '../../model/id_token';
import { RecaptchaActionName, RecaptchaClientType } from '../../api';

Expand Down Expand Up @@ -103,61 +103,15 @@ export async function sendPasswordResetEmail(
email,
clientType: RecaptchaClientType.WEB
};
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);
}
});
if (actionCodeSettings) {
_setActionCodeSettingsOnRequest(authInternal, request, actionCodeSettings);
}
await handleRecaptchaFlow(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
authentication.sendPasswordResetEmail
);
}

/**
Expand Down Expand Up @@ -318,32 +272,12 @@ export async function createUserWithEmailAndPassword(
password,
clientType: RecaptchaClientType.WEB
};
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 signUpResponse: Promise<IdTokenResponse> = handleRecaptchaFlow(
authInternal,
request,
RecaptchaActionName.SIGN_UP_PASSWORD,
signUp
);
const response = await signUpResponse.catch(error => {
if (
error.code === `auth/${AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS}`
Expand Down
40 changes: 8 additions & 32 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 { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { handleRecaptchaFlow } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
import { RecaptchaActionName, RecaptchaClientType } from '../../api';

/**
Expand Down Expand Up @@ -101,37 +101,13 @@ export async function 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);
}
});
}
setActionCodeSettings(request, actionCodeSettings);
await handleRecaptchaFlow(
authInternal,
request,
RecaptchaActionName.GET_OOB_CODE,
api.sendSignInLinkToEmail
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ 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 @@ -175,6 +176,45 @@ 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
28 changes: 28 additions & 0 deletions packages/firestore/test/integration/api/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1912,6 +1912,34 @@ apiDescribe('Queries', persistence => {
});
}
);

it('inequality query will reject if document key is not the last orderBy field', () => {
return withEmptyTestCollection(persistence, async coll => {
// Implicitly ordered by: __name__ asc, 'key' asc,
const queryForRejection = query(
coll,
where('key', '!=', 42),
orderBy(documentId())
);

await expect(getDocs(queryForRejection)).to.be.eventually.rejectedWith(
'order by clause cannot contain more fields after the key'
);
});
});

it('inequality query will reject if document key appears only in equality filter', () => {
return withEmptyTestCollection(persistence, async coll => {
const query_ = query(
coll,
where('key', '!=', 42),
where(documentId(), '==', 'doc1')
);
await expect(getDocs(query_)).to.be.eventually.rejectedWith(
'Equality on key is not allowed if there are other inequality fields and key does not appear in inequalities.'
);
});
});
});

// OR Query tests only run when the SDK's local cache is configured to use
Expand Down
2 changes: 1 addition & 1 deletion scripts/emulator-testing/emulators/firestore-emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class FirestoreEmulator extends Emulator {
// Use locked version of emulator for test to be deterministic.
// The latest version can be found from firestore emulator doc:
// https://firebase.google.com/docs/firestore/security/test-rules-emulator
'https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.18.1.jar',
'https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.18.2.jar',
port
);
this.projectId = projectId;
Expand Down

0 comments on commit cfcc5fe

Please sign in to comment.