Skip to content

Commit 241e4c8

Browse files
author
renkelvin
authored
Merge 99662da into f7cac56
2 parents f7cac56 + 99662da commit 241e4c8

File tree

16 files changed

+128
-132
lines changed

16 files changed

+128
-132
lines changed

common/api-review/auth.api.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,14 @@ export const AuthErrorCodes: {
227227
readonly WEAK_PASSWORD: "auth/weak-password";
228228
readonly WEB_STORAGE_UNSUPPORTED: "auth/web-storage-unsupported";
229229
readonly ALREADY_INITIALIZED: "auth/already-initialized";
230-
readonly RECAPTCHA_CHECK_FAILED: "auth/recaptcha-check-failed";
231230
readonly RECAPTCHA_NOT_ENABLED: "auth/recaptcha-not-enabled";
231+
readonly MISSING_RECAPTCHA_TOKEN: "auth/missing-recaptcha-token";
232+
readonly INVALID_RECAPTCHA_TOKEN: "auth/invalid-recaptcha-token";
233+
readonly INVALID_RECAPTCHA_ACTION: "auth/invalid-recaptcha-action";
234+
readonly MISSING_CLIENT_TYPE: "auth/missing-client-type";
235+
readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version";
236+
readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version";
237+
readonly INVALID_REQ_TYPE: "auth/invalid-req-type";
232238
};
233239

