I am pretty new to React Native; I have been trying to add google authentication via firebase and I keep running into this warning:
[Unhandled promise rejection: ReferenceError: Can't find variable: auth]
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:86:13 in tryCatch
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:66:31 in <anonymous>
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:86:13 in tryCatch
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:124:27 in invoke
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:148:16 in PromiseImpl$argument_0
at node_modules\promise\setimmediate\core.js:45:6 in tryCallTwo
at node_modules\promise\setimmediate\core.js:200:22 in doResolve
at node_modules\promise\setimmediate\core.js:66:11 in Promise
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:147:15 in callInvokeWithMethodAndArg
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:152:154 in _invoke
at node_modules\#babel\runtime\helpers\regeneratorRuntime.js:238:57 in exports.async
at node_modules\promise\setimmediate\core.js:37:13 in tryCallOne
at node_modules\promise\setimmediate\core.js:123:24 in setImmediate$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:123:14 in _callTimer
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:177:14 in _callImmediatesPass
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:437:30 in callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:388:6 in __callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:132:6 in __guard$argument_0
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:365:10 in __guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:131:4 in flushedQueue
Here is my code:
import React, { createContext, useContext } from 'react';
import * as Google from "expo-google-app-auth";
import{
GoogleAuthProvider,
onAuthStateChanged,
signInWithCredential,
signOut,
} from "#firebase/auth";
const AuthContext = createContext({});
const config = {
androidClientId: key,
iosClientId: key,
scopes: ['profile', 'email'],
permissions: ['public_profile', 'email', 'gender', 'location'],
}
export const AuthProvider = ({children}) => {
const signInWithGoogle = async () => {
await Google.logInAsync(config).then(async (logInResult) => {
if(logInResult.type === 'success'){
const { idToken, accessToken } = logInResult;
const credential = GoogleAuthProvider.credential(idToken, accessToken);
await signInWithCredential(auth, credential);
}
return Promise.reject();
});
};
return (
<AuthContext.Provider value ={{
user: null,
signInWithGoogle
}}
>
{children}
</AuthContext.Provider>
);
};
export default function useAuth() {
return useContext(AuthContext);
}
I pass the variable auth here:
await signInWithCredential(auth, credential);
However, no users show up on the authentication page of firebase, and I keep receiving this warning.
Thanks for your help!
As the error says, you're trying to pass an auth variable to signInWithCredential, but you don't declare or initialize it anywhere. As shown in the documentation on getting started with Firebase Authentication on the web, you can get this with:
import { getAuth } from "firebase/auth";
// TODO: Replace the following with your app's Firebase project configuration
// See: https://firebase.google.com/docs/web/learn-more#config-object
const firebaseConfig = {
// ...
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize Firebase Authentication and get a reference to the service
const auth = getAuth(app);
Please refer to the official documentation provided by firebase for google auth
The google-signin library provides a wrapper around the official Google login library, allowing you to create a credential and sign-in to Firebase.
Most configuration is already setup when using Google Sign-In with Firebase, however you need to ensure your machines SHA1 key has been configured for use with Android. You can see how to generate the key on the Getting Started documentation.
Ensure the "Google" sign-in provider is enabled on the Firebase Console.
Follow these instructions to install and setup google-signin
Before triggering a sign-in request, you must initialize the Google SDK using your any required scopes and the webClientId, which can be found in the android/app/google-services.json file as the client/oauth_client/client_id property (the id ends with .apps.googleusercontent.com). Make sure to pick the client_id with client_type: 3
import { GoogleSignin } from '#react-native-google-signin/google-signin';
GoogleSignin.configure({
webClientId: '',
});
Once initialized, setup your application to trigger a sign-in request with Google using the signIn method.
import React from 'react';
import { Button } from 'react-native';
function GoogleSignIn() {
return (
<Button
title="Google Sign-In"
onPress={() => onGoogleButtonPress().then(() => console.log('Signed in with Google!'))}
/>
);
}
The onGoogleButtonPress can then be implemented as follows:
import auth from '#react-native-firebase/auth';
import { GoogleSignin } from '#react-native-google-signin/google-signin';
async function onGoogleButtonPress() {
// Get the users ID token
const { idToken } = await GoogleSignin.signIn();
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
return auth().signInWithCredential(googleCredential);
}
Upon successful sign-in, any onAuthStateChanged listeners will trigger with the new authentication state of the user.
Related
I can't firebase v9 enableIndexedDbPersistence in a nextjs pwa.
this erros are trow
index.js // firebase main
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore'
import { getStorage } from 'firebase/storage'
const firebaseConfig =
process.env.NEXT_PUBLIC_FIREBASE_CONFIG
const app = initializeApp(JSON.parse(firebaseConfig))
export const db = getFirestore(app)
// enableIndexedDbPersistence(db) // >>> if put here it said can't be invoked after getFirestore or any other funtion
export const auth = getAuth(app)
export const storage = getStorage(app)
So where should i invoke
The enableIndexedDbPersistence(db) function must be called before . From its documentation:
Must be called before any other functions (other than
{#link initializeFirestore}, {#link (getFirestore:1)} or
{#link clearIndexedDbPersistence}.
So getFirestore(app) is actually exempted from that, in contrast what you stated in the comment of your code snipped:
// >>> if put here it said can't be invoked after getFirestore or any other function
Consequently, my guess would be that you might use the exported db before the promise of enableIndexedDbPersistence(db) completes.
You could resolve this in different ways, such as wrapping it in a service or method and make sure you are await-ing the promise, or generally change your app that db is not used right away.
In my Ionic PWA app, I'm successfully using the following:
import { getFirestore, enableIndexedDbPersistence } from "firebase/firestore";
import { initializeApp } from "firebase/app";
const firebaseConfig = {
// ...
};
const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
enableIndexedDbPersistence(db)
.then(() => console.log("Enabled offline persistence"))
.catch((error) => {
if (error.code == "failed-precondition") {
// Multiple tabs open, persistence can only be enabled
// in one tab at a a time.
// ...
} else if (error.code == "unimplemented") {
// The current browser does not support all of the
// features required to enable persistence
// ...
}
});
This is very similar to your snipped indeed. But the first access to Firestore happens after via user interaction, and not right away.
I have a React Native app that needs to be served on its web version. So, I need to implement the Google OAuth with the firebase js lib.
I'm following these steps.
If I use the same code from the docs I have a weird error (of course I put the proper config object in the initializeApp method):
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { ResponseType } from 'expo-auth-session';
import * as Google from 'expo-auth-session/providers/google';
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider, signInWithCredential } from 'firebase/auth';
import { Button } from 'react-native';
// Initialize Firebase
initializeApp({
/* Config */
});
WebBrowser.maybeCompleteAuthSession();
export default function App() {
const [request, response, promptAsync] = Google.useIdTokenAuthRequest(
{
clientId: 'Your-Web-Client-ID.apps.googleusercontent.com',
},
);
React.useEffect(() => {
if (response?.type === 'success') {
const { id_token } = response.params;
const auth = getAuth();
const credential = GoogleAuthProvider.credential(id_token);
signInWithCredential(auth, credential);
}
}, [response]);
return (
<Button
disabled={!request}
title="Login"
onPress={() => {
promptAsync();
}}
/>
);
}
The error is:
I get this error specifically when I call signInWithCredential()
The strange thing is that in the Network tab (Chrome DevTools) I have a successful response:
And the problem is: If I want to get the user information with onAuthStateChanged, it is null because the login attempt just failed (despite the dev-tools being all good)
Note: I've tried with a new starter project, moving our dependencies with the same version as they are here and it's working.
But I can't figure out what can be happening with this project.
EDIT: Adding images of the requests that are being made:
I am using firebase web sdk without React. I have 4 pages in my application. I am using express js in for each pages to send html file of that route.
I have a file called common.js in which I defined methods that will be shared by all pages. In the same file I defined a method to act on auth changed is as following:
Modified below code
import { initializeApp } from 'firebase/app';
import { getAuth, onAuthStateChanged } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { firebaseConfig } from './firebase.config';
const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);
const auth = getAuth(firebaseApp);
const addNavbar = () => {
$('#nav').load('nav.html', () => {
document.getElementById('btnSignOut').addEventListener('click', (e) => {
auth.signOut();
});
});
}
// createAlert, createEditDeleteButtons, displayLoaderAndHideApp defined here
const unsubscribeOnAuthStateChanged = onAuthStateChanged(auth, async (user) => {
if (user) {
window.location.replace('/home');
} else {
window.location.replace('/login');
}
});
export {
addNavbar, auth, createAlert, createEditDeleteButtons, db, displayLoaderAndHideApp,
};
On each page I first call handleOnAuthStateChanged(), and it is also called after successful login. I want to persist the auth so I came across setPersistence(), so I used it in login.js and is as follows:
import { browserLocalPersistence, browserSessionPersistence, setPersistence, signInWithEmailAndPassword } from 'firebase/auth';
import { auth, handleOnAuthStateChanged } from './common';
document.getElementById('btnSignin').addEventListener('click', async (e) => {
e.preventDefault();
try {
const form = document.querySelector('form.formLogin');
const email = form.email.value.trim();
const password = form.password.value;
await setPersistence(auth, browserLocalPersistence);
await signInWithEmailAndPassword(auth, email, password);
handleOnAuthStateChanged(auth.currentUser); // here auth will retrieve the new logged in user
} catch (e) {
console.error(e);
}
});
If I use onAuthStateChanged() on other pages my user is null then I am stuck in a loop.
Am I missing something?
Update
All of the minimal code is after removing all the functionalities except a few that might causing the problem.
I have a file called nav.html that is loaded into page by common.js and is as defined above.
Code for home, page 1 and page 2 adds navigation by calling addNav. Home will import methods from common.js, page 2's js, and page 1's js. Home page's js is as following:
import { addDoc, collection, deleteDoc, doc, getDocs, onSnapshot, query, setDoc, where } from 'firebase/firestore';
import { addNavbar, createAlert, createEditDeleteButtons, db, displayLoaderAndHideApp as displayLoader } from './common';
// imports from page 1 and page 2
addNavbar();
// Other methods are defined here...
// Nothing is exported as of now..
Firebase automatically persists the auth state on most browser environments, and restores it on app/page reload. To detect this restore of auth state (and other auth state changes) you will need to register an auth state listener with the SDK as shown in the first code fragment in the documentation on getting the current user:
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
Most of the code of your handleOnAuthStateChanged can probably go into this handler.
I'm using Google firebase v9. On the first sign in, usually there would be a pop up for user to choose which email to sigh in with, but for some reason, when I sign in, it automatically send me to the chat page automatically. I tried delete user in my firebase app and tried again and get the same result. What am I doing wrong?
firebase.js:
import { GoogleAuthProvider } from 'firebase/auth';
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {//myap config};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const provider = new GoogleAuthProvider();
export { auth, provider };
login-page:
import { auth, provider } from '../firebase';
import { useAuthState } from 'react-firebase-hooks/auth';
import { useNavigate } from 'react-router-dom';
import { signInWithPopup } from 'firebase/auth';
function Header(){
const [user] = useAuthState(auth);
const navigate = useNavigate();
const signIn = (e) => {
signInWithPopup(auth, provider)
.then(() => navigate('/channel'))
.catch((error) => alert(error.message));
};
return (
<buttononClick={!user ? signIn : () => navigate('/channel')}>Login</button/>
);
};
Maybe It's because I am already sign-in on chrome? If someone can check my code and verified, that would be awesome.
It sounds like your app is already associated with your Google account. Signing out does not sever that association.
Removing that association is described in the Remove third-party account access, but mostly consists of going to the list of Third-party apps with account access and removing your app from that list.
If that doesn't do it, have a look at the results on forcing the account picker to show up.
I am struggling with migrating firebase v8 -> v9. I have a component library with Storybook for demoing components. This library is published as a package through NPM/Github packages.
The migration have not given me any trouble, except this. The problem occurs when importing and using the component below in my web app.
The code below works perfectly in Storybook, but fails when importing the component into my web app.
const useGetLanguages = (firestoreInstance: Firestore): Response => {
const [loading, setLoading] = useState(false)
const [data, setData] = useState<ILanguage[]>([])
console.log('firestoreInstance: ', firestoreInstance) // Logs firestore instance
const fetch = useCallback(() => {
if (!firestoreInstance) return
setLoading(true)
const collectionRef = collection(firestoreInstance, 'data')
console.log('blip') // this logs fine
getDoc(doc(collectionRef, 'tags')) // crashes here...
.then(res => {
const data = res?.data()?.languages
if (data) {
const languages = (Object.values(data) as unknown) as ILanguage[]
if (languages) {
setData(languages.sort(sortBy('name')))
}
}
})
.finally(() => {
setLoading(false)
})
}, [firestoreInstance])
I initially used:
getDoc(doc(firestoreInstance, 'data', 'tags'))
which also worked in Storybook, but when navigating to a page using this hook in my web app, I get this error:
Uncaught FirebaseError: Expected first argument to collection() to be a CollectionReference,
a DocumentReference or FirebaseFirestore
I have read through similar questions, but it does not solve the issue. Any input would be much appreciated!
EDIT: I am importing firebase like this:
// firebaseConfig.ts
import { FirebaseOptions, initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore'
import { getFunctions } from 'firebase/functions'
const config: FirebaseOptions = {
// ...config
}
export const app = initializeApp(firebaseConfig)
export const authInstance = getAuth(app)
export const firestoreInstance = getFirestore(app)
export const functionsInstance = getFunctions(app)
I then import firestoreInstance and pass it to the function/component that is imported from the package.
I will try to pass the config itself and consume that in the package and post result.
It turned out to be a slip up from our side. We needed to install firebase as a peerDependency in our package.json. Had similar issues when migrating to MUI 5. If anyone else is getting obscure errors that do not make sense in your situation it might be worth a shot to check where you have your dependencies!