Issues With Persistent React Native Login - javascript

I am trying to make a persistent login system with React Native and expo but am running into several issues. I read online that AsyncStorage is the way to do this. I wrote three functions for dealing with login, all seen below.
// use asyncstorage to log in the user
logInUser = async (uid) => await AsyncStorage.setItem('loggedin', uid)
// set loggedin to null in asyncstorage
logOutUser = async() => await AsyncStorage.setItem('loggedin', null)
// returns userid if user is logged in
getUserState = async() => await AsyncStorage.getItem('loggedin')
On my login screen, I use the following onPress event to log in the user.
onPress={() => {
db.logInUser(this.user[1]).then(() => {
//this.removekey(this.user[1]) // delete the user's one-time-login key
this.props.navigation.navigate('Home') // navigate to home
})
}}
Also on the login screen, I use the following componentDidMount function to send the user to the homescreen if they are already logged in.
async componentDidMount() {
db.getUserState().then(loggedin => {
if (loggedin != null) { // log the user in if they have a uid in asyncstorage
this.props.navigation.navigate('Home')
}
})
}
The app will not have a "logout" feature, and users should stay logged in until either buying a new phone or reinstalling the app. Unfortunately this code does not work, and actually automatically logs the user in. I was thinking that it could maybe relate to the user with id=0, but removing this user from the database had no effect. The code of the actual application is here.

Related

Trying to block authentication if user is in the banned collection

