Skip to content

Commit 7cf7c1a

Browse files
refactor(auth)!: migrate to TypeScript and bring auth closer in alignment with firebase-js-sdk API (#8991)
* refactor(auth): convert package sources to TypeScript * test(ci): run iOS e2e on macos-26 to match local dev * test(ios): target iPhone 17 simulator on macos-26 / Xcode latest * fix: deep PR review and related fixes for type gaps * fix(auth): final deep review pass, docs reorg/prep * test(ios): additional instrumentation and mitigation for e2e orchestration flakes * test: further e2e orchestration flake hardening Fix usesLiveMetro() when DETOX_CONFIGURATION is unset in the Jet child and retry the full Jet run after post-1006 session desync signals. * test: mitigate two more test-specific e2e flakes Harden RTDB listener e2e tests with native registration gates and make Firestore emulator wipe retries validate HTTP responses. * test: fix android coverage pull and adb reverse failure e2e flakes Move Android native coverage pull to a post-Detox CI step with polling, harden Detox adb reverse teardown, and make Jacoco/Codecov best-effort. * fix(analytics, ios): fix infinite getSessionId wait, harden related e2e test * test(android): use /mnt/avd for android emulator in e2e as intended --------- Co-authored-by: Mike Hardy <github@mikehardy.net>
1 parent 9bb4165 commit 7cf7c1a

96 files changed

Lines changed: 7613 additions & 3227 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* Known differences between the firebase-js-sdk @firebase/auth public
3+
* API and the @react-native-firebase/auth modular API.
4+
*
5+
* Each entry must have a `name` and a `reason`. Any undocumented
6+
* difference or stale entry will fail `yarn compare:types`.
7+
*
8+
* Platform shorthand used in reasons:
9+
* - iOS/Android: native Firebase Auth SDK
10+
* - Other/All: Other/Hermes + Other/Web (firebase-js-sdk JS bridge)
11+
* - Other/Hermes: react-native-macos, Windows RN, etc. (no DOM)
12+
* - Other/Web: true browser/DOM context
13+
*/
14+
15+
import type { PackageConfig } from '../src/types';
16+
17+
const config: PackageConfig = {
18+
nameMapping: {},
19+
20+
missingInRN: [
21+
{
22+
name: 'initializeRecaptchaConfig',
23+
reason:
24+
'iOS/Android: native SDKs own phone verification. Other/Hermes: not applicable (no DOM). Other/Web: not implemented yet; firebase-js-sdk support is possible.',
25+
},
26+
{
27+
name: 'AuthErrorCodes',
28+
reason:
29+
'iOS/Android: native auth error code strings. Other/All: not exported yet; firebase-js-sdk re-export is possible.',
30+
},
31+
{
32+
name: 'browserCookiePersistence',
33+
reason:
34+
'iOS/Android: not applicable. Other/Hermes: not applicable. Other/Web: not exported yet; firebase-js-sdk browser persistence is possible.',
35+
},
36+
{
37+
name: 'browserLocalPersistence',
38+
reason:
39+
'iOS/Android: not applicable. Other/Hermes: not applicable. Other/Web: not exported yet; firebase-js-sdk browser persistence is possible.',
40+
},
41+
{
42+
name: 'browserPopupRedirectResolver',
43+
reason:
44+
'iOS/Android: native provider flows. Other/Hermes: not applicable. Other/Web: not exported yet; firebase-js-sdk popup/redirect resolver is possible.',
45+
},
46+
{
47+
name: 'browserSessionPersistence',
48+
reason:
49+
'iOS/Android: not applicable. Other/Hermes: not applicable. Other/Web: not exported yet; firebase-js-sdk browser persistence is possible.',
50+
},
51+
{
52+
name: 'debugErrorMap',
53+
reason:
54+
'iOS/Android: not exported. Other/All: not implemented yet; firebase-js-sdk error-map selection via initializeAuth is possible.',
55+
},
56+
{
57+
name: 'indexedDBLocalPersistence',
58+
reason:
59+
'iOS/Android: not applicable. Other/Hermes: not applicable. Other/Web: not exported yet; firebase-js-sdk IndexedDB persistence is possible.',
60+
},
61+
{
62+
name: 'inMemoryPersistence',
63+
reason:
64+
'iOS/Android: native SDKs manage persistence. Other/All: not exported yet; firebase-js-sdk inMemoryPersistence via initializeAuth is possible.',
65+
},
66+
{
67+
name: 'prodErrorMap',
68+
reason:
69+
'iOS/Android: not exported. Other/All: not implemented yet; firebase-js-sdk error-map selection via initializeAuth is possible.',
70+
},
71+
{
72+
name: 'ReactNativeAsyncStorage',
73+
reason:
74+
'iOS/Android: persistence delegated to native SDKs. Other/Hermes: not exported yet; firebase-js-sdk React Native persistence via initializeAuth is possible.',
75+
},
76+
{
77+
name: 'RecaptchaParameters',
78+
reason:
79+
'iOS/Android: not applicable. Other/Hermes: not applicable. Other/Web: not exported; browser reCAPTCHA configuration only.',
80+
},
81+
{
82+
name: 'RecaptchaVerifier',
83+
reason:
84+
'iOS/Android: native verification. Other/Hermes: not applicable (no DOM). Other/Web: not implemented yet; firebase-js-sdk RecaptchaVerifier is possible.',
85+
},
86+
{
87+
name: 'SAMLAuthProvider',
88+
reason:
89+
'iOS/Android: not exported. Other/Hermes: not applicable. Other/Web: not exported yet; firebase-js-sdk SAML provider is possible.',
90+
},
91+
],
92+
93+
extraInRN: [
94+
{
95+
name: 'AppleAuthProvider',
96+
reason:
97+
'Deprecated RN Firebase helper for Sign in with Apple. Prefer OAuthProvider("apple.com"). Retained for compatibility; firebase-js-sdk uses OAuthProvider only.',
98+
},
99+
{
100+
name: 'NativeFirebaseAuthError',
101+
reason:
102+
'RN Firebase-specific native bridge auth error type used in place of the firebase-js-sdk AuthError export.',
103+
},
104+
{
105+
name: 'OIDCAuthProvider',
106+
reason:
107+
'Deprecated RN Firebase OIDC helper. Prefer OAuthProvider("oidc.<suffix>"). Retained for compatibility; firebase-js-sdk uses OAuthProvider only.',
108+
},
109+
{
110+
name: 'OIDCProvider',
111+
reason:
112+
'Deprecated with OIDCAuthProvider. Prefer OAuthProvider for OIDC flows.',
113+
},
114+
{
115+
name: 'PhoneAuthState',
116+
reason:
117+
'RN Firebase-specific enum-like object describing native phone-auth listener states. iOS/Android only; Other uses js-sdk phone flows instead of verifyPhoneNumber().',
118+
},
119+
{
120+
name: 'PhoneAuthListener',
121+
reason:
122+
'RN Firebase-specific listener object returned by verifyPhoneNumber(). iOS/Android only.',
123+
},
124+
{
125+
name: 'PhoneAuthError',
126+
reason:
127+
'RN Firebase-specific phone verification error snapshot type used by native phone-auth listeners. iOS/Android only.',
128+
},
129+
{
130+
name: 'PhoneAuthSnapshot',
131+
reason:
132+
'RN Firebase-specific phone verification snapshot type used by native phone-auth listeners. iOS/Android only.',
133+
},
134+
{
135+
name: 'verifyPhoneNumber',
136+
reason:
137+
'RN Firebase-specific native phone verification listener flow. iOS/Android only; Other/All: use signInWithPhoneNumber / PhoneAuthProvider via js-sdk instead.',
138+
},
139+
{
140+
name: 'setLanguageCode',
141+
reason:
142+
'RN Firebase modular helper for auth.languageCode. iOS/Android: native. Other/All: not delegated yet; firebase-js-sdk languageCode / useDeviceLanguage is possible.',
143+
},
144+
{
145+
name: 'useUserAccessGroup',
146+
reason:
147+
'RN Firebase-specific iOS keychain sharing helper. iOS native only.',
148+
},
149+
{
150+
name: 'getCustomAuthDomain',
151+
reason:
152+
'RN Firebase-specific native auth domain helper on iOS/Android. Other/All: auth.config from firebase-js-sdk may cover this instead (see auth.config in migration guide).',
153+
},
154+
{
155+
name: 'AdditionalUserInfoNative',
156+
reason:
157+
'RN Firebase extension type: firebase-js-sdk AdditionalUserInfo core fields plus index signature for extra native bridge keys. Use when typing values from getAdditionalUserInfo or UserCredential.additionalUserInfo that may include provider-specific native fields.',
158+
},
159+
],
160+
161+
differentShape: [
162+
{
163+
name: 'OAuthCredential',
164+
reason:
165+
'RN Firebase OAuthCredential exposes rawNonce for Apple / limited-login flows. OAuth 1.0 token secrets use the inherited AuthCredential.secret field (firebase-js-sdk optional secret on OAuthCredential).',
166+
},
167+
{
168+
name: 'FacebookAuthProvider',
169+
reason:
170+
'RN Firebase exports an extra credential(token, secret) overload for Facebook limited-login nonce behaviour. firebase-js-sdk public types only declare credential(accessToken). credentialFromResult / credentialFromError always return null at runtime today (types match js-sdk). Same runtime applies to GoogleAuthProvider, GithubAuthProvider, TwitterAuthProvider, OAuthProvider, and PhoneAuthProvider — see those entries. iOS/Android: no native extraction planned. Other/Hermes: not delegated. Other/Web: future implementation should delegate to firebase-js-sdk in RNFBAuthModule — do not invest in native iOS/Android extraction.',
171+
},
172+
{
173+
name: 'UserCredential',
174+
reason:
175+
'RN Firebase modular UserCredential includes optional enumerable additionalUserInfo (firebase-js-sdk core fields plus preserved native bridge keys). firebase-js-sdk keeps additionalUserInfo off the public UserCredential interface — use getAdditionalUserInfo there.',
176+
},
177+
{
178+
name: 'isSignInWithEmailLink',
179+
reason:
180+
'iOS/Android: Promise<boolean> via native bridge. Other/All: not aligned yet; firebase-js-sdk synchronous boolean is possible on Other/Hermes and Other/Web.',
181+
},
182+
{
183+
name: 'linkWithRedirect',
184+
reason:
185+
'iOS/Android: resolves immediately with UserCredential. Other/Hermes: not applicable (no DOM). Other/Web: not delegated yet; firebase-js-sdk redirect flow is possible.',
186+
},
187+
{
188+
name: 'reauthenticateWithRedirect',
189+
reason:
190+
'iOS/Android: Promise<void> after native in-app provider flow. Other/Hermes: not applicable. Other/Web: not delegated yet; firebase-js-sdk redirect semantics are possible.',
191+
},
192+
{
193+
name: 'signInWithRedirect',
194+
reason:
195+
'iOS/Android: resolves immediately with UserCredential. Other/Hermes: not applicable. Other/Web: not delegated yet; firebase-js-sdk redirect flow is possible.',
196+
},
197+
{
198+
name: 'OAuthProvider',
199+
reason:
200+
'iOS/Android: retains toObject() and native bridge configuration helpers. Other/Hermes: scopes/custom parameters via js-sdk are possible. Other/Web: full js-sdk OAuthProvider surface is possible; toObject() remains iOS/Android bridge-only. credentialFromResult / credentialFromError always return null at runtime today (types match js-sdk). Same runtime applies to GoogleAuthProvider, GithubAuthProvider, TwitterAuthProvider, FacebookAuthProvider, and PhoneAuthProvider. Other/Web is the future delegation path via firebase-js-sdk; iOS/Android native extraction is not planned.',
201+
},
202+
{
203+
name: 'PhoneAuthProvider',
204+
reason:
205+
'RN Firebase retains native multi-factor verifyPhoneNumber overloads for iOS/Android native bridge methods. MFA also works on Other via firebase-js-sdk through the web bridge (see tests/local-tests); this overload is an intentional RN extension, not a missing Other implementation. credentialFromResult / credentialFromError always return null at runtime today (types match js-sdk). Same runtime applies to GoogleAuthProvider, GithubAuthProvider, TwitterAuthProvider, FacebookAuthProvider, and OAuthProvider. Other/Web js-sdk delegation is the future path for non-null extraction.',
206+
},
207+
{
208+
name: 'TotpMultiFactorGenerator',
209+
reason:
210+
'RN Firebase extension: optional Auth overload for non-default native Firebase apps. firebase-js-sdk uses the default app only.',
211+
},
212+
{
213+
name: 'TotpSecret',
214+
reason:
215+
'iOS/Android: generateQrCodeUrl is Promise<string> via native bridge; openInOtpApp is an RN-only helper. Other/All: js-sdk synchronous generateQrCodeUrl is possible when MFA is supported on Other.',
216+
},
217+
],
218+
};
219+
220+
export default config;

.github/scripts/compare-types/src/registry.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ import appCheckConfig from '../configs/app-check';
1818
import firestoreConfig from '../configs/firestore';
1919
import firestorePipelinesConfig from '../configs/firestore-pipelines';
2020
import remoteConfigConfig from '../configs/remote-config';
21+
import authConfig from '../configs/auth';
2122
import installationsConfig from '../configs/installations';
2223
import perfConfig from '../configs/perf-config';
2324

24-
const SCRIPT_DIR = path.resolve(__dirname, '..');
25-
const REPO_ROOT = path.resolve(SCRIPT_DIR, '..', '..', '..');
25+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
2626

2727
export interface PackageEntry {
2828
/** Short name used in reports (e.g. "remote-config"). */
@@ -103,6 +103,41 @@ function optionalFirebasePackage(
103103
}
104104

105105
export const packages: PackageEntry[] = [
106+
{
107+
name: 'auth',
108+
firebaseSdkTypesPaths: [requiredFirebaseTypes('auth')],
109+
rnFirebaseModularFiles: [
110+
path.join(rnDist('auth'), 'types', 'auth.d.ts'),
111+
path.join(rnDist('auth'), 'modular.d.ts'),
112+
],
113+
rnFirebaseSupportFiles: [
114+
path.join(rnDist('auth'), 'index.d.ts'),
115+
path.join(rnDist('auth'), 'constants.d.ts'),
116+
path.join(rnDist('auth'), 'namespaced.d.ts'),
117+
path.join(rnDist('auth'), 'types', 'namespaced.d.ts'),
118+
path.join(rnDist('auth'), 'types', 'internal.d.ts'),
119+
path.join(rnDist('auth'), 'ConfirmationResult.d.ts'),
120+
path.join(rnDist('auth'), 'MultiFactorResolver.d.ts'),
121+
path.join(rnDist('auth'), 'PhoneAuthListener.d.ts'),
122+
path.join(rnDist('auth'), 'PhoneMultiFactorGenerator.d.ts'),
123+
path.join(rnDist('auth'), 'Settings.d.ts'),
124+
path.join(rnDist('auth'), 'TotpMultiFactorGenerator.d.ts'),
125+
path.join(rnDist('auth'), 'TotpSecret.d.ts'),
126+
path.join(rnDist('auth'), 'User.d.ts'),
127+
path.join(rnDist('auth'), 'getMultiFactorResolver.d.ts'),
128+
path.join(rnDist('auth'), 'multiFactor.d.ts'),
129+
path.join(rnDist('auth'), 'providers', 'AppleAuthProvider.d.ts'),
130+
path.join(rnDist('auth'), 'providers', 'EmailAuthProvider.d.ts'),
131+
path.join(rnDist('auth'), 'providers', 'FacebookAuthProvider.d.ts'),
132+
path.join(rnDist('auth'), 'providers', 'GithubAuthProvider.d.ts'),
133+
path.join(rnDist('auth'), 'providers', 'GoogleAuthProvider.d.ts'),
134+
path.join(rnDist('auth'), 'providers', 'OAuthProvider.d.ts'),
135+
path.join(rnDist('auth'), 'providers', 'OIDCAuthProvider.d.ts'),
136+
path.join(rnDist('auth'), 'providers', 'PhoneAuthProvider.d.ts'),
137+
path.join(rnDist('auth'), 'providers', 'TwitterAuthProvider.d.ts'),
138+
],
139+
config: authConfig,
140+
},
106141
{
107142
name: 'storage',
108143
firebaseSdkTypesPaths: [requiredFirebaseTypes('storage')],

.github/workflows/tests_e2e_android.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,29 +241,36 @@ jobs:
241241
nohup sh -c "until false; do $ANDROID_HOME/platform-tools/adb shell input tap 100 800; sleep 0.2; done" &
242242
shell: bash
243243

244+
- name: Prepare AVD home on /mnt
245+
# GitHub-hosted runners mount a ~74GB volume at /mnt. Set ANDROID_AVD_HOME before
246+
# android-emulator-runner creates the AVD (pre-emulator-launch-script runs too late
247+
# for ln -s ~/.android/avd to relocate an already-created AVD off root disk).
248+
run: |
249+
sudo mkdir -p /mnt/avd
250+
sudo chown "$USER:$USER" /mnt/avd
251+
df -h / /mnt
252+
244253
- name: Detox Tests
245254
# https://github.com/reactivecircus/android-emulator-runner/releases
246255
uses: reactivecircus/android-emulator-runner@0a638108440efd5c7f980e6ba145dbcdd8f32009 # v2.37.0
247256
timeout-minutes: 45
257+
env:
258+
ANDROID_AVD_HOME: /mnt/avd
248259
with:
249260
api-level: ${{ matrix.api-level }}
250261
avd-name: TestingAVD
251262
target: ${{ matrix.target }}
252263
disable-spellchecker: true
253264
arch: ${{ matrix.arch }}
254-
pre-emulator-launch-script: |
255-
sudo mkdir /mnt/avd
256-
sudo chown $USER:$USER /mnt/avd
257-
mkdir -p $HOME/.android
258-
ln -s /mnt/avd $HOME/.android/avd
259265
script: |
260266
$ANDROID_HOME/platform-tools/adb devices
261267
nohup sh -c "$ANDROID_HOME/platform-tools/adb logcat '*:D' > adb-log.txt" &
262268
yarn tests:android:test-cover --headless
263-
yarn tests:android:test:jacoco-report
269+
yarn tests:android:post-e2e-coverage
264270
265271
# https://github.com/codecov/codecov-action/releases
266272
- uses: codecov/codecov-action@e53489f4d376d79066609109e7a95a29eb3740b1 # v7.0.0
273+
continue-on-error: true
267274
with:
268275
verbose: true
269276

.github/workflows/tests_e2e_ios.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ jobs:
313313
- name: Pre-fetch Javascript bundle
314314
if: contains(matrix.buildmode, 'debug')
315315
run: |
316-
nohup yarn tests:packager:jet-ci &
316+
nohup yarn tests:packager:jet-ci > metro.log 2>&1 &
317317
printf 'Waiting for packager to come online'
318318
until curl --output /dev/null --silent --head --fail http://localhost:8081/status; do
319319
printf '.'
@@ -388,6 +388,15 @@ jobs:
388388
name: simulator-${{ matrix.buildmode }}-${{ matrix.iteration }}_log
389389
path: simulator.log
390390

391+
- name: Upload Metro log
392+
# https://github.com/actions/upload-artifact/releases
393+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
394+
continue-on-error: true
395+
if: always() && contains(matrix.buildmode, 'debug')
396+
with:
397+
name: metro-${{ matrix.buildmode }}-${{ matrix.iteration }}_log
398+
path: metro.log
399+
391400
- name: Upload Screen Recording
392401
# https://github.com/actions/upload-artifact/releases
393402
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

.yarn/patches/detox-npm-20.51.0-3e13b6e309.patch

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,28 @@ index 10743c87d17de135317e1bac3e65159b9872412f..abbb302307fbd5eabbbff875983f284a
146146
}
147147
}
148148

149+
diff --git a/src/devices/common/drivers/android/exec/ADB.js b/src/devices/common/drivers/android/exec/ADB.js
150+
index 1111111111111111111111111111111111111111..2222222222222222222222222222222222222222 100644
151+
--- a/src/devices/common/drivers/android/exec/ADB.js
152+
+++ b/src/devices/common/drivers/android/exec/ADB.js
153+
@@ -379,7 +379,19 @@ class ADB {
154+
}
155+
156+
async reverseRemove(deviceId, port) {
157+
- return this.adbCmd(deviceId, `reverse --remove tcp:${port}`);
158+
+ try {
159+
+ return await this.adbCmd(deviceId, `reverse --remove tcp:${port}`);
160+
+ } catch (error) {
161+
+ const details = `${error.stderr || ''} ${error.message || ''}`;
162+
+ if (/listener .* not found/i.test(details)) {
163+
+ log.warn(
164+
+ { deviceId, port },
165+
+ 'Ignoring missing adb reverse listener during Detox teardown',
166+
+ );
167+
+ return;
168+
+ }
169+
+ throw error;
170+
+ }
171+
}
172+
173+
async emuKill(deviceId) {

0 commit comments

Comments
 (0)