I'm stuck on making firebase work in my gatsby application that uses Redux with Redux-sagas. I know about the existence of firebase-sagas but I'm trying to make without using it.
I'm trying to init firebase auth by:
import * as firebase from 'firebase/app';
import 'firebase/auth';
export const app = firebase.initializeApp(
{
apiKey : "apiKey",
authDomain : "project.firebaseapp.com",
databaseURL : "https://project.firebaseio.com",
projectId : "project",
storageBucket : "project.appspot.com",
appId : "appId"
}
)
export const authRef = () => app.auth(); //also tried: firebase.auth() and firebase.auth(app)
//firebase.auth returns a function, but firebase.auth() throws error
I have the following config on my gatsby-node.js:
const path = require('path');
exports.onCreateWebpackConfig = ({ actions, plugins, loaders, getConfig }) => {
const config = getConfig()
config.resolve = {
...config.resolve,
mainFields: ['module', 'browser', 'main'],
alias: {
...config.resolve.alias,
['firebase/app'] : path.resolve(__dirname, 'node_modules/firebase/app/dist/index.cjs.js'),
['firebase/auth'] : path.resolve(__dirname, 'node_modules/firebase/auth/dist/index.cjs.js'),
}
}
actions.replaceWebpackConfig(config)
}
It trows the error:
{ [M [Error]: The XMLHttpRequest compatibility library was not found.]
code: 'auth/internal-error',
message: 'The XMLHttpRequest compatibility library was not found.' }
I think it's some problem related to webpack. I would love any insights on this problem :)
As Gatsby builds pages in a server environment, you can't access Firebase during Gatsby build time. Firebase calls (using the Web SDK) have to happen when the user is on a browser/client environment.
One solution to this problem is creating a function like so:
firebase.js:
import firebase from '#firebase/app';
import '#firebase/auth';
import '#firebase/firestore';
import '#firebase/functions';
const config = {
... firebase config here
};
let instance;
export default function getFirebase() {
if (typeof window !== 'undefined') {
if (instance) return instance;
instance = firebase.initializeApp(config);
return instance;
}
return null;
}
This file returns a function, which returns an instance of Firebase if the user has the global window available (e.g. on the browser). It also caches the Firebase instance to ensure it cannot be reinitialised again (in case of the user changing page on your website).
In your components, you can now do something similar to the following:
import getFirebase from './firebase';
function MyApp() {
const firebase = getFirebase();
}
As Gatsby will try to build this page into HTML during gatsby build, the firebase const will return null, which is correct, as the Firebase Web SDK cannot initialise on a server environment. However, to make use of Firebase on your website, you need to wait until Firebase is available (so the user has to have loaded your website), so we can make use of Reacts useEffect hook:
import React { useEffect } from 'react';
import getFirebase from './firebase';
function MyApp() {
const firebase = getFirebase();
useEffect(() => {
if (!firebase) return;
firebase.auth().onAuthStateChanged((user) => { ... });
}, [firebase]);
}
This works as Firebase is being used in a browser environment and has access to the browser, which is needed for the Web SDK to work.
It does have drawbacks; your compo have to return null in instances when you need Firebase to display content, which will mean your HTML build on the server will not contain any HTML, and it'll be injected via the client. In most cases though, such as an account page, this is fine.
If you need access to data from say Cloud Firestore to display page content, you're best using the Admin SDK to fetch content and add it to GraphQL during Gatsby build. That way it will be available on the server during build time.
Sorry if that was a waffle or not clear!
Related
I have an app that persists some values in a cookie. I know that there are other tools such as useState, useContext, etc... but this particular app works with a library that stores information in a jwt so I have to read certain values by fetching the jwt. I am porting the app from next.js 12 (with webpack) to next.js 13 (with turbopack).
I've already ported the app structurally to fit the app style routing of next.js 13. My pages all go in their individual folders with sub layouts WITHIN the app directory, and I have a master layout and homepage directly in the app directory.
The old code for my protected page in next.js 12 looked like this:
protected.tsx
import type { NextPage } from 'next';
import { GetServerSideProps } from 'next';
import { useContext } from 'react';
//#ts-ignore
import Cookies from 'cookies';
const Protected: NextPage = (props: any) => {
if (!props.authorized) {
return (
<h2>Unauthorized</h2>
)
} else {
return (
<div className="max-w-md">
<h1 className="font-bold">This is the Protected Section</h1>
</div>
)}
}
export const getServerSideProps: GetServerSideProps = async ({ req, res, query }) => {
const { id } = query
const cookies = new Cookies(req, res)
const jwt = cookies.get('<MY TOKEN NAME>')
if (!jwt) {
return {
props: {
authorized: false
},
}
}
const { verified } = <MY TOKEN SDK INSTANCE>.verifyJwt({ jwt })
return {
props: {
authorized: verified ? true : false
},
}
}
export default Protected
I have this page moved into it's own directory now.
"getServerSideProps" isn't supported in Next.js 13 https://beta.nextjs.org/docs/data-fetching/fundamentals. The docs say "previous Next.js APIs such as getServerSideProps, getStaticProps, and getInitialProps are not supported in the new app directory." So how would I change my code to work in Next.js 13?
P.S. I know what it looks like but this cookie IS NOT HANDLING USER AUTHENTICATION. I understand that someone could alter the cookie and gain access to the protected page. This is just a small piece of a larger app with other security mechanisms that I have in place.
import { cookies } from "next/headers";
this is next/headers.js cookie function
function cookies() {
(0, _staticGenerationBailout).staticGenerationBailout('cookies');
const requestStore = _requestAsyncStorage.requestAsyncStorage && 'getStore' in _requestAsyncStorage.requestAsyncStorage ? _requestAsyncStorage.requestAsyncStorage.getStore() : _requestAsyncStorage.requestAsyncStorage;
return requestStore.cookies;
}
this is making a request to the client side to get the cookie. In app directory, you are on the server and you can write this inside the component.
const cookie = cookies().get("cookieName")?.value
you can access to your cookie via "cookies-next" library.
pnpm i cookies-next
check this out : https://www.npmjs.com/package/cookies-next
I have built an application using Ionic React and have added an iOS build via Capacitor.
Everything works on the web build, but Firebase stops working on the iOS build.
The build is successful and network requests appear to be firing, but anything related to Firebase (auth and any firestore requests) appear to load infinitely.
I am using Firebase v9 and have tried the following as a fix:
// Initialize Firebase
export const firebaseApp = initializeApp(firebaseConfig);
function whichAuth() {
let auth;
if (Capacitor.isNativePlatform()) {
auth = initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence,
});
} else {
auth = getAuth();
}
return auth;
}
export const firebaseAuth = whichAuth();
export const db = getFirestore();
export const firebaseAnalytics = getAnalytics(firebaseApp);
I have git clone a React+ Firebase project.
When launching the project, I get this error:
Server Error
FirebaseError: Firebase Storage: No default bucket found. Did you set the 'storageBucket' property when initializing the app? (storage/no-default-bucket)
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Have you put your bucket URL on your app?
Reference:
Google Storage Jacascript
import { initializeApp } from "firebase/app";
import { getStorage } from "firebase/storage";
// TODO: Replace the following with your app's Firebase project configuration
// See: https://firebase.google.com/docs/web/learn-more#config-object
const firebaseConfig = {
// ...
storageBucket: ''
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize Cloud Storage and get a reference to the service
const storage = getStorage(app);
I am building an app where the user connects to a server by entering the URL when logging in.
Each server provides an API and I can't hard-code the value for the BaseURL when creating the Axios client instance. I somehow need to globally store the URL and set it based on the user input in the login form.
So I would like to know what is a proper way to do this. I am really not sure what is the best approach to dynamically store a value that I can access when creating the API connection. Any ideas and best practice suggestions are welcome.
Here is the code for creating the API connection: client.js
import { create } from 'apisauce';
const api = create({
baseURL: "BASE_URL_FROM_USER_INPUT" + "server/odata/"
});
export default api;
This is how I use the API: users.js
import api from './client';
const getUsers = () => api.get("/users");
export default {
getUsers
}
This is how I will get my data:
import React, { useState, useEffect } from "react";
import usersApi from '../api/users';
const TestScreenAuthenticated = ({navigation}) => {
const [users, setUsers] = useState([]);
useEffect(() => {
loadUsers();
});
const loadUsers = async () => {
const response = await usersApi.getUsers();
setUsers(response);
}
...
};
export default TestScreenAuthenticated;
you can use the localStoreto store the baseUrl.
//save the value
localStore.setItem('baseUrl', value)
//read the value
localStore.getItem('baseUrl')
If you can use the .env file in your project and put the web service address there. This file is out of reach of the user viewing the site and will not be able to edit it
this link:
https://create-react-app.dev/docs/adding-custom-environment-variables/
I am trying to figure out how to add authentication to a react app that uses Cloud Firestore rather than Realtime Database.
I followed this tutorial and got the whole thing working. Then - the change I'm trying to add is the move from Realtime Database to Cloud Firestore - this makes a difference to whether authentication works. I have made 20 new projects to try to get this work - totally without the process in the tutorial and just relying on firebase documentation. None of them work.
Currently, I have a config file with:
import app from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import firestore from "firebase/firestore";
class Firebase {
constructor() {
app.initializeApp(config).firestore();
this.auth = app.default.auth();
// this.db = app.firebase.database()
this.db = app.firestore();
}
Then, i have a form with this submit handler:
import Firebase from '../../../firebase.1';
handleCreate = () => {
const { form } = this.formRef.props;
form.validateFields((err, values) => {
if (err) {
return;
};
const payload = {
// ...values,
name: values.name,
email: values.email,
organisation: values.organisation,
beta: values.beta,
role: values.role,
// createdAt: Firebase.FieldValue.serverTimestamp()
}
console.log("formvalues", payload);
Firebase
.auth()
.createUserWithEmailAndPassword(values.email, values.password)
console.log('Received values of form: ', values);
Firebase
.collection("users")
.add(payload)
// .then(docRef => {
// resetForm(initialValues);
// })
.then(e => this.setState({ modalShow: true }))
form.resetFields();
this.setState({ visible: false });
this.props.history.push(DASHBOARD);
});
};
At the moment, when I console.log(Firebase) I get:
Uncaught ReferenceError: Firebase is not defined
I have seen this post and followed each one of the recommendations in all of the answers.
I have tried changing the config file uses:
this.auth = app.default.auth();
It makes no difference.
When I try to use this, i get an error that says:
TypeError: _firebase_1__WEBPACK_IMPORTED_MODULE_14__.default.auth is not a function
Does anyone know how to use auth with firebase - where there is a Cloud Firestore instead of a Realtime Database - it's so weird that this makes a difference to whether the authentication tool works.
I've turned off the timestamp entry because I can't get firestore to record that either - but that is a problem for another day. I'm really trying to figure out how to use the authentication tool for now.
NEXT ATTEMPT
I tried to change the firebase.js file so that the config now looks like this:
import app from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
const devConfig = {
};
const prodConfig = {
};
const config =
process.env.NODE_ENV === 'production' ? prodConfig : devConfig;
const Firebase = app.initializeApp(config);
const database = app.firestore();
const auth = app.auth();
const settings = { timestampsInSnapshots: true };
export { Firebase, database as default, settings, auth };
Now, I get an error that says:
TypeError: _components_firebase__WEBPACK_IMPORTED_MODULE_2__.default
is not a constructor
I have been googling - what is a constructor. What is a webpack imported module number reference etc for the last few hours. I would love to know how to translate these error messages into something understandable.
Googling this exact error message suggests that something is wrong with the way the import and export statements are made. The new export in firebase.js is unusual (but others on Stack Overflow have tried it with problems using Firebase). It's still a question mark for me because I don't understand what the error message means.
The error message points to this line of my src/index.js
ReactDOM.render(
<FirebaseContext.Provider value={new Firebase()}>
That line comes from:
import FirebaseContext, { withFirebase } from './Context';
import Firebase from '../../firebase.1';
export default Firebase;
export { FirebaseContext, withFirebase };
That file imports from:
import React from 'react';
const FirebaseContext = React.createContext(null);
export const withFirebase = Component => props => (
<FirebaseContext.Consumer>
{firebase => <Component {...props} firebase={firebase} />}
</FirebaseContext.Consumer>
);
export default FirebaseContext;
It would be a huge reveal if anyone has any advice for learning how to learn what error messages mean. For the moment I'm guessing.
I just finished the tutorial recently also, but I simplified my firebase file. I export only the reference to the initialised firebase
import app from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
const config = {
//...
};
const firebase = app.initializeApp(config);
export default firebase;
And in my project I have:
//...
import firebase from '../../firebase';
//...
useEffect(() => {
const listener = firebase
.firestore()
.collection(COLLECTIONS.USERS)
.onSnapshot(querySnapshot => {
setUsers(querySnapshot);
querySnapshot.forEach(doc => console.log(doc.id, doc.data()));
});
return listener;
}, []);
//...
Check out my Github project here -> https://github.com/henev/react-auth-with-firebase