How to set individual IndexedDb storage per user email - javascript

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.

Related

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

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 = ...
}
}
});

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 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.

ReactJS: How to constantly check if token in localstorage has expired?

In ReactJS, is there a way to constantly check to see if the token saved in the localstorage has expired? If it has expired, would like to remove the token.
Came across the following but doesn't it only get triggered when the page gets reloaded?:
window.onbeforeunload = function() {
//remove token
return '';
}
The following assumes you are using redux... you can create a middleware that will trigger an action when a token expires.. this will allow for you to handle a reducer downstream. The redux approach is mainly because Redux is currently the most popular state management solution used with React.
// export the action type used as a const, so it can be imported for
// the reducer to listen for... The export const/key is more convenient
// then you can use a more namespaced string value to help prevent collisions
export const TOKEN_EXPIRED = 'tokenExpiredMiddleware_TokenExpired';
// redux middleware pattern (store) => (next) => (action) => result;
export default function expiredTokenMiddleware(store) {
// here we only care about a handle to the store for store.dispatch
// start checking every 15 seconds, probably want this configurable
setInterval(checkToken.bind(null, store), 15000);
// return a pass-through for the plugin, so other plugins work
return (next) => (action) => next(action);
}
// export checkToken for testing... etc
// should probably be a separate module
export function checkToken(store) {
var tokenId = ''; // TODO: Identify token
var isExpired = true; // TODO: replace with function to get current state
if (isExpired) {
store.dispatch({
type: TOKEN_EXPIRED,
payload: { tokenId },
});
}
};
Then, when you do your createStore, you can just add this middleware which will emit the appropriate action, and you can handle it in your appropriate reducer... I do something similar for the window's resize/scroll events so that my size/position is always set.
This is using ES6+ syntax, since you're using React I think that is a fair assumption
Since you cannot use code running on the user's machine for security relevant use cases anyway, why not check the token only when it is used?
At some point you most likely load the token from local storage and use it to e.g. authenticate the session. Why not first check its validity then before using it?
This saves you the trouble of having an ongoing activity that checks the token, bundles related functionality and most likely reduces the complexity of your code.
After all, the token won't do any harm by just being stored in the browser's storage without being used, would it?

Categories