Save values to a .env variable in ReactJS - reactjs

I've been trying to create a .env variable where initially it will be empty but after login process it will store the data to the .env variable for further work, but unfortunately, I am not able to do so.
Before I put my code example, I would like to have some suggestions!!
Yea, in the login process I'm using session storage to store the user token. So, will it be a good work to store the user data into a .env file and later access it for future use or should I just call getToken function every time I need the token to verify if the user is logged in.
login.js:
const getToken = () => {
const tokenString = sessionStorage.getItem('token');
const userToken = JSON.parse(tokenString);
return userToken?.token
}
const saveToken = (userData) => {
sessionStorage.setItem('token', JSON.stringify(userData));
setToken(userData)
}
Tried different techniques to make it work, but I just couldn't get the data from the .env file.
Watched many different YouTube videos and did exactly like them but it was all in vain.
I checked multiple timed if there is any type or bug in my code or not! There was no error. I was getting the token after successful login and by default it was returning null. I was storing the token only when the user login successfully so that no garbage value gets inserted into the value.
Here's my logic:
const handleSubmit = async function (e) {
e.preventDefault();
const response = await loginUser(user);
if (response.status === 200) {
setToken(response.data);
process.env.REACT_APP_USER_TOKEN=response.data;
navigate("/");
} else {
console.error(response)
}
}

ENV files are used to store sensitive Api keys or secrets. which can only be read by the code when needed.
Storing user data in .env file is not the right way. If your user data should not be available easily in frontend, try encryption and store the encryption key in .env file or backend.

Related

React JS and Firebase : Cannot upload image

Im trying to create a signup form for my website. Im storing the user information on MongoDB and their images on Firebase. When Signup button is pressed i get "POST https://firebasestorage.googleapis.com/v0/b/app/o?name=image.jpg 403" in the browser's console
I have seen several tutorials on how to upload files to firebase storage using React.js and this is what i tried:
const [file , setfile] = useState(null);
const handleClick = (e)=>{
e.preventDefault();
const fileName = new Date().getTime() + file?.name;
const storage = getStorage(app);
const StorageRef = ref(storage , fileName);
const uploadTask = uploadBytesResumable(StorageRef, file);
uploadTask.on('state_changed',
() => {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
//signup() is the node js signup endpoint
signup(dispatch ,{email , password , username , phonenumber , profile:downloadURL});
})
});
}
Image field:
<input type="file" name="file" id="file" onChange={(e)=>setfile(e.target.files[0])} />
If is needed to upload any other part of the code please let me know
Based on the comments under your question you get a 403 error because your Security Rules prevent any user to upload a file (allow read, write: if false;). This is also a point listed by #eugenemusebe in his answer.
You need to adapt your Security Rules in such a way the desired users have the correct access right for writing:
For example a user needs to be authenticated to upload an image:
service firebase.storage {
// The {bucket} wildcard indicates we match files in all Cloud Storage buckets
match /b/{bucket}/o {
// Match filename
match /filename {
allow read: if <condition>;
allow write: if request.auth != null;
}
}
}
More details in the Security Rules documentation.
Note that for testing (and confirming that this is the problem) you can (temporarily) open the write access with:
service firebase.storage {
// The {bucket} wildcard indicates we match files in all Cloud Storage buckets
match /b/{bucket}/o {
// Match filename
match /filename {
allow read: if <condition>;
allow write: if true;
}
}
}

Firebase data deleted upon page refresh in React

I have been stumped on a work around for this problem for a while now and was hoping someone could help.
I am currently working on a React UI that sends info to the backend Firebase for a budgeting app.
When the page first loads, I pull in the data using this:
const [incomeSources, setIncomeSources] = React.useState([]);
/////////////////////////////////
// PULL IN DATA FROM FIREBASE //
///////////////////////////////
async function getData() {
const doc = await getDoc(userCollectionRef);
const incomesData = doc.data().incomeSources;
// const expensesData = doc.data().expenses;
// const savingsData = doc.data().savingsAllocation;
// SET STATES //
if (incomesData.length > 0) {
setIncomeSources(incomesData);
}
}
then when I want to add a new object to the state array I use a input and button. The issue I currently have is that I have it set up like this:
async function updateFirebaseDocs(userID, stateName, state) {
const userRef = doc(db, "users", userID);
try {
await setDoc(userRef, { [stateName]: state }, { merge: true });
} catch (err) {
console.error("error adding document", err);
}
}
React.useEffect(() => {
updateFirebaseDocs(userID, 'incomeSources', incomeSources)
},[incomeSources])
this works so long as I don't refresh the page, because upon page refresh, incomeSources defaults back to an empty array on render. Causing firebase docs to become an empty array again which deletes firestore data.
I can't for the life of me figure out the workaround even though I know its probably right in front of me. Can someone point me in the right direction please.
Brief summary: I am able to pull in data from backend and display it, but I need a way to keep the backend database up to date with changes made in the Frontend. And upon refreshing the page, I need the data to persist so that the backend doesn't get reset.
Please advise if more information is needed. First time posting.
I have tried using the above method using useEffects dependency, I have also tried using localstorage to work around this but also don't can't think of a way of implementing it. I feel I am tiptoeing around the solution.

setPersistence function is not working properly (Firebase + React)

