Skip to content

Bug: authentication persistence incompatible with React-native v0.71.x ( and Expo SDK 48 or upper) #7129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Neosoulink opened this issue Mar 16, 2023 · 3 comments

Comments

@Neosoulink
Copy link

[REQUIRED] Describe your environment

  • Operating System version: Node.js v18.15.0
  • Browser version: React-native v0.71.3 | Expo SDK 48
  • Firebase SDK version: Firebase v9.17.2
  • Firebase Product: auth

[REQUIRED] Describe the problem

It was working fine before, but after I updated Expo SDK from 46.0.0 to 48.0.0 and react-native from 0.69.6 to 0.71.3, the authentication persistence wasn't working anymore. I was required to log in every time I opened the app to connect.

I tried to force persistence by using reactNativeLocalPersistence but was returning an undefined, only inMemoryPersistence returned a value

So I realized that, since react-native v0.71.x Async-storage was removed from react-native and actually, firebase is using that Async-Storage from react-native to persist authentications

Steps to reproduce:

By using the following environment, you should get the same behavior:

  • Node.js v18.15.0
  • React-native v0.71.3 ( and Expo SDK 48 if you're using expo)
  • Firebase v9.17.2

Then try to use the authentication system, to log in with Email and password, for example, you'll notice the auth persistence will not work. The current User will be lost when the app activity will be destroyed.

Relevant Code:

Here is the class I used to manage my firebase logic

class FirebaseHelper {
	private app: FirebaseApp;

	constructor() {
		this.init();
	}

	/**
	 * Init firebase app
	 *
	 * @returns void
	 */
	init = () => {
		if (!this.app) {
			this.app = initializeApp({
				apiKey: ENV.FIREBASE_CONFIG.apiKey,
				authDomain: ENV.FIREBASE_CONFIG.authDomain,
				projectId: ENV.FIREBASE_CONFIG.projectId,
				storageBucket: ENV.FIREBASE_CONFIG.storageBucket,
				messagingSenderId: ENV.FIREBASE_CONFIG.messagingSenderId,
				appId: ENV.FIREBASE_CONFIG.appId,
				measurementId: ENV.FIREBASE_CONFIG.measurementId,
			});
		}
	};

	/**
	 * Method to get the Firebase App
	 */
	getApp = () => {
		return this.app;
	};
	
	/**
	 * Sign-up user and sign-in user
	 *
	 * @param form `SignUpFormType` object that contains new user data
	 */
	async signUp(form: SignUpFormType): Promise<boolean> {
		const _VALIDATION_CONSTRAINT = {
			username: {
				...REQUIRE_NOT_EMPTY_PRESENCE,
				length: {
					maximum: 15,
				},
			},
			email: REQUIRE_EMAIL,
			password: {
				...REQUIRE_NOT_EMPTY_PRESENCE,
				length: {
					minimum: 6,
				},
			},
			amount: REQUIRE_NUMERIC,
			confirmPassword: {
				equality: 'password',
			},
		};

		const _VALIDATION_RESULT = validate(form, _VALIDATION_CONSTRAINT);

		if (_VALIDATION_RESULT) {
			const _ERR_KEYS: string[] = [];
			Object.keys(_VALIDATION_RESULT).map((key) => _ERR_KEYS.push(key));

			showMessage({
				type: 'danger',
				message: _ERR_KEYS[0],
				description: _VALIDATION_RESULT[_ERR_KEYS[0]][0],
			});

			return false;
		}

		try {
			const _USER_CREDENTIALS = await createUserWithEmailAndPassword(
				this.auth,
				form.email,
				form.password,
			);

			if (_USER_CREDENTIALS.user) {
				const DEFAULT_PERIOD = SUPPORTED_PERIODS[0];
				const _USER_DOC_REF = doc(this.db, 'users', _USER_CREDENTIALS.user.uid);

				const _USER_GOAL_DOC_REF = doc(
					this.db,
					'users_goals',
					_USER_CREDENTIALS.user.uid,
				);

				const _NEW_USER_DATA: NewUserDataInterface = {
					email: form.email,
					name: form.username,
					photoURL: _USER_CREDENTIALS.user.photoURL || '',
					phoneNumber: _USER_CREDENTIALS.user.phoneNumber || '',
					signUpMethod: 'EmailAndPassword',
					deleted: false,
					createdAt: Timestamp.now(),
					updatedAt: Timestamp.now(),
				};

				const _NEW_USER_GOAL_DATA: NewUserGoalDataInterface = {
					amount: 0,
					amount_goal: parseInt(form.amount, 10),
					...DEFAULT_PERIOD,
					user: _USER_DOC_REF,
					startAt: Timestamp.now(),
					createdAt: Timestamp.now(),
					updatedAt: Timestamp.now(),
				};

				await setDoc(_USER_DOC_REF, _NEW_USER_DATA);
				await setDoc(_USER_GOAL_DOC_REF, _NEW_USER_GOAL_DATA);
				await this.setUpUserCharity({
					user: _USER_DOC_REF,
				});

				showMessage({
				        type: 'success',
				 	message: '🎉 Sign up successfully',
				});

				return true;
			}
		} catch (err) {
			console.warn('🚧 FirebaseHelper->signUp->catch', err);
			showMessage({
				type: 'danger',
				message: 'Something went wrong',
			});
		}

		return false;
	}

	/**
	 * Sign-in user
	 *
	 * @param form
	 */
	async signIn(
		form: SignInFormType,
		showSuccessMessage: boolean = true,
	): Promise<boolean> {
		try {
			const _USER_CREDENTIAL = await signInWithEmailAndPassword(
				this.auth,
				form.email,
				form.password,
			);

			if (_USER_CREDENTIAL.user) {
				if (await this.userDataExist(_USER_CREDENTIAL.user.uid)) {
					showSuccessMessage &&
						showMessage({
							type: 'success',
							message: '🎉 Connected successfully',
						});
					return true;
				}
				showMessage({
					type: 'danger',
					message: "🤔 It looks like you don't exist in our system",
					duration: 3500,
				});
			}
		} catch (err) {
			console.warn('🚧 FirebaseHelper->signIn->catch', err);
		}

		return false;
	}

       // Other methods ...
       
       	/**
	 * Getter that simply returns a Firestore instance
	 * @returns {Firestore}
	 */
	get db(): Firestore {
		return getFirestore(this.app);
	}

	/**
	 * Get an Auth instance
	 *
	 * @returns {Auth}
	 */
	get auth(): Auth {
		return getAuth(this.app);
	}
}

📌 Note
Actually, I just downgraded my react-native version to 0.70.5 and my expo SDK to 47 to make things work as expected

@Neosoulink
Copy link
Author

I just noticed there's a PR for it: #7128

@jbalidiong jbalidiong added the v9 label Mar 16, 2023
@jbalidiong
Copy link
Contributor

Hi @Neosoulink, thanks for bringing this to our attention. This seems to be similar to this one: #6493. If this is similar to what you want to fix, you have to import the AsyncStorage yourself from @ract-native-async-storage/async-storage and then pass it to getReactNativePersistence(), and then pass that to initializeAuth(), per Sam's comment there.

@jbalidiong
Copy link
Contributor

I’ll be marking the issue #6493 as duplicate and I'll be closing this issue now. If you encounter another issue, feel free to create a new one.

@firebase firebase locked and limited conversation to collaborators Apr 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants