VueJS & firebase: Check if a user is logged in after page refresh - javascript

I am working on a webapp with the frameworks Vue 3 and firebase. As stroe I use Pinia. For authentication I use firebase, which works well so far, users can register and log in. As soon as a user logs in, his data is stored in the pinia store.
However, as soon as the user logs in and reloads the page, the data in the pinia store is lost. If I use the firebase function onAuthStateChanged() there is still a user logged in.
My first solution was to call the onAuthStateChanged() function every time the app is started and the pinia store is then filled with the required data. I already tried to call the onAuthStateChanged() function in my firebase config file, but the pinia store is not initialized here yet.
At which point in the Vue app must the onAuthStateChanged() function be called so that the user is automatically logged in after a refresh and I can write the user data to the Pinia store?

I am not sure what you have tried but I know this will work. You can, of course, move the onAuthStateChanged out of your store and it will still work. Keep in mind you will have to use a watcher or computed prop to track store.user and update the UI.
import { getAuth, onAuthStateChanged } from 'firebase/auth';
const auth = getAuth();
onAuthStateChanged(auth, async () => {
const store = useStore();
store.setUser();
});
const useStore = defineStore('store', {
state: () => ({
user: null
}),
actions: {
setUser() {
// Set user here
// this.user = ...
}
}
});

Related

React keycloak - How to use onAuthSuccess and onAuthLogout events?

I use keycloak on my React project as authentication and authorization tool and want to store some data of the user inside a React Context to access data like username etc. This context should update when I log in or log out.
My first approach was to use two events called onAuthSuccess and onAuthLogout but it seems that it will not be fired at all.
To test this approach I execute a console.log. Unfortunately nothing happens if I log in via keycloak.login() and logout via keycloak.logout().
import Keycloak from 'keycloak-js';
import configData from './config.json';
const keycloak = new Keycloak(configData);
keycloak.onAuthSuccess = () => {
console.log('log in!');
}
keycloak.onAuthLogout = () => {
console.log('log out');
}
export default keycloak
Any ideas what the problem could be?

How to set individual IndexedDb storage per user email

I am making a react application where a user can create some data in their own profile and later view it. I am saving this data in IndexedDB using localForage. The problem is due to IndexedDB's design, an IndexedDB store saves data according to the domain. And I have multiple users logging in and creating data. I am using firebase and I can get user email by using onAuthStateChange method. I need to create a store for each account in IndexedDB. I tried doing it but I am stuck with running async and sync code in a correct way. Here's my code where I am creating an IndexedDb instance -
import localforage from 'localforage';
let totalCardsData = localforage.createInstance({
name: 'totalCardsData',
storeName: 'cards',
});
export { totalCardsData };
Using this I can create only 1 store called 'totalCardsData'. Here I tried making a dynamic name for the object.
import firebase from 'firebase/app';
import localforage from 'localforage';
import initFirebase from '../utils/auth/initFirebase';
initFirebase();
let userEmail = '';
let totalCardsData;
firebase.auth().onAuthStateChanged(user => {
if (user) {
userEmail = user.email;
}
});
totalCardsData = localforage.createInstance({
// name: 'totalCardsData',
name: `${userEmail}`,
storeName: 'cards',
});
export { totalCardsData };
onAuthStateChanged is an asynchronous method. So I am getting an empty string for ${userEmail} because onAuthStateChanged finishes it's execution after rest of the code have been finished executing.
I want to run totalCardsData after the onAuthStateChanged has finished execution. So that I can get userEmail string.
I suggest
Seperate firebase onAuthStateChange (ex: module1) and localstorage creation logic(Ex: module2) in two different modules, import both in parent file.
Write a method which takes userEmail as argument and retuns localstorage instance in module2 and export that method(ex: createLocalStorageInstance).
Generate a custom event and emit it from onAuthStateChange and listen to it in parent file and from there call createLocalStorageInstance.
This should work.

How to modify a property in a Javascript class from within a method of that class?

