Firebase Authentication - How To Deal With Delay - firebase

I have set-up my Firebase project as per the video from David East, as below with this in my app.js file. I have removed my config parameters.
#topName refers to an element on the page that displays the authenticated user's username. Unfortunately what happens is that someone logs in, or is logged in and goes to the page, it initially displays guest and then after some time it switches to the username of that user. This is quick (<500ms) but causes the page to render twice which is confusing.
How can I avoid this, do I need to store something in local storage?
(function() {
//Initialise Firebase
var config = {
apiKey: "",
authDomain: "",
databaseURL: "",
projectId: "",
storageBucket: "",
messagingSenderId: ""
};
firebase.initializeApp(config);
//Add a realtime listener.
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
console.log(firebaseUser);
$('#topName').text(firebaseUser.email);
}
else
{
console.log('not logged in');
$('#topName').text("Guest");
}
});
}());

This is normal, it happens since the data that is being entered is being sent to the Firebase server, then you wait for a response from Firebase to check if this email is authenticated or not. Also internet connection can effect this.
So lots of stuff are happening in the background, to solve this maybe add a loading spinner widget, or try and store the credentials locally.
To solve this you can use localStorage example:
localStorage.setItem("lastname", "userx"); //store data
document.getElementById("result").innerHTML = localStorage.getItem("lastname") //to retrieve
For more info check this: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
Or you can use sessionStorage, more info here: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage

i solved this with the next code, just show a loader component while waiting for auth.onAuthStateChanged, the var has three values null, true and false.
const Routes = () => {
const dispatch = useDispatch();
// firebase observer for user auth
useEffect(() => {
let unsubscribeFromAuth = null;
unsubscribeFromAuth = auth.onAuthStateChanged(user => {
if (user) {
dispatch(setCurrentUser(user));
} else {
dispatch(clearCurrentUser());
}
});
return () => unsubscribeFromAuth();
}, [dispatch]);
return (
<Switch>
<Route exact path="/" component={Dashboard} />
<Route path="/signin">
<SignIn />
</Route>
<ProtectedRoute path="/protected">
<Dashboard />
</ProtectedRoute>
<Route path="*">
<NoMatch />
</Route>
</Switch>
);
};
const ProtectedRoute = ({ children, ...rest }) => {
const currentUser = useSelector(state => state.auth.currentUser);
if (currentUser === null) {
// handle the delay in firebase auth info if current user is null show a loader if is false user sign out
// TODO create a loading nice component
return <h1>Loading</h1>;
}
return (
<Route
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
render={({ location }) =>
currentUser ? (
children
) : (
<Redirect
to={{
pathname: '/signin',
state: { from: location }
}}
/>
)
}
/>
);
};
export default Routes;

Related

next-redux-wrapper: after hydration useSelector returns initial value (null), but getServerSideProps passes the correct value to the page

I got getServerSideProps like this, which gets token from cookie and gets uses it to fetch user data with RTKQ endpoint. Then dispatches that data to authSlice.
So far it's good.
const getServerSideProps = wrapper.getServerSideProps(
(store) =>
async ({ req, res }: GetServerSidePropsContext) => {
let result: AuthState = null;
const data = getCookie('device_access_token', { req, res });
if (data?.toString()) {
result = await store
.dispatch(
usersApi.endpoints.getUserByToken.initiate(data?.toString())
)
.unwrap();
}
if (result) store.dispatch(setUser(result));
return { props: { auth: result } };
}
);
Then I merge this auth data in the store like this:
const reducer = (state: ReturnType<typeof rootReducer>, action: AnyAction) => {
if (action.type === HYDRATE) {
console.log('payload#HYDRATE', action.payload);
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
};
if (state.auth.user) {
nextState.auth.user = state.auth.user;
nextState.auth.token = state.auth.token;
} // preserve auth value on client side navigation
return nextState;
} else {
return rootReducer(state, action);
}
};
console.log('payload#HYDRATE', action.payload); also shows correct data.
The problem is in a page where I export getServerSideProps,
const IndexPage: NextPage = ({ auth }: any) => {
console.log('user#index', auth);
console.log('userSelect#index', useSelector(selectCurrentUser));
return auth ? <Home /> : <NoAuthHome />;
};
auth shows correct value, but useSelector(selectCurrentUser) shows null
Can someone tell me if this is how it is intended to be, or I'm doing something wrong?
Because I don't want prop-drilling auth on countless pages, just use useSelector(selectCurrentUser) wherever necessary.
Finally found the problem!
problem was in _app.tsx
I wrapped <Component {...pageProps} /> with <Provider store={store} at the same time exporting with wrapper.withRedux(MyApp)

