@react-native-firebase/auth 21.14.0 → 22.1.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,34 @@
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
+ ## [22.1.0](https://github.com/invertase/react-native-firebase/compare/v22.0.0...v22.1.0) (2025-04-30)
7
+
8
+ ### Features
9
+
10
+ - **auth, expo:** add support for AppDelegate.swift ([e4966ef](https://github.com/invertase/react-native-firebase/commit/e4966ef3b692b3b8a3b89fbf54a0a0d59ca06ab6))
11
+
12
+ ### Bug Fixes
13
+
14
+ - **auth, android:** catch native error in signWithEmailLink ([a08580e](https://github.com/invertase/react-native-firebase/commit/a08580e29d672af99e890a60dbef8aba094b697c))
15
+ - **auth:** delegate modular `multiFactor` to MultiFactorUser constructor ([0937c77](https://github.com/invertase/react-native-firebase/commit/0937c77c9642da082de34de39cbf6a1847ae065c))
16
+
17
+ ## [22.0.0](https://github.com/invertase/react-native-firebase/compare/v21.14.0...v22.0.0) (2025-04-25)
18
+
19
+ ### ⚠ BREAKING CHANGES
20
+
21
+ - **auth:** return type of auth isSignInWithEmailLink changed from boolean to Promise<boolean>
22
+
23
+ ### Features
24
+
25
+ - **auth:** use native isSignInWithEmailLink - returns Promise<boolean> vs boolean ([#8450](https://github.com/invertase/react-native-firebase/issues/8450)) ([3785690](https://github.com/invertase/react-native-firebase/commit/37856901323d81ee8b232bfa0279b6606dbf366d))
26
+
27
+ ### Bug Fixes
28
+
29
+ - **analytics:** Type defs ([#8363](https://github.com/invertase/react-native-firebase/issues/8363)) ([74efc84](https://github.com/invertase/react-native-firebase/commit/74efc848e3ecda47b5c7cdf79e5e72370cd10b7d))
30
+ - **android:** use `=` assignment vs deprecated space-assignment ([39c2ecb](https://github.com/invertase/react-native-firebase/commit/39c2ecb0069a8a5a65b04fb7f86ccecf83273868))
31
+ - **auth:** Make REVERSED_CLIENT_ID optional ([#8470](https://github.com/invertase/react-native-firebase/issues/8470)) ([a909d1b](https://github.com/invertase/react-native-firebase/commit/a909d1beb597dcb92035a2ee6e66995c3aee8a5a))
32
+ - enable provenance signing during publish ([4535f0d](https://github.com/invertase/react-native-firebase/commit/4535f0d5756c89aeb8f8e772348c71d8176348be))
33
+
6
34
  ## [21.14.0](https://github.com/invertase/react-native-firebase/compare/v21.13.0...v21.14.0) (2025-04-14)
7
35
 
8
36
  ### Bug Fixes
@@ -61,20 +61,20 @@ project.ext {
61
61
  android {
62
62
  def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0].toInteger()
63
63
  if (agpVersion >= 7) {
64
- namespace 'io.invertase.firebase.auth'
64
+ namespace = 'io.invertase.firebase.auth'
65
65
  }
66
66
 
67
67
  defaultConfig {
68
- multiDexEnabled true
68
+ multiDexEnabled = true
69
69
  }
70
70
  lintOptions {
71
71
  disable 'GradleCompatible'
72
- abortOnError false
72
+ abortOnError = false
73
73
  }
74
74
  if (agpVersion < 8) {
75
75
  compileOptions {
76
- sourceCompatibility JavaVersion.VERSION_11
77
- targetCompatibility JavaVersion.VERSION_11
76
+ sourceCompatibility = JavaVersion.VERSION_11
77
+ targetCompatibility = JavaVersion.VERSION_11
78
78
  }
79
79
  }
80
80
  }
@@ -402,6 +402,20 @@ class ReactNativeFirebaseAuthModule extends ReactNativeFirebaseModule {
402
402
  });
403
403
  }
404
404
 
405
+ /**
406
+ * isSignInWithEmailLink
407
+ *
408
+ * @param email
409
+ * @param promise
410
+ */
411
+ @ReactMethod
412
+ public void isSignInWithEmailLink(String appName, String emailLink, final Promise promise) {
413
+ Log.d(TAG, "isSignInWithEmailLink");
414
+ FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
415
+ FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
416
+ promise.resolve(firebaseAuth.isSignInWithEmailLink(emailLink));
417
+ }
418
+
405
419
  /**
406
420
  * signInWithEmailAndPassword
407
421
  *
@@ -445,18 +459,23 @@ class ReactNativeFirebaseAuthModule extends ReactNativeFirebaseModule {
445
459
  FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
446
460
  FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
447
461
 
448
- firebaseAuth
449
- .signInWithEmailLink(email, emailLink)
450
- .addOnSuccessListener(
451
- authResult -> {
452
- Log.d(TAG, "signInWithEmailLink:onComplete:success");
453
- promiseWithAuthResult(authResult, promise);
454
- })
455
- .addOnFailureListener(
456
- exception -> {
457
- Log.e(TAG, "signInWithEmailLink:onComplete:failure", exception);
458
- promiseRejectAuthException(promise, exception);
459
- });
462
+ try {
463
+ firebaseAuth
464
+ .signInWithEmailLink(email, emailLink)
465
+ .addOnSuccessListener(
466
+ authResult -> {
467
+ Log.d(TAG, "signInWithEmailLink:onComplete:success");
468
+ promiseWithAuthResult(authResult, promise);
469
+ })
470
+ .addOnFailureListener(
471
+ exception -> {
472
+ Log.e(TAG, "signInWithEmailLink:onComplete:failure", exception);
473
+ promiseRejectAuthException(promise, exception);
474
+ });
475
+ } catch (Exception exception) {
476
+ Log.e(TAG, "signInWithEmailLink:onComplete:totalfailure", exception);
477
+ promiseRejectAuthException(promise, exception);
478
+ }
460
479
  }
461
480
 
462
481
  @ReactMethod
@@ -254,6 +254,15 @@ RCT_EXPORT_METHOD(signInWithEmailAndPassword
254
254
  }];
255
255
  }
256
256
 
257
+ RCT_EXPORT_METHOD(isSignInWithEmailLink
258
+ : (FIRApp *)firebaseApp
259
+ : (NSString *)emailLink
260
+ : (RCTPromiseResolveBlock)resolve
261
+ : (RCTPromiseRejectBlock)reject) {
262
+ resolve(
263
+ @([RCTConvert BOOL:@([[FIRAuth authWithApp:firebaseApp] isSignInWithEmailLink:emailLink])]));
264
+ }
265
+
257
266
  RCT_EXPORT_METHOD(signInWithEmailLink
258
267
  : (FIRApp *)firebaseApp
259
268
  : (NSString *)email
package/lib/index.d.ts CHANGED
@@ -2008,17 +2008,18 @@ export namespace FirebaseAuthTypes {
2008
2008
  sendSignInLinkToEmail(email: string, actionCodeSettings?: ActionCodeSettings): Promise<void>;
2009
2009
 
2010
2010
  /**
2011
- * Returns whether the user signed in with a given email link.
2011
+ * Checks if an incoming link is a sign-in with email link suitable for signInWithEmailLink.
2012
+ * Note that android and other platforms require `apiKey` link parameter for signInWithEmailLink
2012
2013
  *
2013
2014
  * #### Example
2014
2015
  *
2015
2016
  * ```js
2016
- * const signedInWithLink = firebase.auth().isSignInWithEmailLink(link);
2017
+ * const valid = await firebase.auth().isSignInWithEmailLink(link);
2017
2018
  * ```
2018
2019
  *
2019
- * @param emailLink The email link to check whether the user signed in with it.
2020
+ * @param emailLink The email link to verify prior to using signInWithEmailLink
2020
2021
  */
2021
- isSignInWithEmailLink(emailLink: string): boolean;
2022
+ isSignInWithEmailLink(emailLink: string): Promise<boolean>;
2022
2023
 
2023
2024
  /**
2024
2025
  * Signs the user in with an email link.
package/lib/index.js CHANGED
@@ -361,11 +361,7 @@ class FirebaseAuthModule extends FirebaseModule {
361
361
  }
362
362
 
363
363
  isSignInWithEmailLink(emailLink) {
364
- return (
365
- typeof emailLink === 'string' &&
366
- (emailLink.includes('mode=signIn') || emailLink.includes('mode%3DsignIn')) &&
367
- (emailLink.includes('oobCode=') || emailLink.includes('oobCode%3D'))
368
- );
364
+ return this.native.isSignInWithEmailLink(emailLink);
369
365
  }
370
366
 
371
367
  signInWithEmailLink(email, emailLink) {
@@ -165,13 +165,14 @@ export interface PopupRedirectResolver {}
165
165
  export function initializeRecaptchaConfig(auth: Auth): Promise<void>;
166
166
 
167
167
  /**
168
- * Checks if an incoming link is a sign-in with email link suitable for signInWithEmailLink().
168
+ * Checks if an incoming link is a sign-in with email link suitable for signInWithEmailLink.
169
+ * Note that android and other platforms require `apiKey` link parameter for signInWithEmailLink
169
170
  *
170
171
  * @param auth - The Auth instance.
171
172
  * @param emailLink - The email link to check.
172
- * @returns True if the link is a sign-in with email link.
173
+ * @returns A promise that resolves if the link is a sign-in with email link.
173
174
  */
174
- export function isSignInWithEmailLink(auth: Auth, emailLink: string): boolean;
175
+ export function isSignInWithEmailLink(auth: Auth, emailLink: string): Promise<boolean>;
175
176
 
176
177
  /**
177
178
  * Adds an observer for changes to the user's sign-in state.
@@ -19,6 +19,7 @@ import { getApp } from '@react-native-firebase/app';
19
19
  import { fetchPasswordPolicy } from '../password-policy/passwordPolicyApi';
20
20
  import { PasswordPolicyImpl } from '../password-policy/PasswordPolicyImpl';
21
21
  import FacebookAuthProvider from '../providers/FacebookAuthProvider';
22
+ import { MultiFactorUser } from '../multiFactor';
22
23
  export { FacebookAuthProvider };
23
24
 
24
25
  /**
@@ -165,7 +166,7 @@ export async function getRedirectResult(auth, resolver) {
165
166
  * Checks if an incoming link is a sign-in with email link suitable for signInWithEmailLink().
166
167
  * @param {Auth} auth - The Auth instance.
167
168
  * @param {string} emailLink - The email link to check.
168
- * @returns {boolean}
169
+ * @returns {Promise<boolean>}
169
170
  */
170
171
  export function isSignInWithEmailLink(auth, emailLink) {
171
172
  return auth.isSignInWithEmailLink(emailLink);
@@ -462,7 +463,7 @@ export async function linkWithRedirect(user, provider, resolver) {
462
463
  * @returns {MultiFactorUser}
463
464
  */
464
465
  export function multiFactor(user) {
465
- return user._auth.multiFactor(user);
466
+ return new MultiFactorUser(getAuth(), user);
466
467
  }
467
468
 
468
469
  /**
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- module.exports = '21.14.0';
2
+ module.exports = '22.1.0';
@@ -10,6 +10,7 @@ import {
10
10
  multiFactor,
11
11
  createUserWithEmailAndPassword,
12
12
  signInWithEmailAndPassword,
13
+ isSignInWithEmailLink,
13
14
  signInWithEmailLink,
14
15
  signInWithCustomToken,
15
16
  sendPasswordResetEmail,
@@ -230,20 +231,18 @@ function getCachedAuthInstance(appName) {
230
231
  // Warn auth persistence is is disabled unless Async Storage implementation is provided.
231
232
  // eslint-disable-next-line no-console
232
233
  console.warn(
233
- ```
234
- Firebase Auth persistence is disabled. To enable persistence, provide an Async Storage implementation.
235
-
236
- For example, to use React Native Async Storage:
237
-
238
- import AsyncStorage from '@react-native-async-storage/async-storage';
239
-
240
- // Before initializing Firebase set the Async Storage implementation
241
- // that will be used to persist user sessions.
242
- firebase.setReactNativeAsyncStorage(AsyncStorage);
243
-
244
- // Then initialize Firebase as normal.
245
- await firebase.initializeApp({ ... });
246
- ```,
234
+ 'Firebase Auth persistence is disabled. To enable persistence, provide an Async Storage implementation.\n' +
235
+ '\n' +
236
+ 'For example, to use React Native Async Storage:\n' +
237
+ '\n' +
238
+ " import AsyncStorage from '@react-native-async-storage/async-storage';\n" +
239
+ '\n' +
240
+ ' // Before initializing Firebase set the Async Storage implementation\n' +
241
+ ' // that will be used to persist user sessions.\n' +
242
+ ' firebase.setReactNativeAsyncStorage(AsyncStorage);\n' +
243
+ '\n' +
244
+ ' // Then initialize Firebase as normal.\n' +
245
+ ' await firebase.initializeApp({ ... });\n',
247
246
  );
248
247
  }
249
248
  instances[appName] = initializeAuth(getApp(appName), {
@@ -449,6 +448,19 @@ export default {
449
448
  });
450
449
  },
451
450
 
451
+ /**
452
+ * Check if a sign in with email link is valid
453
+ * @param {string} appName - The name of the app to get the auth instance for.
454
+ * @param {string} emailLink - The email link to sign in with.
455
+ * @returns {Promise<boolean>} - Whether the link is a valid sign in with email link.
456
+ */
457
+ async isSignInWithEmailLink(appName, emailLink) {
458
+ return guard(async () => {
459
+ const auth = getCachedAuthInstance(appName);
460
+ return await isSignInWithEmailLink(auth, emailLink);
461
+ });
462
+ },
463
+
452
464
  /**
453
465
  * Sign in with email link.
454
466
  * @param {string} appName - The name of the app to get the auth instance for.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-firebase/auth",
3
- "version": "21.14.0",
3
+ "version": "22.1.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": "21.14.0",
30
+ "@react-native-firebase/app": "22.1.0",
31
31
  "expo": ">=47.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/plist": "^3.0.5",
35
- "expo": "^52.0.35"
35
+ "expo": "^52.0.46"
36
36
  },
37
37
  "peerDependenciesMeta": {
38
38
  "expo": {
@@ -40,7 +40,8 @@
40
40
  }
41
41
  },
42
42
  "publishConfig": {
43
- "access": "public"
43
+ "access": "public",
44
+ "provenance": true
44
45
  },
45
- "gitHead": "7c7beaab9cf8a619927a33ef7a15b8000e2874c2"
46
+ "gitHead": "af6096ed9df878f0f169e899564b0aab68d396e5"
46
47
  }
@@ -12,9 +12,12 @@ export declare function withOpenUrlFixForAppDelegate({ config, props, }: {
12
12
  config: ExportedConfigWithProps<AppDelegateProjectFile>;
13
13
  props?: PluginConfigType;
14
14
  }): ExportedConfigWithProps<AppDelegateProjectFile>;
15
+ export declare function modifyAppDelegate(contents: string, language: string): string | null;
15
16
  export declare const appDelegateOpenUrlInsertionPointAfter: RegExp;
16
17
  export declare const multiline_appDelegateOpenUrlInsertionPointAfter: RegExp;
17
18
  export declare function modifyObjcAppDelegate(contents: string): string | null;
19
+ export declare const appDelegateSwiftOpenUrlInsertionPointAfter: RegExp;
20
+ export declare function modifySwiftAppDelegate(contents: string): string | null;
18
21
  export type ExpoConfigPluginEntry = string | [] | [string] | [string, any];
19
22
  export declare function isFirebaseSwizzlingDisabled(config: ExportedConfigWithProps<InfoPlist>): boolean;
20
23
  export declare function ensureFirebaseSwizzlingIsEnabled(config: ExportedConfigWithProps<InfoPlist>): boolean;
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.multiline_appDelegateOpenUrlInsertionPointAfter = exports.appDelegateOpenUrlInsertionPointAfter = exports.withIosCaptchaOpenUrlFix = void 0;
3
+ exports.appDelegateSwiftOpenUrlInsertionPointAfter = exports.multiline_appDelegateOpenUrlInsertionPointAfter = exports.appDelegateOpenUrlInsertionPointAfter = exports.withIosCaptchaOpenUrlFix = void 0;
4
4
  exports.shouldApplyIosOpenUrlFix = shouldApplyIosOpenUrlFix;
5
5
  exports.withOpenUrlFixForAppDelegate = withOpenUrlFixForAppDelegate;
6
+ exports.modifyAppDelegate = modifyAppDelegate;
6
7
  exports.modifyObjcAppDelegate = modifyObjcAppDelegate;
8
+ exports.modifySwiftAppDelegate = modifySwiftAppDelegate;
7
9
  exports.isFirebaseSwizzlingDisabled = isFirebaseSwizzlingDisabled;
8
10
  exports.ensureFirebaseSwizzlingIsEnabled = ensureFirebaseSwizzlingIsEnabled;
9
11
  const config_plugins_1 = require("@expo/config-plugins");
@@ -42,32 +44,37 @@ function shouldApplyIosOpenUrlFix({ config, props, }) {
42
44
  function withOpenUrlFixForAppDelegate({ config, props, }) {
43
45
  const { language, contents } = config.modResults;
44
46
  const configValue = props?.ios?.captchaOpenUrlFix || 'default';
45
- if (['objc', 'objcpp'].includes(language)) {
46
- const newContents = modifyObjcAppDelegate(contents);
47
- if (newContents === null) {
48
- if (configValue === true) {
49
- throw new Error("Failed to apply iOS openURL fix because no 'openURL' method was found");
50
- }
51
- else {
52
- config_plugins_1.WarningAggregator.addWarningIOS('@react-native-firebase/auth', "Skipping iOS openURL fix because no 'openURL' method was found");
53
- return config;
54
- }
47
+ const newContents = modifyAppDelegate(contents, language);
48
+ if (newContents === null) {
49
+ if (configValue === true) {
50
+ throw new Error("Failed to apply iOS openURL fix because no 'openURL' method was found");
55
51
  }
56
52
  else {
57
- if (configValue === 'default') {
58
- config_plugins_1.WarningAggregator.addWarningIOS('@react-native-firebase/auth', 'modifying iOS AppDelegate openURL method to ignore firebaseauth reCAPTCHA redirect URLs');
59
- }
60
- return {
61
- ...config,
62
- modResults: {
63
- ...config.modResults,
64
- contents: newContents,
65
- },
66
- };
53
+ config_plugins_1.WarningAggregator.addWarningIOS('@react-native-firebase/auth', "Skipping iOS openURL fix because no 'openURL' method was found");
54
+ return config;
67
55
  }
68
56
  }
69
57
  else {
70
- // TODO: Support Swift
58
+ if (configValue === 'default') {
59
+ config_plugins_1.WarningAggregator.addWarningIOS('@react-native-firebase/auth', 'modifying iOS AppDelegate openURL method to ignore firebaseauth reCAPTCHA redirect URLs');
60
+ }
61
+ return {
62
+ ...config,
63
+ modResults: {
64
+ ...config.modResults,
65
+ contents: newContents,
66
+ },
67
+ };
68
+ }
69
+ }
70
+ function modifyAppDelegate(contents, language) {
71
+ if (language === 'objc' || language === 'objcpp') {
72
+ return modifyObjcAppDelegate(contents);
73
+ }
74
+ else if (language === 'swift') {
75
+ return modifySwiftAppDelegate(contents);
76
+ }
77
+ else {
71
78
  throw new Error(`Don't know how to apply openUrlFix to AppDelegate of language "${language}"`);
72
79
  }
73
80
  }
@@ -116,6 +123,35 @@ function modifyObjcAppDelegate(contents) {
116
123
  comment: '//',
117
124
  }).contents;
118
125
  }
126
+ // NOTE: `mergeContents()` doesn't support newlines for the `anchor` regex, so we have to replace it manually
127
+ const skipOpenUrlForFirebaseAuthBlockSwift = `\
128
+ // @generated begin @react-native-firebase/auth-openURL - expo prebuild (DO NOT MODIFY)
129
+ if url.host.toLowerCase() == "firebaseauth" {
130
+ // invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
131
+ return false
132
+ }
133
+ // @generated end @react-native-firebase/auth-openURL\
134
+ `;
135
+ exports.appDelegateSwiftOpenUrlInsertionPointAfter = /public\s*override\s*func\s*application\s*\(\n\s*_\s*app\s*:\s*UIApplication,\n\s*open\s*url\s*:\s*URL,\n\s*options\s*:\s*\[UIApplication\.OpenURLOptionsKey\s*:\s*Any\]\s*=\s*\[:\]\n\s*\)\s*->\s*Bool\s*{\n/;
136
+ function modifySwiftAppDelegate(contents) {
137
+ const pattern = exports.appDelegateSwiftOpenUrlInsertionPointAfter;
138
+ const fullMatch = contents.match(pattern);
139
+ if (!fullMatch) {
140
+ if (contents.match(/open url\s*:/)) {
141
+ throw new Error([
142
+ "Failed to apply 'captchaOpenUrlFix' but detected 'openURL' method.",
143
+ "Please manually apply the fix to your AppDelegate's openURL method,",
144
+ "then update your app.config.json by configuring the '@react-native-firebase/auth' plugin",
145
+ 'to set `captchaOpenUrlFix: false`.',
146
+ ].join(' '));
147
+ }
148
+ else {
149
+ // openURL method was not found in AppDelegate
150
+ return null;
151
+ }
152
+ }
153
+ return contents.replace(pattern, `${fullMatch[0]}${skipOpenUrlForFirebaseAuthBlockSwift}\n`);
154
+ }
119
155
  // Search the ExpoConfig plugins array to see if `pluginName` is present
120
156
  function isPluginEnabled(config, pluginName) {
121
157
  if (config.plugins === undefined) {
@@ -21,13 +21,21 @@ function getReversedClientId(googleServiceFilePath) {
21
21
  const googleServicePlist = fs_1.default.readFileSync(googleServiceFilePath, 'utf8');
22
22
  const googleServiceJson = plist_1.default.parse(googleServicePlist);
23
23
  const REVERSED_CLIENT_ID = googleServiceJson.REVERSED_CLIENT_ID;
24
- if (!REVERSED_CLIENT_ID) {
25
- throw new TypeError('REVERSED_CLIENT_ID missing');
26
- }
27
24
  return REVERSED_CLIENT_ID;
28
25
  }
29
26
  catch {
30
- throw new Error('[@react-native-firebase/auth] Failed to parse your GoogleService-Info.plist. Are you sure it is a valid Info.Plist file with a REVERSE_CLIENT_ID field?');
27
+ throw new Error('[@react-native-firebase/auth] Failed to parse your GoogleService-Info.plist. Are you sure it is a valid Info.Plist file with a REVERSED_CLIENT_ID field?');
28
+ }
29
+ }
30
+ // Utility function to make REVERSED_CLIENT_ID optional by only proceeding if it exists in Google-Services.plist
31
+ function reversedClientIDExists(googleServiceFilePath) {
32
+ try {
33
+ const googleServicePlist = fs_1.default.readFileSync(googleServiceFilePath, 'utf8');
34
+ const googleServiceJson = plist_1.default.parse(googleServicePlist);
35
+ return !!googleServiceJson.REVERSED_CLIENT_ID;
36
+ }
37
+ catch {
38
+ return false;
31
39
  }
32
40
  }
33
41
  // add phone auth support by configuring recaptcha
@@ -56,7 +64,13 @@ function setUrlTypesForCaptcha({ config, }) {
56
64
  if (!fs_1.default.existsSync(googleServiceFilePath)) {
57
65
  throw new Error(`[@react-native-firebase/auth] GoogleService-Info.plist doesn't exist in ${googleServiceFilePath}. Place it there or configure the path in app.json`);
58
66
  }
59
- const reversedClientId = getReversedClientId(googleServiceFilePath);
60
- addUriScheme(config, reversedClientId);
67
+ if (reversedClientIDExists(googleServiceFilePath)) {
68
+ const reversedClientId = getReversedClientId(googleServiceFilePath);
69
+ addUriScheme(config, reversedClientId);
70
+ }
71
+ else {
72
+ // eslint-disable-next-line no-console
73
+ console.warn('[@react-native-firebase/auth] REVERSED_CLIENT_ID field not found in GoogleServices-Info.plist. Google Sign-In requires this is - if you need Google Sign-In, enable it and re-download your plist file');
74
+ }
61
75
  return config;
62
76
  }
@@ -62,35 +62,40 @@ export function withOpenUrlFixForAppDelegate({
62
62
  const { language, contents } = config.modResults;
63
63
  const configValue = props?.ios?.captchaOpenUrlFix || 'default';
64
64
 
65
- if (['objc', 'objcpp'].includes(language)) {
66
- const newContents = modifyObjcAppDelegate(contents);
67
- if (newContents === null) {
68
- if (configValue === true) {
69
- throw new Error("Failed to apply iOS openURL fix because no 'openURL' method was found");
70
- } else {
71
- WarningAggregator.addWarningIOS(
72
- '@react-native-firebase/auth',
73
- "Skipping iOS openURL fix because no 'openURL' method was found",
74
- );
75
- return config;
76
- }
65
+ const newContents = modifyAppDelegate(contents, language);
66
+ if (newContents === null) {
67
+ if (configValue === true) {
68
+ throw new Error("Failed to apply iOS openURL fix because no 'openURL' method was found");
77
69
  } else {
78
- if (configValue === 'default') {
79
- WarningAggregator.addWarningIOS(
80
- '@react-native-firebase/auth',
81
- 'modifying iOS AppDelegate openURL method to ignore firebaseauth reCAPTCHA redirect URLs',
82
- );
83
- }
84
- return {
85
- ...config,
86
- modResults: {
87
- ...config.modResults,
88
- contents: newContents,
89
- },
90
- };
70
+ WarningAggregator.addWarningIOS(
71
+ '@react-native-firebase/auth',
72
+ "Skipping iOS openURL fix because no 'openURL' method was found",
73
+ );
74
+ return config;
75
+ }
76
+ } else {
77
+ if (configValue === 'default') {
78
+ WarningAggregator.addWarningIOS(
79
+ '@react-native-firebase/auth',
80
+ 'modifying iOS AppDelegate openURL method to ignore firebaseauth reCAPTCHA redirect URLs',
81
+ );
91
82
  }
83
+ return {
84
+ ...config,
85
+ modResults: {
86
+ ...config.modResults,
87
+ contents: newContents,
88
+ },
89
+ };
90
+ }
91
+ }
92
+
93
+ export function modifyAppDelegate(contents: string, language: string): string | null {
94
+ if (language === 'objc' || language === 'objcpp') {
95
+ return modifyObjcAppDelegate(contents);
96
+ } else if (language === 'swift') {
97
+ return modifySwiftAppDelegate(contents);
92
98
  } else {
93
- // TODO: Support Swift
94
99
  throw new Error(`Don't know how to apply openUrlFix to AppDelegate of language "${language}"`);
95
100
  }
96
101
  }
@@ -147,6 +152,40 @@ export function modifyObjcAppDelegate(contents: string): string | null {
147
152
  }).contents;
148
153
  }
149
154
 
155
+ // NOTE: `mergeContents()` doesn't support newlines for the `anchor` regex, so we have to replace it manually
156
+ const skipOpenUrlForFirebaseAuthBlockSwift: string = `\
157
+ // @generated begin @react-native-firebase/auth-openURL - expo prebuild (DO NOT MODIFY)
158
+ if url.host.toLowerCase() == "firebaseauth" {
159
+ // invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
160
+ return false
161
+ }
162
+ // @generated end @react-native-firebase/auth-openURL\
163
+ `;
164
+
165
+ export const appDelegateSwiftOpenUrlInsertionPointAfter: RegExp =
166
+ /public\s*override\s*func\s*application\s*\(\n\s*_\s*app\s*:\s*UIApplication,\n\s*open\s*url\s*:\s*URL,\n\s*options\s*:\s*\[UIApplication\.OpenURLOptionsKey\s*:\s*Any\]\s*=\s*\[:\]\n\s*\)\s*->\s*Bool\s*{\n/;
167
+
168
+ export function modifySwiftAppDelegate(contents: string): string | null {
169
+ const pattern = appDelegateSwiftOpenUrlInsertionPointAfter;
170
+ const fullMatch = contents.match(pattern);
171
+ if (!fullMatch) {
172
+ if (contents.match(/open url\s*:/)) {
173
+ throw new Error(
174
+ [
175
+ "Failed to apply 'captchaOpenUrlFix' but detected 'openURL' method.",
176
+ "Please manually apply the fix to your AppDelegate's openURL method,",
177
+ "then update your app.config.json by configuring the '@react-native-firebase/auth' plugin",
178
+ 'to set `captchaOpenUrlFix: false`.',
179
+ ].join(' '),
180
+ );
181
+ } else {
182
+ // openURL method was not found in AppDelegate
183
+ return null;
184
+ }
185
+ }
186
+ return contents.replace(pattern, `${fullMatch[0]}${skipOpenUrlForFirebaseAuthBlockSwift}\n`);
187
+ }
188
+
150
189
  export type ExpoConfigPluginEntry = string | [] | [string] | [string, any];
151
190
 
152
191
  // Search the ExpoConfig plugins array to see if `pluginName` is present
@@ -23,18 +23,25 @@ function getReversedClientId(googleServiceFilePath: string): string {
23
23
  const googleServiceJson = plist.parse(googleServicePlist) as { REVERSED_CLIENT_ID: string };
24
24
  const REVERSED_CLIENT_ID = googleServiceJson.REVERSED_CLIENT_ID;
25
25
 
26
- if (!REVERSED_CLIENT_ID) {
27
- throw new TypeError('REVERSED_CLIENT_ID missing');
28
- }
29
-
30
26
  return REVERSED_CLIENT_ID;
31
27
  } catch {
32
28
  throw new Error(
33
- '[@react-native-firebase/auth] Failed to parse your GoogleService-Info.plist. Are you sure it is a valid Info.Plist file with a REVERSE_CLIENT_ID field?',
29
+ '[@react-native-firebase/auth] Failed to parse your GoogleService-Info.plist. Are you sure it is a valid Info.Plist file with a REVERSED_CLIENT_ID field?',
34
30
  );
35
31
  }
36
32
  }
37
33
 
34
+ // Utility function to make REVERSED_CLIENT_ID optional by only proceeding if it exists in Google-Services.plist
35
+ function reversedClientIDExists(googleServiceFilePath: string): boolean {
36
+ try {
37
+ const googleServicePlist = fs.readFileSync(googleServiceFilePath, 'utf8');
38
+ const googleServiceJson = plist.parse(googleServicePlist) as { REVERSED_CLIENT_ID: string };
39
+ return !!googleServiceJson.REVERSED_CLIENT_ID;
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+
38
45
  // add phone auth support by configuring recaptcha
39
46
  // https://github.com/invertase/react-native-firebase/pull/6167
40
47
  function addUriScheme(
@@ -84,8 +91,15 @@ export function setUrlTypesForCaptcha({
84
91
  );
85
92
  }
86
93
 
87
- const reversedClientId = getReversedClientId(googleServiceFilePath);
88
- addUriScheme(config, reversedClientId);
94
+ if (reversedClientIDExists(googleServiceFilePath)) {
95
+ const reversedClientId = getReversedClientId(googleServiceFilePath);
96
+ addUriScheme(config, reversedClientId);
97
+ } else {
98
+ // eslint-disable-next-line no-console
99
+ console.warn(
100
+ '[@react-native-firebase/auth] REVERSED_CLIENT_ID field not found in GoogleServices-Info.plist. Google Sign-In requires this is - if you need Google Sign-In, enable it and re-download your plist file',
101
+ );
102
+ }
89
103
 
90
104
  return config;
91
105
  }
@@ -1 +1 @@
1
- {"root":["./src/index.ts","./src/pluginConfig.ts","./src/ios/index.ts","./src/ios/openUrlFix.ts","./src/ios/urlTypes.ts"],"version":"5.7.3"}
1
+ {"root":["./src/index.ts","./src/pluginConfig.ts","./src/ios/index.ts","./src/ios/openUrlFix.ts","./src/ios/urlTypes.ts"],"version":"5.8.3"}