I'm creating a React app in which I have a class called FirebaseManager. After logging in with Google, we retrieve an object with all the info we need from the user from Firebase. I want to store this info in an object of the class User, to serve it locally in the UI. For this purpose I created a property currentUser in FirebaseManager in which I initialise an empty object of User and then update it with the method updateUser. Here is the code for them:
// Current logged in user
currentUser = new User();
// Update user
updateUser(user, data) {
user.name = data.user.displayName;
user.email = data.user.email;
user.picture = data.user.photoURL;
user.uid = data.user.uid;
console.log("User inside updateUser method: ", user, "Data: ", data)
};
Now, I call updateUser right after logging in to Firebase with Google. I do this with the method responseGoogle:
// Logs in with Google
responseGoogle = googleUser => {
const unsubscribe = firebase.auth().onAuthStateChanged(firebaseUser => {
unsubscribe();
if (!this.isGoogleUserEqual(googleUser, firebaseUser)) {
// Build Firebase credential with the Google ID token.
const credential = this.googleProvider.credential(googleUser.tokenId);
// Sign in with credential from the Google user.
firebase
.auth()
.signInWithCredential(credential)
.then(data => {
// We populate the currentUser object
this.updateUser(this.currentUser, data)
console.log("Current user from promise: ", this.currentUser);
})
.catch(error => {
console.log(error);
});
} else {
console.log("User already signed-in Firebase.");
}
});
};
Within the promise .then, the currentUser property is modified, but whenever I try to import it from another file, it is empty and its new values haven't been populated. What I do to import it to another file is exporting an object of the class FirebaseManager:
let firebaseManager = new FirebaseManager();
export default firebaseManager;
Then I just import firebaseManager in the files I need it and I try to access the property currentUser, but firebaseManager.currentUser always returns an empty object of the class User.
All in all, and as a summary, what we want is being able to store the user info from Firebase in an User object and being able to export it to other modules in the app.
Thanks
It turns out that whenever I called the firebaseManager object, a new object of the class was created, thus, even if I changed the property currentUser in one object, this change wasn't replicated in the rest of objects.
What I did, instead, is the following:
Refactoring the class FirebaseManager and turn it into several objects.
Using this firebase state listener to populate an object of the class User.
I did this from the most general component of my app: App. I stored the active user in its state, and I passed it down to any children component that needs it.
As a side note, this functionality can be improved upon with Redux, and we might implement this in the future.

Is it recommended to have a mobx Store for each page?

I am building a single page application with Reactjs and MobX at the frontend (port 3000) and Nodejs and Express at the backend (API, port 4000). I am new to both, MobX and Reactjs and I am trying to set up a well-structured project.
My question is: Is it okay to have a Store for each view?
For example, I have a UserStore which stores the Session information and takes care of the login and logout of the user within the platform. However, after Logging in, I want to redirect the user to the dashboard page. This dashboard page must retrieve information regarding the user, but also it must contact the API and retrieve some data (i.e. Some Todos).
This is how I would do it:
This is the login function in which the redirection to Dashboard is made:
*UserStore.js*
[...]
import navigationStore from './NavigationStore';
[...]
login = async (user) => {
try {
const res = await axios.post('/session/login', {
username: user.username,
password: user.password
});
this.saveUser(res.data);
navigationStore.push('/dashboard');
} catch (error) {
[...]
}
}
And, then, I have created a DashboardStore.js which has the following code:
*DashboardStore.js*
[... imports and initializations ...]
class Store {
#observable todos = null
constructor() {
this.getDashboard();
}
#action('Load dashboard') getDashboard = async () => {
const res = await axios.get('/api/dashboard/', {});
this.todos = res.todos
}
}
const DashboardStore = new Store();
export default DashboardStore;
But this would mean that I'd end up doing another Store for the Todos page and another Store for whatever page I'd need.
In NodeJs you can make a controller for each class and there's nothing weird about it. However, I'm not sure that's how it works on MobX.
It depends on the complexity of your app. I wouldn't create a store for each view or concern, but you could create two, like the MobX docs recommend: https://mobx.js.org/best/store.html.
I'm working on a bigger project right now, and we started with a single store for everything. Obviously, it grew a lot as we kept adding functionality, so I think we might split it at some point to reduce complexity.

Calling function to clear localstorage value from unit test - angular

I want to test the if the localStorage is cleared down whenever I call my function.
Component
ngOnInit() {
// Logout on reaching login screen so we can login
this.authService.logout();
}
authService
logout() {
// remove logged in user from localStorage
localStorage.removeItem('currentUser');
}
TEST
fit('ngOnInit should logout user stored in localStorage', () => {
// Exmample data stored as token
localStorage.setItem('token', 'someUserToken');
component.ngOnInit();
expect(localStorage.getItem('token')).toEqual('{}');
});
Is there any way I can achieve this?
Unit testing should only concern the feature you're testing.
This means that you should not check if the local storage is cleared : that's not what your component does. Your component calls your auth service, which then clears the storage.
This means you should test if the correct method is called.
it('ngOnInit should call auth.logout', () => {
spyOn(component['authService'], 'logout');
component.ngOnInit();
expect(component['authService'].logout).toHaveBeenCalled();
});

Categories