@react-native-firebase/auth 23.5.0 → 23.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [23.7.0](https://github.com/invertase/react-native-firebase/compare/v23.6.0...v23.7.0) (2025-12-08)
7
+
8
+ ### Features
9
+
10
+ - **auth:** cache password policy per-app in validatePassword ([cc29117](https://github.com/invertase/react-native-firebase/commit/cc291171e076d00dfde1fef9d03242379e567af4))
11
+
12
+ ## [23.6.0](https://github.com/invertase/react-native-firebase/compare/v23.5.0...v23.6.0) (2025-12-08)
13
+
14
+ ### Bug Fixes
15
+
16
+ - **auth, ios:** append openURL changes only once in Expo plugin ([#8763](https://github.com/invertase/react-native-firebase/issues/8763)) ([5f74256](https://github.com/invertase/react-native-firebase/commit/5f74256d2b470456206a3655ff6986198d016790))
17
+
6
18
  ## [23.5.0](https://github.com/invertase/react-native-firebase/compare/v23.4.1...v23.5.0) (2025-10-30)
7
19
 
8
20
  **Note:** Version bump only for package @react-native-firebase/auth
@@ -1,3 +1,4 @@
1
+ import com.android.Version
1
2
  import io.invertase.gradle.common.PackageJson
2
3
 
3
4
  buildscript {
@@ -36,11 +37,8 @@ def jsonTargetSdk = appPackageJson['sdkVersions']['android']['targetSdk']
36
37
  def jsonCompileSdk = appPackageJson['sdkVersions']['android']['compileSdk']
37
38
  def coreVersionDetected = appPackageJson['version']
38
39
  def coreVersionRequired = packageJson['peerDependencies'][appPackageJson['name']]
39
- // Only log after build completed so log warning appears at the end
40
40
  if (coreVersionDetected != coreVersionRequired) {
41
- gradle.buildFinished {
42
- project.logger.warn("ReactNativeFirebase WARNING: NPM package '${packageJson['name']}' depends on '${appPackageJson['name']}' v${coreVersionRequired} but found v${coreVersionDetected}, this might cause build issues or runtime crashes.")
43
- }
41
+ project.logger.warn("ReactNativeFirebase WARNING: NPM package '${packageJson['name']}' depends on '${appPackageJson['name']}' v${coreVersionRequired} but found v${coreVersionDetected}, this might cause build issues or runtime crashes.")
44
42
  }
45
43
 
46
44
  project.ext {
@@ -59,7 +57,7 @@ project.ext {
59
57
  }
60
58
 
61
59
  android {
62
- def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0].toInteger()
60
+ def agpVersion = Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0].toInteger()
63
61
  if (agpVersion >= 7) {
64
62
  namespace = 'io.invertase.firebase.auth'
65
63
  }
package/lib/index.js CHANGED
@@ -51,6 +51,7 @@ import TwitterAuthProvider from './providers/TwitterAuthProvider';
51
51
  import { TotpSecret } from './TotpSecret';
52
52
  import version from './version';
53
53
  import fallBackModule from './web/RNFBAuthModule';
54
+ import { PasswordPolicyMixin } from './password-policy/PasswordPolicyMixin';
54
55
 
55
56
  const PhoneAuthState = {
56
57
  CODE_SENT: 'sent',
@@ -103,6 +104,8 @@ class FirebaseAuthModule extends FirebaseModule {
103
104
  this._authResult = false;
104
105
  this._languageCode = this.native.APP_LANGUAGE[this.app._name];
105
106
  this._tenantId = null;
107
+ this._projectPasswordPolicy = null;
108
+ this._tenantPasswordPolicies = {};
106
109
 
107
110
  if (!this.languageCode) {
108
111
  this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]'];
@@ -336,15 +339,45 @@ class FirebaseAuthModule extends FirebaseModule {
336
339
  }
337
340
 
338
341
  createUserWithEmailAndPassword(email, password) {
339
- return this.native
340
- .createUserWithEmailAndPassword(email, password)
341
- .then(userCredential => this._setUserCredential(userCredential));
342
+ return (
343
+ this.native
344
+ .createUserWithEmailAndPassword(email, password)
345
+ .then(userCredential => this._setUserCredential(userCredential))
346
+ /* istanbul ignore next - native error handling cannot be unit tested */
347
+ .catch(error => {
348
+ if (error.code === 'auth/password-does-not-meet-requirements') {
349
+ return this._recachePasswordPolicy()
350
+ .catch(() => {
351
+ // Silently ignore recache failures - the original error matters more
352
+ })
353
+ .then(() => {
354
+ throw error;
355
+ });
356
+ }
357
+ throw error;
358
+ })
359
+ );
342
360
  }
343
361
 
344
362
  signInWithEmailAndPassword(email, password) {
345
- return this.native
346
- .signInWithEmailAndPassword(email, password)
347
- .then(userCredential => this._setUserCredential(userCredential));
363
+ return (
364
+ this.native
365
+ .signInWithEmailAndPassword(email, password)
366
+ .then(userCredential => this._setUserCredential(userCredential))
367
+ /* istanbul ignore next - native error handling cannot be unit tested */
368
+ .catch(error => {
369
+ if (error.code === 'auth/password-does-not-meet-requirements') {
370
+ return this._recachePasswordPolicy()
371
+ .catch(() => {
372
+ // Silently ignore recache failures - the original error matters more
373
+ })
374
+ .then(() => {
375
+ throw error;
376
+ });
377
+ }
378
+ throw error;
379
+ })
380
+ );
348
381
  }
349
382
 
350
383
  signInWithCustomToken(customToken) {
@@ -382,7 +415,23 @@ class FirebaseAuthModule extends FirebaseModule {
382
415
  }
383
416
 
384
417
  confirmPasswordReset(code, newPassword) {
385
- return this.native.confirmPasswordReset(code, newPassword);
418
+ return (
419
+ this.native
420
+ .confirmPasswordReset(code, newPassword)
421
+ /* istanbul ignore next - native error handling cannot be unit tested */
422
+ .catch(error => {
423
+ if (error.code === 'auth/password-does-not-meet-requirements') {
424
+ return this._recachePasswordPolicy()
425
+ .catch(() => {
426
+ // Silently ignore recache failures - the original error matters more
427
+ })
428
+ .then(() => {
429
+ throw error;
430
+ });
431
+ }
432
+ throw error;
433
+ })
434
+ );
386
435
  }
387
436
 
388
437
  applyActionCode(code) {
@@ -493,6 +542,9 @@ class FirebaseAuthModule extends FirebaseModule {
493
542
  }
494
543
  }
495
544
 
545
+ // Apply password policy mixin to FirebaseAuthModule
546
+ Object.assign(FirebaseAuthModule.prototype, PasswordPolicyMixin);
547
+
496
548
  // import { SDK_VERSION } from '@react-native-firebase/auth';
497
549
  export const SDK_VERSION = version;
498
550
 
@@ -16,8 +16,6 @@
16
16
  */
17
17
 
18
18
  import { getApp } from '@react-native-firebase/app';
19
- import { fetchPasswordPolicy } from '../password-policy/passwordPolicyApi';
20
- import { PasswordPolicyImpl } from '../password-policy/PasswordPolicyImpl';
21
19
  import { MultiFactorUser } from '../multiFactor';
22
20
  import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/lib/common';
23
21
 
@@ -637,15 +635,17 @@ export function getCustomAuthDomain(auth) {
637
635
  * @returns {Promise<PasswordValidationStatus>}
638
636
  */
639
637
  export async function validatePassword(auth, password) {
638
+ if (!auth || !auth.app) {
639
+ throw new Error(
640
+ "firebase.auth().validatePassword(*) 'auth' must be a valid Auth instance with an 'app' property. Received: undefined",
641
+ );
642
+ }
643
+
640
644
  if (password === null || password === undefined) {
641
645
  throw new Error(
642
646
  "firebase.auth().validatePassword(*) expected 'password' to be a non-null or a defined value.",
643
647
  );
644
648
  }
645
- let passwordPolicy = await fetchPasswordPolicy(auth);
646
-
647
- const passwordPolicyImpl = await new PasswordPolicyImpl(passwordPolicy);
648
- let status = passwordPolicyImpl.validatePassword(password);
649
649
 
650
- return status;
650
+ return auth.validatePassword.call(auth, password, MODULAR_DEPRECATION_ARG);
651
651
  }
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ import { fetchPasswordPolicy } from './passwordPolicyApi';
19
+ import { PasswordPolicyImpl } from './PasswordPolicyImpl';
20
+
21
+ const EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION = 1;
22
+
23
+ /**
24
+ * Password policy mixin - provides password policy caching and validation.
25
+ * Expects the target object to have: _tenantId, _projectPasswordPolicy,
26
+ * _tenantPasswordPolicies, and app.options.apiKey
27
+ */
28
+ export const PasswordPolicyMixin = {
29
+ _getPasswordPolicyInternal() {
30
+ if (this._tenantId === null) {
31
+ return this._projectPasswordPolicy;
32
+ }
33
+ return this._tenantPasswordPolicies[this._tenantId];
34
+ },
35
+
36
+ async _updatePasswordPolicy() {
37
+ const response = await fetchPasswordPolicy(this);
38
+ const passwordPolicy = new PasswordPolicyImpl(response);
39
+ if (this._tenantId === null) {
40
+ this._projectPasswordPolicy = passwordPolicy;
41
+ } else {
42
+ this._tenantPasswordPolicies[this._tenantId] = passwordPolicy;
43
+ }
44
+ },
45
+
46
+ async _recachePasswordPolicy() {
47
+ if (this._getPasswordPolicyInternal()) {
48
+ await this._updatePasswordPolicy();
49
+ }
50
+ },
51
+
52
+ async validatePassword(password) {
53
+ if (!this._getPasswordPolicyInternal()) {
54
+ await this._updatePasswordPolicy();
55
+ }
56
+ const passwordPolicy = this._getPasswordPolicyInternal();
57
+
58
+ if (passwordPolicy.schemaVersion !== EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION) {
59
+ throw new Error(
60
+ 'auth/unsupported-password-policy-schema-version: The password policy received from the backend uses a schema version that is not supported by this version of the SDK.',
61
+ );
62
+ }
63
+
64
+ return passwordPolicy.validatePassword(password);
65
+ },
66
+ };
@@ -23,8 +23,6 @@
23
23
  * @throws {Error} Throws an error if the request fails or encounters an issue.
24
24
  */
25
25
  export async function fetchPasswordPolicy(auth) {
26
- let schemaVersion = 1;
27
-
28
26
  try {
29
27
  // Identity toolkit API endpoint for password policy. Ensure this is enabled on Google cloud.
30
28
  const baseURL = 'https://identitytoolkit.googleapis.com/v2/passwordPolicy?key=';
@@ -37,19 +35,10 @@ export async function fetchPasswordPolicy(auth) {
37
35
  `firebase.auth().validatePassword(*) failed to fetch password policy from Firebase Console: ${response.statusText}. Details: ${errorDetails}`,
38
36
  );
39
37
  }
40
- const passwordPolicy = await response.json();
41
-
42
- if (passwordPolicy.schemaVersion !== schemaVersion) {
43
- throw new Error(
44
- `Password policy schema version mismatch. Expected: ${schemaVersion}, received: ${passwordPolicy.schemaVersion}`,
45
- );
46
- }
47
- return passwordPolicy;
38
+ return await response.json();
48
39
  } catch (error) {
49
40
  throw new Error(
50
41
  `firebase.auth().validatePassword(*) Failed to fetch password policy: ${error.message}`,
51
42
  );
52
43
  }
53
44
  }
54
-
55
- module.exports = { fetchPasswordPolicy };
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- module.exports = '23.5.0';
2
+ module.exports = '23.7.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-firebase/auth",
3
- "version": "23.5.0",
3
+ "version": "23.7.0",
4
4
  "author": "Invertase <oss@invertase.io> (http://invertase.io)",
5
5
  "description": "React Native Firebase - The authentication module provides an easy-to-use API to integrate an authentication workflow into new and existing applications. React Native Firebase provides access to all Firebase authentication methods and identity providers.",
6
6
  "main": "lib/index.js",
@@ -27,12 +27,12 @@
27
27
  "plist": "^3.1.0"
28
28
  },
29
29
  "peerDependencies": {
30
- "@react-native-firebase/app": "23.5.0",
30
+ "@react-native-firebase/app": "23.7.0",
31
31
  "expo": ">=47.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/plist": "^3.0.5",
35
- "expo": "^53.0.20"
35
+ "expo": "^54.0.27"
36
36
  },
37
37
  "peerDependenciesMeta": {
38
38
  "expo": {
@@ -43,5 +43,5 @@
43
43
  "access": "public",
44
44
  "provenance": true
45
45
  },
46
- "gitHead": "5d27948f349d4d6c977c55036e2afd13df8d622f"
46
+ "gitHead": "2a30c0b1e41a2b239172afd167f98a03fd422f2c"
47
47
  }
@@ -150,6 +150,10 @@ function modifySwiftAppDelegate(contents) {
150
150
  return null;
151
151
  }
152
152
  }
153
+ const isAlreadyModified = contents.includes(skipOpenUrlForFirebaseAuthBlockSwift);
154
+ if (isAlreadyModified) {
155
+ return null;
156
+ }
153
157
  return contents.replace(pattern, `${fullMatch[0]}${skipOpenUrlForFirebaseAuthBlockSwift}\n`);
154
158
  }
155
159
  // Search the ExpoConfig plugins array to see if `pluginName` is present
@@ -183,6 +183,10 @@ export function modifySwiftAppDelegate(contents: string): string | null {
183
183
  return null;
184
184
  }
185
185
  }
186
+ const isAlreadyModified = contents.includes(skipOpenUrlForFirebaseAuthBlockSwift);
187
+ if (isAlreadyModified) {
188
+ return null;
189
+ }
186
190
  return contents.replace(pattern, `${fullMatch[0]}${skipOpenUrlForFirebaseAuthBlockSwift}\n`);
187
191
  }
188
192
 
@@ -1 +1 @@
1
- {"root":["./src/index.ts","./src/pluginConfig.ts","./src/ios/index.ts","./src/ios/openUrlFix.ts","./src/ios/urlTypes.ts"],"version":"5.9.2"}
1
+ {"root":["./src/index.ts","./src/pluginConfig.ts","./src/ios/index.ts","./src/ios/openUrlFix.ts","./src/ios/urlTypes.ts"],"version":"5.9.3"}