Remember me with redux storage - reactjs

I have userDetails in redux store and I used redux persist with redux storage to save the details after loads page or after closing my app.
The problem is, when I logged in and I don't checked the rememberMe in index.js is checked if !rememberMe and do logOut() right after login.
So I need to check just at the entrance into the app for the first time and if !rememberMe I will call logOut() but if userDetails !== 'emepty' && rememberMe I will do login() for get valid token.
Why I need to check this it in the first time? because I need to reset the uesrDetails to guest details if !rememberMe.
This is my index.js:
const setBeforeLists = useCallback(
(lists) => {
beforeListsChanged(lists);
},
[beforeListsChanged]
);
const setUserDetails = useCallback(
(userDetails) => {
userDetailsChanged(userDetails);
},
[userDetailsChanged]
);
useEffect(() => {
//userDetails.id === 'empty' is guest.
if (
userDetails.id !== 'empty' &&
!userDetails.rememberMe
) {
logOut(setUserDetails, setBeforeLists);
}
}, [
setBeforeLists,
setUserDetails,
userDetails
]);

When you refresh the app, the store will be reset. The token is gone. As we expected.
So, we can’t use the state to store the authentication token. This is where AsyncStorage comes onto the stage.
I'm replying to a react-native application.
If it's a web application use session storage.
Let’s import AsyncStorage in the React Native project:
import { AsyncStorage } from "react-native";
Then, let’s create two functions,setToken and getToken, for storing and retrieving the persistent data. These are the main methods of this tutorial. Here, we are storing and retrieving the user data from/to AsyncStorage.
async storeToken(user) {
try {
await AsyncStorage.setItem("userDetails", JSON.stringify(user));
} catch (error) {
console.log("Something went wrong", error);
}
}
async getToken(user) {
try {
let userDetails = await AsyncStorage.getItem("userDetails");
let data = JSON.parse(userDetails);
console.log(data);
} catch (error) {
console.log("Something went wrong", error);
}
}
componentDidMount() {
this.getToken();
}
Let’s prove it. The token is thereafter you quit & reopen the app. Try it out yourself. Now, our app automatically logs you in after each session.
Hope this helps!

Related

React - Session is lost when page is refreshed

I am using Firebase, and after login to watch if user is logged in using the following block;
useEffect(() => {
const checkAuthToken = () => {
const token = sessionStorage.getItem('auth-token');
if (token) {
setIsAuthenticated(true);
} else {
setIsAuthenticated(false);
}
}
window.addEventListener('storage', checkAuthToken);
return () => {
window.removeEventListener('storage', checkAuthToken);
}
}, [])
It works without issue until I refresh the page even sessionStorage data is still on the browser memory.
The idiomatic way to respond to sign in with Firebase is to listen for auth state changes, rather than reading something from local storage yourself.
I recommend sticking to that approach, which is shown in the first snippet in the documentation on getting the current user.

Firebase auth not persisting on page refresh

For some reason when I refresh the page the authentication doesn't persist and itll redirect the user back to sign in, I've seen a couple other postings about this on here but still doesn't work in my case. I'm using nextjs and I have localhost added to my signin method on firebase, it won't let me add localhost:3000. I don't want the user to be redirected to the sign in I want it to be persisted, so however many times the user refreshes the page it stays the same. I'm using nextjs, Here is my code:
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
const userDetails = fetchDetails(user.uid);
userDetails.then((use) => setUser(use.data()));
} else {
router.push("/signin");
}
});
}, []);
I've also tried it just like this too:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
const userDetails = fetchDetails(user.uid);
userDetails.then((use) => setUser(use.data()));
} else {
router.push("/signin");
}
});

React native firebase phone authentication, if code sent navigate to otp screen