Right way to keep Firebase user data and session stored - React Native

I have been looking on several ways to keep the user logged in my react native application. One of those are:
auth.onAuthStateChanged((user) => {
if (user) {
setIsAuthenticated(true);
} else {
setIsAuthenticated(false);
});
Which isn't working for me, whenever I press r on the application to hot refresh, it losses the user.
I'm currently storing the user email, uid on the local storage, and only loggin it out whenever he presses the logout button. Is this the right way to do so? I have encountered some problems, for example. I can't use auth().currentUser since the data is stored in async storage.
This is how i'm managing it:
export default function App() {
// Autentication
const [isAuthenticated, setIsAuthenticated] = useState(false);
// Dark mode
const [darkMode, setDarkMode] = useState(false);
// Setting location
let [locale, setLocale] = useState(Localization.locale);
i18n.fallbacks = true;
i18n.translations = { en, pt };
i18n.locale = locale;
// Settings fonts
const [fontsLoaded] = useFonts({
Roboto_400Regular,
Roboto_500Medium,
Roboto_700Bold,
});
useEffect(() => {
setLocale('pt');
getData('#rememberUser')
.then((data) => data && setIsAuthenticated(true))
.catch(() => console.log('No user on async'));
// auth.onAuthStateChanged((user) => {
// if (user) {
// setIsAuthenticated(true);
// } else {
// setIsAuthenticated(false);
// }
// console.log(user);
// });
}, []);
if (!fontsLoaded) return <></>;
return (
<DataProvider
setIsAuthenticated={setIsAuthenticated}
setDarkMode={setDarkMode}
darkMode={darkMode}
>
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
{!isAuthenticated ? (
<NavigationContainer>
<AuthRoutes />
</NavigationContainer>
) : (
<Provider store={store}>
<Main />
</Provider>
)}
<ToastMessage />
</ThemeProvider>
</DataProvider>
);
}
storing:
async function storeData(value: any) {
try {
await clearAll();
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem('#rememberUser', jsonValue);
} catch (error) {
console.log('Error on async storage');
}
}
Is this the right way to keep track of the user?

How to properly persist login state using FireBase in React Native EXPO?

I used email and password to sign in through Firebase. However, once I reload the app, I need to sign in again. Is there a way to automatically log user in once the app is reloaded?
I am using EXPO managed project with functional structure btw, not with class structure.
if you're still facing this problem.
Here's how I've done it.
Here's my App.js
const [loggedIn, setLoggedIn] = useState(false);
useEffect(() => {
return firebase.auth().onAuthStateChanged(setLoggedIn);
}, []);
if (loggedIn) {
return <HomeScreen />
} else {
<Login />
}
Here's the login code.
I have a simple form with email and password and a button, when pressed, this functions is called.
const handleLogin = async () => {
await firebase
.auth()
.signInWithEmailAndPassword(email, password)
.catch((err) => {
setVisible(true);
setModalMessage(err.message);
});
};
Let me know if this worked.

Vue.js Vuex Firebase - Cannot get user from store

In my router, I have a beforeEach guard to check for protected pages..
When I display the store state, I can see that there is a user ( bind to Firebase) but I cannot get it , even display it ...
router.beforeEach((to, from, next) => {
console.log('ROUTER from: ', from, ' to: ', to, ' next: ', next)
if (to.matched.some(record => record.meta.requiresAuth)) {
console.log('protected page')
console.log('store state: ', store.state)
console.log('store state user: ', store.state.user)
if (!store.state.user || !store.state.user.emailVerified || store.state.user.isAnonymous) {
// console.log('ROUTER auth user: ', store.state.user)
next({
path: '/signin'
})
} else {
next()
}
} else {
// console.log('ROUTER no auth: ', to)
next()
}
})
console.log output from Chrome dev tools
Make sure that the User is Auth, you can use the method onAuthStateChanged() of firebase auth in you main.js if you are using vue-cli. See the example:
in you main.js after firebase inicialization
// Initialize Firebase
let config = {
apiKey: "you_app_generated_key",
authDomain: "youapp-randomkey.firebaseapp.com",
databaseURL: "https://youapp-randomkey.firebaseio.com",
projectId: "youapp-randomkey",
storageBucket: "gs://youapp-randomkey.appspot.com/",
};
firebase.initializeApp(config);
//If the event onAuthStateChanged() is emited
//You can can verify if the user is auth or not.
//If he isn't you can set null for the user.
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.$store.dispatch('setUser', user) //set the User
} else {
this.$store.dispatch('setUser', null) //set null because he is notauth
}
})
}
})
Now in any part of you project you can verify if there's a User or not in you state.
If there's a User, To display it, it depends of what user prop you need to display.
When need to display or get user: name, email and photo. I do:
let user = state.user
let user_id: user.uid
let user_name: user.displayName,
let user_photo: user.photoURL
.
Sorry for my english.