234240
// @public
@@ -665,11 +671,6 @@ export function reauthenticateWithPopup(user: User, provider: AuthProvider, reso
665671
// @public
666672
export function reauthenticateWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise<never>;
667673

668-
// @public (undocumented)
669-
export interface RecaptchaConfig {
670-
emailPasswordEnabled: boolean;
671-
}
672-
673674
// @public
674675
export interface RecaptchaParameters {
675676
// (undocumented)

docs-devsite/auth.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ Firebase Authentication
130130
| [PhoneSingleFactorInfoOptions](./auth.phonesinglefactorinfooptions.md#phonesinglefactorinfooptions_interface) | Options used for single-factor sign-in. |
131131
| [PopupRedirectResolver](./auth.popupredirectresolver.md#popupredirectresolver_interface) | A resolver used for handling DOM specific operations like [signInWithPopup()](./auth.md#signinwithpopup) or [signInWithRedirect()](./auth.md#signinwithredirect)<!-- -->. |
132132
| [ReactNativeAsyncStorage](./auth.reactnativeasyncstorage.md#reactnativeasyncstorage_interface) | Interface for a supplied <code>AsyncStorage</code>. |
133-
| [RecaptchaConfig](./auth.recaptchaconfig.md#recaptchaconfig_interface) | |
134133
| [RecaptchaParameters](./auth.recaptchaparameters.md#recaptchaparameters_interface) | Interface representing reCAPTCHA parameters.<!-- -->See the \[reCAPTCHA docs\](https://developers.google.com/recaptcha/docs/display\#render\_param) for the list of accepted parameters. All parameters are accepted except for <code>sitekey</code>: Firebase Auth provisions a reCAPTCHA for each project and will configure the site key upon rendering.<!-- -->For an invisible reCAPTCHA, set the <code>size</code> key to <code>invisible</code>. |
135134
| [User](./auth.user.md#user_interface) | A user account. |
136135
| [UserCredential](./auth.usercredential.md#usercredential_interface) | A structure containing a [User](./auth.user.md#user_interface)<!-- -->, the [OperationType](./auth.md#operationtype)<!-- -->, and the provider ID. |
@@ -1823,8 +1822,14 @@ AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY: {
18231822
readonly WEAK_PASSWORD: "auth/weak-password";
18241823
readonly WEB_STORAGE_UNSUPPORTED: "auth/web-storage-unsupported";
18251824
readonly ALREADY_INITIALIZED: "auth/already-initialized";
1826-
readonly RECAPTCHA_CHECK_FAILED: "auth/recaptcha-check-failed";
18271825
readonly RECAPTCHA_NOT_ENABLED: "auth/recaptcha-not-enabled";
1826+
readonly MISSING_RECAPTCHA_TOKEN: "auth/missing-recaptcha-token";
1827+
readonly INVALID_RECAPTCHA_TOKEN: "auth/invalid-recaptcha-token";
1828+
readonly INVALID_RECAPTCHA_ACTION: "auth/invalid-recaptcha-action";
1829+
readonly MISSING_CLIENT_TYPE: "auth/missing-client-type";
1830+
readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version";
1831+
readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version";
1832+
readonly INVALID_REQ_TYPE: "auth/invalid-req-type";
18281833
}
18291834
```
18301835

docs-devsite/auth.recaptchaconfig.md

Lines changed: 0 additions & 34 deletions
This file was deleted.

packages/auth/src/api/authentication/recaptcha.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
_performApiRequest,
2424
_addTidIfNecessary
2525
} from '../index';
26-
import { Auth, RecaptchaConfig } from '../../model/public_types';
26+
import { Auth } from '../../model/public_types';
2727

2828
interface GetRecaptchaParamResponse {
2929
recaptchaSiteKey?: string;
@@ -47,9 +47,14 @@ interface GetRecaptchaConfigRequest {
4747
version?: RecaptchaVersion;
4848
}
4949

50-
interface GetRecaptchaConfigResponse {
51-
recaptchaKey?: string;
52-
recaptchaConfig?: RecaptchaConfig;
50+
interface RecaptchaEnforcementState {
51+
provider: string;
52+
enforcementState: string;
53+
}
54+
55+
export interface GetRecaptchaConfigResponse {
56+
recaptchaKey: string;
57+
recaptchaEnforcementState: RecaptchaEnforcementState[];
5358
}
5459

5560
export async function getRecaptchaConfig(

packages/auth/src/api/errors.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,14 @@ export const enum ServerError {
9191
USER_DISABLED = 'USER_DISABLED',
9292
USER_NOT_FOUND = 'USER_NOT_FOUND',
9393
WEAK_PASSWORD = 'WEAK_PASSWORD',
94-
INVALID_RECAPTCHA_SCORE = 'invalid-recaptcha-score',
94+
RECAPTCHA_NOT_ENABLED = 'recaptcha-not-enabled',
9595
MISSING_RECAPTCHA_TOKEN = 'missing-recaptcha-token',
9696
INVALID_RECAPTCHA_TOKEN = 'invalid-recaptcha-token',
97-
INVALID_RECAPTCHA_ACTION = 'invalide-recaptcha-action',
98-
INVALID_RECAPTCHA_ENFORCEMENT_STATE = 'invalid-recaptcha-enforcement-state',
99-
RECAPTCHA_NOT_ENABLED = 'recaptcha-not-enabled',
97+
INVALID_RECAPTCHA_ACTION = 'invalid-recaptcha-action',
10098
MISSING_CLIENT_TYPE = 'missing-client-type',
10199
MISSING_RECAPTCHA_VERSION = 'missing-recaptcha-version',
102-
INVALID_REQ_TYPE = 'invalid-req-type',
103-
INVALID_RECAPTCHA_VERSION = 'invalid-recaptcha-version'
100+
INVALID_RECAPTCHA_VERSION = 'invalid-recaptcha-version',
101+
INVALID_REQ_TYPE = 'invalid-req-type'
104102
}
105103

106104
/**
@@ -216,15 +214,15 @@ export const SERVER_ERROR_MAP: Partial<ServerErrorMap<ServerError>> = {
216214
[ServerError.BLOCKING_FUNCTION_ERROR_RESPONSE]: AuthErrorCode.INTERNAL_ERROR,
217215

218216
// Recaptcha related errors.
219-
[ServerError.INVALID_RECAPTCHA_SCORE]: AuthErrorCode.RECAPTCHA_CHECK_FAILED,
220217
[ServerError.RECAPTCHA_NOT_ENABLED]: AuthErrorCode.RECAPTCHA_NOT_ENABLED,
221-
[ServerError.MISSING_RECAPTCHA_TOKEN]: AuthErrorCode.INTERNAL_ERROR,
222-
[ServerError.INVALID_RECAPTCHA_TOKEN]: AuthErrorCode.INTERNAL_ERROR,
223-
[ServerError.INVALID_RECAPTCHA_ACTION]: AuthErrorCode.INTERNAL_ERROR,
224-
[ServerError.INVALID_RECAPTCHA_ENFORCEMENT_STATE]:
225-
AuthErrorCode.INTERNAL_ERROR,
226-
[ServerError.MISSING_CLIENT_TYPE]: AuthErrorCode.INTERNAL_ERROR,
227-
[ServerError.MISSING_RECAPTCHA_VERSION]: AuthErrorCode.INTERNAL_ERROR,
228-
[ServerError.INVALID_REQ_TYPE]: AuthErrorCode.INTERNAL_ERROR,
229-
[ServerError.INVALID_RECAPTCHA_VERSION]: AuthErrorCode.INTERNAL_ERROR
218+
[ServerError.MISSING_RECAPTCHA_TOKEN]: AuthErrorCode.MISSING_RECAPTCHA_TOKEN,
219+
[ServerError.INVALID_RECAPTCHA_TOKEN]: AuthErrorCode.INVALID_RECAPTCHA_TOKEN,
220+
[ServerError.INVALID_RECAPTCHA_ACTION]:
221+
AuthErrorCode.INVALID_RECAPTCHA_ACTION,
222+
[ServerError.MISSING_CLIENT_TYPE]: AuthErrorCode.MISSING_CLIENT_TYPE,
223+
[ServerError.MISSING_RECAPTCHA_VERSION]:
224+
AuthErrorCode.MISSING_RECAPTCHA_VERSION,
225+
[ServerError.INVALID_RECAPTCHA_VERSION]:
226+
AuthErrorCode.INVALID_RECAPTCHA_VERSION,
227+
[ServerError.INVALID_REQ_TYPE]: AuthErrorCode.INVALID_REQ_TYPE
230228
};

packages/auth/src/core/auth/auth_impl.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {
2222
AuthErrorMap,
2323
AuthSettings,
2424
EmulatorConfig,
25-
RecaptchaConfig,
2625
NextOrObserver,
2726
Persistence,
2827
PopupRedirectResolver,
@@ -65,6 +64,7 @@ import { HttpHeader, RecaptchaClientType, RecaptchaVersion } from '../../api';
6564
import { getRecaptchaConfig } from '../../api/authentication/recaptcha';
6665
import { RecaptchaEnterpriseVerifier } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
6766
import { AuthMiddlewareQueue } from './middleware';
67+
import { RecaptchaConfig } from '../../platform_browser/recaptcha/recaptcha';
6868

6969
interface AsyncAction {
7070
(): Promise<void>;
@@ -397,16 +397,14 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
397397
clientType: RecaptchaClientType.WEB,
398398
version: RecaptchaVersion.ENTERPRISE
399399
});
400-
// TODO(chuanr): Confirm the response format when backend is ready
401-
if (response.recaptchaConfig === undefined) {
402-
throw new Error('recaptchaConfig undefined');
403-
}
404-
const config = response.recaptchaConfig;
405-
if (this.tenantId) {
406-
this._tenantRecaptchaConfigs[this.tenantId] = config;
407-
} else {
400+
401+
const config = new RecaptchaConfig(response);
402+
if (this.tenantId == null) {
408403
this._agentRecaptchaConfig = config;
404+
} else {
405+
this._tenantRecaptchaConfigs[this.tenantId] = config;
409406
}
407+
410408
if (config.emailPasswordEnabled) {
411409
const verifier = new RecaptchaEnterpriseVerifier(this);
412410
void verifier.verify();

packages/auth/src/core/credentials/email.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ describe('core/credentials/email', () => {
233233
recaptchaConfig: { emailPasswordEnabled: true }
234234
}
235235
);
236-
RecaptchaEnterpriseVerifier.agentSiteKey = 'wrong-site-key';
236+
auth._agentRecaptchaConfig!.siteKey = 'wrong-site-key';
237237
await auth.initializeRecaptchaConfig();
238238

239239
const idTokenResponse = await credential._getIdTokenResponse(auth);
@@ -252,7 +252,7 @@ describe('core/credentials/email', () => {
252252
});
253253

254254
it('calls sign in with password with recaptcha verify failed', async () => {
255-
RecaptchaEnterpriseVerifier.agentSiteKey = null;
255+
auth._agentRecaptchaConfig = null;
256256
mockEndpointWithParams(
257257
Endpoint.GET_RECAPTCHA_CONFIG,
258258
{

packages/auth/src/core/credentials/email.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ import {
2929
import { AuthInternal } from '../../model/auth';
3030
import { IdTokenResponse } from '../../model/id_token';
3131
import { AuthErrorCode } from '../errors';
32-
import { ServerError } from '../../api/errors';
3332
import { _fail } from '../util/assert';
3433
import { AuthCredential } from './auth_credential';
3534
import { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
36-
import { RecaptchaActionName } from '../../api';
35+
import { RecaptchaActionName, RecaptchaClientType } from '../../api';
3736
/**
3837
* Interface that represents the credentials returned by {@link EmailAuthProvider} for
3938
* {@link ProviderId}.PASSWORD
@@ -121,7 +120,8 @@ export class EmailAuthCredential extends AuthCredential {
121120
const request: SignInWithPasswordRequest = {
122121
returnSecureToken: true,
123122
email: this._email,
124-
password: this._password
123+
password: this._password,
124+
clientType: RecaptchaClientType.WEB
125125
};
126126
if (auth._getRecaptchaConfig()?.emailPasswordEnabled) {
127127
const requestWithRecaptcha = await injectRecaptchaFields(
@@ -132,7 +132,9 @@ export class EmailAuthCredential extends AuthCredential {
132132
return signInWithPassword(auth, requestWithRecaptcha);
133133
} else {
134134
return signInWithPassword(auth, request).catch(async error => {
135-
if (error.code === `auth/${ServerError.MISSING_RECAPTCHA_TOKEN}`) {
135+
if (
136+
error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`
137+
) {
136138
console.log(
137139
'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.'
138140
);

packages/auth/src/core/errors.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,14 @@ export const enum AuthErrorCode {
124124
WEAK_PASSWORD = 'weak-password',
125125
WEB_STORAGE_UNSUPPORTED = 'web-storage-unsupported',
126126
ALREADY_INITIALIZED = 'already-initialized',
127-
RECAPTCHA_CHECK_FAILED = 'recaptcha-check-failed',
128-
RECAPTCHA_NOT_ENABLED = 'recaptcha-not-enabled'
127+
RECAPTCHA_NOT_ENABLED = 'recaptcha-not-enabled',
128+
MISSING_RECAPTCHA_TOKEN = 'missing-recaptcha-token',
129+
INVALID_RECAPTCHA_TOKEN = 'invalid-recaptcha-token',
130+
INVALID_RECAPTCHA_ACTION = 'invalid-recaptcha-action',
131+
MISSING_CLIENT_TYPE = 'missing-client-type',
132+
MISSING_RECAPTCHA_VERSION = 'missing-recaptcha-version',
133+
INVALID_RECAPTCHA_VERSION = 'invalid-recaptcha-version',
134+
INVALID_REQ_TYPE = 'invalid-req-type'
129135
}
130136

131137
function _debugErrorMap(): ErrorMap<AuthErrorCode> {
@@ -359,10 +365,22 @@ function _debugErrorMap(): ErrorMap<AuthErrorCode> {
359365
'different options. To avoid this error, call initializeAuth() with the ' +
360366
'same options as when it was originally called, or call getAuth() to return the' +
361367
' already initialized instance.',
362-
[AuthErrorCode.RECAPTCHA_CHECK_FAILED]:
363-
'The ReCAPTCHA assessment failed for this request.',
368+
[AuthErrorCode.MISSING_RECAPTCHA_TOKEN]:
369+
'The reCAPTCHA token is missing when sending request to the backend.',
370+
[AuthErrorCode.INVALID_RECAPTCHA_TOKEN]:
371+
'The reCAPTCHA token is invalid when sending request to the backend.',
372+
[AuthErrorCode.INVALID_RECAPTCHA_ACTION]:
373+
'The reCAPTCHA action is invalid when sending request to the backend.',
364374
[AuthErrorCode.RECAPTCHA_NOT_ENABLED]:
365-
'reCAPTCHA integration is not enabled for this project.'
375+
'reCAPTCHA integration is not enabled for this project.',
376+
[AuthErrorCode.MISSING_CLIENT_TYPE]:
377+
'The reCAPTCHA client type is missing when sending request to the backend.',
378+
[AuthErrorCode.MISSING_RECAPTCHA_VERSION]:
379+
'The reCAPTCHA version is missing when sending request to the backend.',
380+
[AuthErrorCode.INVALID_REQ_TYPE]:
381+
'The reCAPTCHA client type or version is invalid when retrieving the site key.',
382+
[AuthErrorCode.INVALID_RECAPTCHA_VERSION]:
383+
'The reCAPTCHA version is invalid when sending request to the backend.'
366384
};
367385
}
368386

@@ -565,6 +583,12 @@ export const AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY = {
565583
WEAK_PASSWORD: 'auth/weak-password',
566584
WEB_STORAGE_UNSUPPORTED: 'auth/web-storage-unsupported',
567585
ALREADY_INITIALIZED: 'auth/already-initialized',
568-
RECAPTCHA_CHECK_FAILED: 'auth/recaptcha-check-failed',
569-
RECAPTCHA_NOT_ENABLED: 'auth/recaptcha-not-enabled'
586+
RECAPTCHA_NOT_ENABLED: 'auth/recaptcha-not-enabled',
587+
MISSING_RECAPTCHA_TOKEN: 'auth/missing-recaptcha-token',
588+
INVALID_RECAPTCHA_TOKEN: 'auth/invalid-recaptcha-token',
589+
INVALID_RECAPTCHA_ACTION: 'auth/invalid-recaptcha-action',
590+
MISSING_CLIENT_TYPE: 'auth/missing-client-type',
591+
MISSING_RECAPTCHA_VERSION: 'auth/missing-recaptcha-version',
592+
INVALID_RECAPTCHA_VERSION: 'auth/invalid-recaptcha-version',
593+
INVALID_REQ_TYPE: 'auth/invalid-req-type'
570594
} as const;

packages/auth/src/core/strategies/email_and_password.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,11 @@ import { _setActionCodeSettingsOnRequest } from './action_code_settings';
3434
import { signInWithCredential } from './credential';
3535
import { _castAuth } from '../auth/auth_impl';
3636
import { AuthErrorCode } from '../errors';
37-
import { ServerError } from '../../api/errors';
3837
import { getModularInstance } from '@firebase/util';
3938
import { OperationType } from '../../model/enums';
4039
import { injectRecaptchaFields } from '../../platform_browser/recaptcha/recaptcha_enterprise_verifier';
4140
import { IdTokenResponse } from '../../model/id_token';
42-
import { RecaptchaActionName } from '../../api';
41+
import { RecaptchaActionName, RecaptchaClientType } from '../../api';
4342

4443
/**
4544
* Sends a password reset email to the given email address.
@@ -81,7 +80,8 @@ export async function sendPasswordResetEmail(
8180
const authInternal = _castAuth(auth);
8281
const request: authentication.PasswordResetRequest = {
8382
requestType: ActionCodeOperation.PASSWORD_RESET,
84-
email
83+
email,
84+
clientType: RecaptchaClientType.WEB
8585
};
8686
if (authInternal._getRecaptchaConfig()?.emailPasswordEnabled) {
8787
const requestWithRecaptcha = await injectRecaptchaFields(
@@ -112,7 +112,7 @@ export async function sendPasswordResetEmail(
112112
await authentication
113113
.sendPasswordResetEmail(authInternal, request)
114114
.catch(async error => {
115-
if (error.code === `auth/${ServerError.MISSING_RECAPTCHA_TOKEN}`) {
115+
if (error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`) {
116116
console.log(
117117
'Password resets are protected by reCAPTCHA for this project. Automatically triggering the reCAPTCHA flow and restarting the password reset flow.'
118118
);
@@ -284,7 +284,8 @@ export async function createUserWithEmailAndPassword(
284284
const request: SignUpRequest = {
285285
returnSecureToken: true,
286286
email,
287-
password
287+
password,
288+
clientType: RecaptchaClientType.WEB
288289
};
289290
let signUpResponse: Promise<IdTokenResponse>;
290291
if (authInternal._getRecaptchaConfig()?.emailPasswordEnabled) {
@@ -296,7 +297,7 @@ export async function createUserWithEmailAndPassword(
296297
signUpResponse = signUp(authInternal, requestWithRecaptcha);
297298
} else {
298299
signUpResponse = signUp(authInternal, request).catch(async error => {
299-
if (error.code === `auth/${ServerError.MISSING_RECAPTCHA_TOKEN}`) {
300+
if (error.code === `auth/${AuthErrorCode.MISSING_RECAPTCHA_TOKEN}`) {
300301
console.log(
301302
'Sign-up is protected by reCAPTCHA for this project. Automatically triggering the reCAPTCHA flow and restarting the sign-up flow.'
302303
);

packages/auth/src/core/strategies/email_link.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ describe('core/strategies/sendSignInLinkToEmail', () => {
266266
}
267267
);
268268

269-
RecaptchaEnterpriseVerifier.agentSiteKey = 'wrong-site-key';
269+
auth._agentRecaptchaConfig!.siteKey = 'wrong-site-key';
270270
mockEndpointWithParams(
271271
Endpoint.GET_RECAPTCHA_CONFIG,
272272
{
@@ -292,7 +292,7 @@ describe('core/strategies/sendSignInLinkToEmail', () => {
292292
});
293293

294294
it('calls send sign in link to email with recaptcha verify failed', async () => {
295-
RecaptchaEnterpriseVerifier.agentSiteKey = null;
295+
auth._agentRecaptchaConfig = null;
296296
mockEndpointWithParams(
297297
Endpoint.GET_RECAPTCHA_CONFIG,
298298
{

0 commit comments

Comments
 (0)