I am using react native firebase, if code sent then i want to navigate to otp screen
But it shows an warning :
Non-serializable values were found in the navigation state. Check:
OtpVerification > params.confirm._auth._app._deleteApp (Function)
This can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions'
My code :
In login Screen :
async function signInWithPhoneNumber(phoneNumber) {
try {
const confirmation = await auth().signInWithPhoneNumber(phoneNumber);
props.navigation.navigate('OtpVerification', {
confirm: confirmation,
phoneNo,
});
} catch (error) {
console.log(error);
}
}
In Otp Screen :
async function confirmCode() {
if (otp) {
try {
await confirm.confirm(otp);
props.navigation.navigate('OnBoarding');
} catch (error) {
AlertMsg('Invalid OTP');
}
} else {
AlertMsg('Please Enter OTP');
}
}
So I believe in belive function where you have confirmation variable, You might have to pass some constant value over there handling the return of firebase signin.
async function signInWithPhoneNumber(phoneNumber) {
try {
const confirmation = await auth().signInWithPhoneNumber(phoneNumber);
props.navigation.navigate('OtpVerification', {
confirm: confirmation,
phoneNo,
});
} catch (error) {
console.log(error);
}
Check this document - https://reactnavigation.org/docs/troubleshooting/#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state
This expo for your navigation - https://snack.expo.dev/vKpugczaG

What's the proper way to use Firebase Authentication along with Firestore?

Hi all—I'm building an app using Next.js and Firebase, both brand new technologies for me. It's a simple app where a user creates an account and must log in. If the user doesn't create an account, the app is useless—they can't move on to the next screen, which is a dashboard. Anyway, when they log in, they can then create a trip/vacation itinerary. I'm using Firebase Auth for auth and Firestore (not real-time db) as my db. My goal is that when a user logs in, the user can see every itinerary that they created and no one else's. It should be full CRUD. This is the first time I've done this sort of authentication as well, so that's likely adding to my confusion.
I know that my code isn't right, but it sort of worked. What keeps happening is that it seems like there's a lag when a user logs in and out. I've tested this on my local copy. When I log out and then log back in as a different user, it tells me that the uid is null. Anywhere from 1 - 30 minutes later (seriously), all of a sudden the page loads for the uid that I logged in with! Everything that I've read says that there's a lag with the authentication, but I couldn't really find a solution other than just pointing out the problem—so basically writing a console log that says who's logged in at the time and then the same when they've logged out. Also, I watched / read tons of tutorials, so maybe it's my code? I'm so sorry in advance for this novel of code—I'll organize as best as I can!
Here's my config info, so I'm referring to Firebase as fire. The sign-in method is email and password, and everything looks as it should in Firebase as far as capturing that information on the Authentication screen.
import firebase from 'firebase';
const firebaseConfig = {
apiKey: 'AIzaSyB-xEPETXSfjboKe5H0kPUu-ZdRDGfszmA',
authDomain: "where-to-jess.firebaseapp.com",
databaseURL: 'https://where-to-jess-default-rtdb.firebaseio.com/',
projectId: "where-to-jess",
storageBucket: "where-to-jess.appspot.com",
messagingSenderId: "914509599583",
appId: "1:914509599583:web:80cdf3e4090417b0f35cea"
};
try {
firebase.initializeApp(firebaseConfig);
} catch(err){
if (!/already exists/.test(err.message)) {
console.error('Firebase initialization error', err.stack)}
}
const fire = firebase;
export default fire;
When a user creates an account, they're also added to a collection 'users' in my db. I am using React Hooks (for the first time) as well. Their email is their username to login, but I'm capturing their email in the db. They are also immediately logged in upon account creation. This part also works.
const handleSubmit = (e) => {
e.preventDefault();
setPassword('');
fire.auth().createUserWithEmailAndPassword(userName, password)
.then(() => {
fire.firestore().collection('users').doc(fire.auth().currentUser.uid)
.set({
firstName: firstName,
lastName: lastName,
email: userName
})
.catch(error => {
console.log('user wasn\'t added to db: ', error);
})
})
.catch(error => {
console.log('user wasn\'t able to create an account: ', error);
})
router.push('/users/dashboard')
};
This is my login code:
const handleLogin = (e) => {
e.preventDefault();
fire.auth()
.signInWithEmailAndPassword(username, password)
.catch((error) => {
console.log('user wasn\'t able to login: ', error);
})
setUsername('')
setPassword('')
router.push('/users/dashboard')
};
Now for the fun part! This is my code for form submission for the itinerary. What I'm trying to achieve here is to have this newly created itinerary attached to their uid in the 'users' db. I'm leaving out all the form stuff because it's super long. This also seems to work—I can see it coming in in the db for whichever account I'm using.
const handleSubmit = (e) => {
e.preventDefault();
fire.firestore()
.collection('users').doc(fire.auth().currentUser.uid).collection('itineraries')
.add({
//
})
.catch(error => {
console.log('itinerary not added to db ', error)
})
router.push('/users/dashboard')
}
Here's where it all went to heck! I suspect it's because I'm cutting corners, which I'll explain next. This dashboard should show ONLY itineraries that the current logged-in user created. If the current logged-in user didn't create any itineraries, I'd get an error saying that the uid was null. SO, my workaround was to just create a fake itinerary manually in the db on their account (since I was testing) and give the tripName value as null. This seems to work, but this is where the weird login / logout stuff happens.
export default function Dashboard() {
const router = useRouter();
const [itineraries, setItineraries] = useState([]);
const [loggedIn, setLoggedIn] = useState(false);
fire.auth()
.onAuthStateChanged((user) => {
if (user) {
console.log(user.email + " is logged in!");
setLoggedIn(true)
} else {
setLoggedIn(false)
console.log('User is logged out!');
}
})
useEffect(() => {
const unsubscribe =
fire.firestore()
.collection('users').doc(fire.auth().currentUser.uid).collection('itineraries').where('tripName', '!=', 'null')
.onSnapshot(snap => {
const itineraries = snap.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
setItineraries(itineraries);
return () => {
unsubscribe();
};
});
}, []);
const handleLogout = () => {
fire.auth()
.signOut()
router.push('/')
};
Lastly, here is the one rule that I have on the db. I got confused reading the rule docs, and I feel like I cut a corner here.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read
allow write
}
}
}
Again, I'm really sorry for ALL of that code. This is my first time using React Hooks, Next, and Firebase—so it's a mashup of Firebases's docs, tutorials, and my own code. I'd appreciate ANY help or advice here.
That rule will allow all access to all documents in your db at present. You want something like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user_id}{
allow read, write: if request.auth != null && request.auth.uid == user_id;
}
}
}
That will allow access only to users that are authenticated
This was 100% user error on my part, but I wanted to share since some of my issues seem pretty common. In addition to AspiringApollo's advice above, I had my function completely out of order (as I mentioned, hook newbie). The above plus structuring my function like this fixed it:
useEffect(() => {
const unsubscribe =
fire.auth()
.onAuthStateChanged((user) => {
if (user) {
let uid = user.uid
console.log(user.email + ' is logged in!');
setLoggedIn(true)
// all the things you want to do while the user is logged in goes here
} else {
setLoggedIn(false)
console.log('user is logged out!');
}
return () => {
unsubscribe();
};
});
}, []);
Still open to suggestions and more sets of eyes because I know this is a little messy!