Persist authentication with React Native / Redux and Firebase

I have a fairly simple React native / Redux app and have recently added Redux persist with AsyncStorage to help carry over the state upon reload.
However, I am having issues getting Firebase to re-authenticate the user. I was wondering if anyone has experience as I am fairly new to Redux as well as Firebase. To be more clear, I would like the user to login once, and then not have login again every time they open the app.
my login user action:
export const loginUser = ({ email, password }) => {
return (dispatch) => {
dispatch({ type: LOGIN_USER });
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => {
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => loginUserFail(dispatch));
});
};
};
My App.js:
class App extends Component {
constructor() {
super();
this.state = {
rehydrated: false,
store
};
}
componentDidMount() {
const persistor = persistStore(
store,
{
storage: AsyncStorage,
whitelist: ['auth']
},
() => { this.setState({ rehydrated: true }); }
);
AppState.addEventListener('change', () => this.handleAppLoaded(AppState.currentState));
const firebase_config = {
apiKey: Config.FIREBASE_API_KEY,
authDomain: `${Config.FIREBASE_PROJECT_NAME}.firebaseapp.com`,
databaseURL: `https://${Config.FIREBASE_PROJECT_NAME}.firebaseio.com`,
storageBucket: `${Config.FIREBASE_PROJECT_NAME}.appspot.com`,
messagingSenderId: Config.FIREBASE_MESSAGE_ID
};
firebase.initializeApp(firebase_config);
}
componentWillUnmount() {
AppState.removeEventListener('change', () => this.handleAppLoaded(AppState.currentState));
}
handleAppLoaded(state) {
if (state === 'active') {
store.dispatch(renewToken(store.getState()));
}
return null;
}
render() {
if (!this.state.rehydrated)
return null;
this.handleAppLoaded(AppState.currentState);
return (
<Provider store={store}>
<RouterWithRedux />
</Provider>
);
}
}
export default App;
Could someone point me in the right direction? I have tried making a reauthUser action, but got stuck as I could not find a way to reauthenticate the user, as the Auth object doesn't hold the password (obviously for security reasons)
Are you using the web SDK? If you are, then first you need to using this instead https://github.com/invertase/react-native-firebase
These libraries are for the react-native project with native support.
If you are already using it, you could check the API here https://rnfirebase.io/docs/v3.2.x/auth/reference/auth
Setup all the onUserChanged, onAuthStateChanged etc. Then your app should no problem with re-authenticate stuff.

Resources