I am building a chat app and I am trying to figure out the banning system. The idea if the user is in the banned collection becaused he used banned words, block authentication from his account. The problem is that for some reason it does execute the code but rather only does the signInWithRedirect() function. Take a look at my code:
const googleProvider = new GoogleAuthProvider();
const signInWithGoogle = async () => {
try {
const res = await signInWithRedirect(auth, googleProvider);
const user = res.user;
const q = query(collection(db, "banned"), where("uid", "==", user.uid));
const docs = await getDocs(q);
if (docs.exists()) {
//if exists ban the user
console.log('you cannot use this chat app , you are banned!)
Here is also the LoginPage.jsx
function LoginPage() {
const [user] = useAuthState(auth)
const navigate = useNavigate()
console.log(user)
return (
<div>
<div style={{'display': 'none'}}>
<Navbar />
</div>
<Welcome />
</div>
)
}
Here is also signOut
const navigate = useNavigate()
const signOutWithGoogle = () => {
signOut(auth)
navigate('/')
}
Here is firebase firestore
I tried this with promises but nothing happened , I used async , nothing happened
I would not implement such a "banning" system only based on front-end code. It would actually be quite easy for a malicious end-user to bypass your check based on a standard query to a collection.
I would use a Firebase Security Rule to enforce the banishment: Security Rules stand between your data and malicious users and are executed/checked in the back-end. To be complete, with this approach the user will still be authenticated and signed-in but will not be able to interact with the Firebase back-end services like Firestore or Cloud Storage.
Concretely you could as follows:
Write a security rule based on a specific collection in which you create a document per banned user with the user ID as document ID. Then you can use the exists() method to check if the user is banned or not.
Use a Cloud Function to ban (and maybe "un-ban") a user. Again, with Cloud Function the code is executed in the back-end and you therefore avoid the possibility that a malicious user writes a code that could "un-ban" him.

firebase - if statement not executed after query

I have integrated firebase in a vue app. I have a navbar that can show some user informations like name and status, and I need to query firebase realtime database after the login to set the needed informations inside my store that will manage application state.
I'm using this code when the navbar component is mounted but the if() statement will be not executed. The strange thing is that I'm able to log each user during the loop. Is there something wrong or another solution to achive this?
onValue(query(ref(db, 'Users'), orderByKey('uid')), users => {
users.forEach( (user) => {
let userData = user.val()
// the console will log each user correctly
console.log(userData)
if( userData.uid == store.currentUser.uid ) {
console.log('called')
console.log(userData)
}
})
})

Firebase: use updated profile with onAuthStateChanged()

I have a little twisted problem with my React app. I'm using Firebase for authentication. What I need to do is to register a user and set their displayName immediately. Entire problem could be avoided if Firebase allowed to call createUserWithEmailAndPassword() without signing user in, but it doesn't.
I do this:
const submitHandler = async event => {
event.preventDefault();
const auth = await firebase.auth();
const { user } = await auth.createUserWithEmailAndPassword(email, password);
await user.updateProfile({
displayName: userName
});
};
The problem is that I'm using firebase.auth().onAuthStateChanged() in my root component to handle current user state, so it's called as soon as createUserWithEmailAndPassword() is done, before the profile is updated. I'm not sure how to tell onAuthStateChanged() about the updated displayName.
I managed to create a working solution, but I'm not happy with it. I tricked onAuthStateChanged() to ignore users without displayName like so:
useEffect(() => {
const unsubscribeAuth = firebase.auth().onAuthStateChanged((user) => {
if (user === null || user.displayName !== null) {
setLoggedInUser(user);
}
});
return () => unsubscribeAuth();
}, []);
And in my sign up component, I do this:
const submitHandler = async event => {
event.preventDefault();
const auth = await firebase.auth();
const { user } = await auth.createUserWithEmailAndPassword(email, password);
await user.updateProfile({
displayName: userName
});
await auth.signOut();
await auth.updateCurrentUser(user);
};
That's obviously hackish and slower than necessary, because I'm signing in twice. It works as intended though, ignoring the auto-login, then signing out (doing nothing, as the user state hasn't been updated before) and then signing in, triggering onAuthStateChanged() with the updated user.
How can I improve my app to sign up users without this double madness?
Updating the user profile not normally execute the onAuthStateChange listener, as (usually) the authentication state won't change because of this API call.
You can force a reload of the user's ID token to get their latest profile information.

Cypress login using request method

I register & login a user, however, when in my test I navigate to a page behind authentication, Cypress fails & takes me back to the login page. From the looks of it, the before function is successfully executed (as verified by the API log). Here is my code:
describe("Dashboard page", () => {
before(() => {
cy.fixture("authUserRegistrationDetail.json").then(userDetail => {
cy.fixture("authUserLoginDetail.json").then(userLoginDetail => {
cy.visit("http://localhost:3000/login");
cy.get(".cookieConsent button").click();
// create a random email for registration
userDetail.email = `${Math.random()
.toString(36)
.slice(-5)}#aaa.aaa`;
// share the email between userLogin & userRegistration obj
userLoginDetail.email = userDetail.email;
// register the user
cy.request("POST", "http://localhost:9000/users/", userDetail)
.its("body")
// login the same user
cy.request("POST", "http://localhost:9000/api-token-auth/", userLoginDetail).then($res => {
cy.request({
url: "http://localhost:9000/loggedinuser/",
headers: {
Authorization: `Token ${$res.body.token}`
}
});
});
});
});
});
// run the test
it("visits the dashboard...", () => {
cy.visit("http://localhost:3000/dashboard/");
cy.get("h2").contains("Your deals");
});
});
Once the code is run, the test fails on assertion and the user is not logged in. Here is the screenshot of the test result. I get a status code 200 when user signs up & then logs in. Why is the user login not persisting in the tests & the dashboard link fails.
EDIT:
I just realised that I am programmatically logging in, however, once logged in, how do I get Cypress browser to recognise the change in state & that the user is logged in. I.e, how do I refresh the Cypress screen to recognise the the user login?
From the above code, it doesn't look like you are preserving the cookie once logged in. Cypress automatically clears all cookies before each test to prevent state from building up. You should be able to do something similar to this:
before(() => {..cy.login() })
beforeEach(() => {
Cypress.Cookies.preserveOnce('session_id', 'remember_token')
})
This cypress doco should provide more context https://docs.cypress.io/api/cypress-api/cookies.html#Preserve-Once

Firebase - keep user signed in while localStorage object is valid

I have an app I'm building with Ionic, Angular2, and Firebase.
What I'm currently doing is that when a user log's in or registers, their auth.currentUser information is being saved in localStorage. My code is currently assuming that if a user has the user variable in localStorage set that the user is also signed in. However, I just realized that that isn't actually the case.
My localStorage variable user looks like this:
and I'm getting it like this:
ngOnInit() {
Promise.all([ //data storage get all..
this.storage.get('user')
])
.then(([user]) => {
this.userData = user; //set the user storage to this.userData
console.log(this.userData)
})
.catch(error => {
console.log("Storage Error.");
});
}
So I have all of the users information, except the fact that they aren't actually signed in... So when I do:
this.afAuth.auth.onAuthStateChanged((user) => {
if (user) {
console.log(user)
} else {
console.log("Not logged in")
}
});
It shows the user isn't logged in. Is it possible for me to keep the user logged in while I have the localStorage information?
Or maybe just have the login state always be logged in unless the user signs out? With it never expiring?
What should I do at this point? Any advice would be great! Thank you.
Firebase manger user in a different way. You should not depend on localstorage to detect if user is logged it. You should depend on firebase to check if user is logged in. Since firebase is built on web sockets, it should be really quick. So let rewrite the ngOnInit as follows:
ngOnInit() {
const user = this.afAuth.auth.currentUser;
if (user) {
this.userData = user;
} else {
// force them to reauthenticate
user.reauthenticateWithCredential(credential)
.then(function() {
// User re-authenticated.
this.userData = this.afAuth.auth.currentUser;
}).catch(function(error) {
// An error happened.
});
}
}
Read more about managing users in Firebase here: https://firebase.google.com/docs/auth/web/manage-users. Hope this helps.

Categories