React login using context API with private route

In my react web app, login-logout functionality is implemented using context-API and hooks. Everything seems to work fine except for the fact that I have to click the 'Login' button twice to push the user to the dashboard.
The statement, return <Redirect to="/dashboard /> does not work, instead I have to use history.push('/dashboard'), which does not seems to be a better option to me while login.
Here is the working snippet :
https://codesandbox.io/s/wizardly-worker-mqkex?file=/src/AuthContext.js
Also, I need some suggestions for the best practise to fetch the logged user details in other components. Using localstorage or global context API state, which of them serves as the best option for the same ?
Any help to resolve this, appreciated :)
Well, it boils down to the simple fact that your context is not updated when you do your check. The simplest solution would be to remove the check for isLoggedIn and push the user to the Dashboard:
const postLogin = async (e) => {
e.preventDefault()
try {
await login()
props.history.push('/dashboard')
} catch (error) {
dispatch({
type: 'LOGIN_FAILURE',
payload: 'Unable to login'
})
}
}
Then throw an error in your login function when you don't get a 200 back:
const login = async () => {
const loginUser = { status: 200, id: '3654634565659abhdgh' }
if (loginUser.status === 200) {
dispatch({
type: 'LOGIN_SUCCESS',
payload: loginUser.id
})
} else {
throw new Error("Invalid Credentials")
}
}
As your login code is in a try catch block, the user won't get pushed to the Dashboard when the login fails.

Resources