I want to make a functionality where I persist the user while the window is open or to be precise in the current session. To make this I've researched the Firebase documentation, and I found out about this: Authentication State Persistence.
After researching it, I decided to put it inside my app, here's how my code looks like:
function logIn(email, password) {
setPersistence(auth, browserLocalPersistence)
.then(() => {
return signInWithEmailAndPassword(auth, email, password);
})
.catch((error) => {
console.log(error);
});
}
This function is inside my UserAuthContext file where I keep all functionalities regarding user. Now some may say that there is nothing wrong with the way this function is made, but whenever email and password are empty the user can still Login without being stopped by Firebase errors like it happens without setPersistence function.
Does anyone know why does my Login go through even though email and password are empty?

Storing CognitoUser in cache make him lose some properties

I'm currently using AWS Cognito in my application.
When a user first connects whit his account, Cognito returns NEW_PASSWORD_REQUIRED as a challenge, which is fine.
I want to redirect to a page where the user can set his new password, so I put the response from Auth.signIn in storage (I tried local storage, session storage and Cache from AWS Amplify) but when I get it back on the other page, it lose some properties and Auth.completeNewPassword returns the error : 'user.completeNewPasswordChallenge is not a function'
Login.js :
try {
var authPromise = Auth.signIn(this.state.email, this.state.password);
authPromise.then((result) => {
console.log(result);
if (result.challengeName === 'NEW_PASSWORD_REQUIRED') {
Cache.setItem("CognitoUser", result);
this.props.history.push("/login/newPassword");
}
else {
this.props.userHasAuthenticated(true);
this.props.history.push("/");
}
});
} catch (e) {
alert(e.message);
this.setState({ isLoading: false });
}
NewPassword.js :
try {
var user = Cache.getItem("CognitoUser");
if (user) {
await Auth.completeNewPassword(user, this.state.newPassword);
this.props.history.push("/");
Cache.removeItem("CognitoUser");
}
} catch (e) {
alert(e.message);
this.setState({ isChanging: false });
}
Any ideas ?
It's javascript so when you write to your localcache and serializes your result into the "CognitoUser" key , it's stored as a a string, which afterwards deserialized will be a plain old Object unaware of the original type before serialization.
Original cause is maybe that your "result" type may expose functions which are not serializable (if not a getter, or if a getter with arguments).
I suggest you to call and store all the data you want into separate keys and re-read them later.
Cache.setItem("CognitoUser", result);
Cache.setItem("CognitoUser-value-1", result.myFunction1("myArg1"));
Cache.setItem("CognitoUser-value-2", result.myFunction2("myArg2"));
// ..
var user = Cache.getItem("CognitoUser");
var myVal1 = Cache.getItem("CognitoUser-value-1");
var myVal2 = Cache.getItem("CognitoUser-value-2");
You can also keep one single key "CognitoUser" in your localStorage if you make all said functions serializable. For instance, extend the type of your result adding prototypes getter functions (no arguments), each calling and returning respective myFunctionX("myArgX") functions, so that they'll appear in the JSON.stringify process.
My work around,
So this problem troubled me for some time. Amplify Cache didn't seem to work and caching username and password is a bad idea, however my work around was just include the username and password in the Require-New-Password form, so I have 4 inputs instead of just newPassword & confirmPassword which now is username, oldPassword, newPassword, and confirmPassword.
https://github.com/aws-amplify/amplify-js/issues/1715#issuecomment-642733574

3 legged OAuth with React and Redux

What's the accepted method of authenticating with OAuth2 in React using Redux?
My current setup involves wrapping react-router components using Redux-Auth-Wrapper, and if the user is not authenticated, dispatching an action that makes the necessary external URL GET request to an OAuth provider (google in this case).
OAuth2 requires sending a callback URL with your request, so I've set up a react-router url endpoint/component that, when onComponentDidMount fires, dispatches actions to parse the returned hash that comes from the OAuth provider, store that data in the redux store, and redirect the user to the page they originally requested, which is stored in the state parameter of the OAuth request.
This all seems very hacky. It is also difficult to manage the OAuth2 callback URL between production and development environments. Does anybody have a slick OAuth2 workflow working?
P.S. I need to get the Auth Token to the client so that it can be used to make client side API requests that use that token to check the user has access to those resources.
The following is a function that will fetch the token and expiry data from google and store it in local storage. It could be modified to simply return that data as an object.
function oAuth2TokenGet() {
// TODO: First try to get the token from sessionStorage here
// Build the oauth request url
const responseType = 'token';
const clientId = 'YOUR-GOOGLE-CLIENT-ID';
const redirectUri = 'YOUR-REDIRECT-URL';
const scope = 'email profile';
const prompt = 'select_account';
const url = `https://accounts.google.com/o/oauth2/v2/auth?response_type=${responseType}&client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&prompt=${prompt}`;
// Open a new window
const win = window.open(url, 'name', 'height=600,width=450');
if (win) win.focus();
const pollTimer = window.setInterval(() => {
try {
if (!!win && win.location.href.indexOf(redirectUri) !== -1) {
window.clearInterval(pollTimer);
// Get the URL hash with your token in it
const hash = win.location.hash;
win.close();
// Parse the string hash and convert to object of keys and values
const result = hash.substring(1)
.split('&')
.map(i => i.split('='))
.reduce((prev, curr) => ({
...prev,
[curr[0]]: curr[1],
}), {});
// Calculate when the token expires and store in the result object
result.expires_at = Date.now() + parseInt(hash.expires_in, 10);
// TODO: Persist result in sessionStorage here
}
} catch (err) {
// do something or nothing if window still not redirected after login
}
}, 100);
}
I've come up with a better solution which involves opening a new window with the OAuth login form, which is then polled by the parent window to see if it has redirected to the callback URL. Once it has, you can capture the child window url with hash that contains the OAuth token information in the parent window and close the child window. You can then parse this hash out and add it to your applications state.
This tutorial was particularly helpful.

Resources