Firebase cloud firestore + auth: write only for signed in users - javascript

I am building a vuejs application and I am trying to add firebase auth to the app. I used cloud firestore for a news system.
Now on the "add news" page I call
firebase.auth().signInWithEmailAndPassword("john#doe.com", "mypassword").catch(function (error) {
console.log(error.code);
console.log(error.message);
if (errorCode === 'auth/wrong-password') {
alert('Wrong password.');
} else {
alert(errorMessage);
}
});
to log the user in, or give him feedback if something went wrong.
Later I am writing data to cloud firestore like this
irestore.collection("news").doc().set({
date: today,
title: "Hello",
text: "A think I am a news!"
});
In the firestore rules I set
allow read, write: if request.auth != null;
to the news collection - so it should only grant write access to logged in users, right?
Now the thing:
If i log in with a wrong password, firebase gives back, that the password was incorrect (so we are not logged in are we?) but the data is written to firestore anyways. What did I do wrong?

Your rule request.auth != null will check if the user is signed in via any method. You can check on the client side for sign in two ways:
Sync:
// Synchronously check for current user
var user = firebase.auth().currentUser;
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
Async:
// Listen for current user status
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
If you get a user then you can expect request.auth to not be null.

Related

Keep logged in between reloads - Firebase (Sign in with Google)

How do you keep a user logged in with Sign in with Google between reloads?
Here is my login function (Firebase):
const loginWithGoogle = async () => {
try {
const provider = new firebase.auth.GoogleAuthProvider();
const res = await firebase.auth().signInWithPopup(provider);
$user = res.user;
$isLoggedIn = true;
}
catch (error) {
console.log(error);
}
}
Although $isLoggedIn and the $user object save to LocalStorage (I'm using SvelteJS), the user does not actually stay logged in after reloading.
After reloading, admin users are no longer able to write to the database and I get the error firestore: PERMISSION_DENIED: Missing or insufficient permissions.
(Here are my firestore rules)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if (request.auth != null && (request.auth.uid == "someAdminID" || request.auth.uid == "otherAdminID"));
}
}
How would I stay logged in after reloading? Or is there some way to automatically log in again if the user had not previously logged out?
On most browser environments Firebase automatically persists the user credentials in local storage, and restores them when the app/page reloads. This requires it to make a call to the server however, a.o. to check if the account was disabled, and thus it isn't completed right away when the page loads.
To react to when the authentication state is restored (or otherwise changes), you'll want to use an auth state listener as shown in the first code snippet in the documentation on getting the currently signed in user:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
// 👈 This is where you can also query the database as the user for the first time
} else {
// User is signed out
// ...
}
});

Firebase email/password authentication in electron

I did firebase authentication with email/password in my electron app, and it works, but only on first page. When I go to second page, I'm no longer signed in. Because I'm new to elecetron.js and firebase as well I used this tutorial:https://www.youtube.com/watch?v=bWS0ocfszmE.
login.js
loginBtn.addEventListener('click', function() {
var emailField = document.getElementById('email').value;
var passwordField = document.getElementById('password').value;
firebase.auth().signInWithEmailAndPassword(emailField, passwordField).then(function() {
document.location.href = 'mainPage.html';
console.log(firebase.auth().currentUser);
}).catch(function(error) {
if (error != null) {
console.log(error.message);
alertify.error(error.message);
return;
}
});
secondpage.js
var firebase = require("firebase/app");
require("firebase/auth");
console.log(firebase.auth().currentUser);
I expected the output in console with user that I signed in, but get null.
The problem is that on each new page Firebase Authentication will check whether the user's sign-in token is still valid. Since this may take some time (and require calling a server), this happens asynchronously. And by the time your console.log(firebase.auth().currentUser) runs, the process hasn't completed yet.
That's why you'll want to use an onAuthStateChanged listener to detect the authentication state of the user:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});

Validate displayName firebase authentication

I'm currently working with Firebase Authentication and absolutely loving it. The only problem right now is that I'm trying to validate the user's display name using my own rules. What I have tried is to use Cloud Firestore to store the display name and other related info such as address, etc. On registration, the user has to declare his/her display name as well as email and password. Somewhere in my code I'm doing this:
try {
await firebase.auth().createUserWithEmailAndPassword(email.value, password.value);
await firebase
.firestore()
.collection('/users')
.doc(firebase.auth().currentUser.uid)
.set({
displayName: displayName.value,
});
} catch (err) {
setIsLoading(false);
setErrorText(err.message);
}
The problem is that the app also redirects the user when he/she finishes his/her registration to the private area like so:
useEffect(() => {
if (authUser) history.push('/dashboard');
}, [authUser, history]);
(authUser is stored in Redux and is updated inside onAuthStateChanged()) The display name won't be validated because as soon as firebase.auth().createUserWithEmailAndPassword() resolves, authUser has a value and immediately redirects the user. It's kind of annoying and would love to know whether there's a better way of organising this bit of code.
Any help is greatly appreciated!
Edit: Firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{documentId} {
allow read: if request.auth != null
allow write: if request.auth != null &&
request.resource.data.displayName is string &&
request.resource.data.displayName.size() >= 6 &&
request.resource.data.displayName.size() <= 20
}
}
}

React Native - Firebase auth persistence not working

Firebase auth does not persist logged in user and everytime I refresh or reopen app I have to sign in again.
I have tried setting persistence to local and the callback does verify its set but the persistence is still no working
For setting persistence I am using...
//set auth persistence
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then(function() {
console.log("successfully set the persistence");
})
.catch(function(error){
console.log("failed to ser persistence: " + error.message)
});
.
.
.
For signing in I am using this code
firebase.auth().signInWithEmailAndPassword(email, password)
.then((user) =>{
this.checkAccountStatus(user.uid, user.email);
})
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorMessage)
// ...
});
And here is the code I am using to check login status...
if (firebase.auth().currentUser) {
const currentUser = firebase.auth().currentUser;
console.log("Signed in username" + currentUser.displayName);
this.props.navigation.navigate('AppTab');
}else{
console.log("no user signed in");
this.props.navigation.navigate('AuthTab');
}
if there anything I am not doing right
You don't need to set persistence. Firebase handles it for you by default. You just need to call this function to check whether user is logged or not:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('user is logged');
}
});
This will not be triggered only if user has sign out or cleaned app data.
You can find more details in the official docs: https://firebase.google.com/docs/auth/web/manage-users
Hope it helps.
Make sure you do not restrict the 'Token Service API' in the console, with the API key you are using. I did not add the service to my key, and it logged me out every 3-4 hours, even with the right code suggested above.
Firebase now recommends to use firestore instead of realtime-database and it manages offline persistence by default. It is clear in its documentation here
You just need to access the user through this code:
if (auth().currentUser !== null) {
console.log('User is logged in');
console.log(auth().currentUser.email);
props.navigation.navigate('Home');
} else {
props.navigation.navigate('Login');
}
This code will prop you to home screen if user is logged-in, even when you close the app. To clear the user's credentials, you manually need to sign-out the user using this code.
try {
auth()
.signOut()
.then(props.navigation.navigate('Login'));
} catch (error) {
Alert.alert('Error', error.toString());
}
You can also check this simple app (used react hooks in it) for further assistance.

Firebase - Auth - discover users who signed up but not verified email

I've set-up a Firebase project which I am using for it's user authentication module. I am also using the firebaseui-web project from Github.
My redirect on sign-on is working fine per this code:
// FirebaseUI config.
var uiConfig = {
'signInSuccessUrl': 'MY_REDIRECT.html',
'signInOptions': [
firebase.auth.EmailAuthProvider.PROVIDER_ID
],
// Terms of service url.
'tosUrl': '<your-tos-url>',
};
When the page loads (i.e. MY_REDIRECT.html) I'm checking the status of the user to see if they have verified their e-mail, and if not then invoke the sendEmailVerification method:
checkLoggedInUser = function() {
auth.onAuthStateChanged(function(user) {
if (user) {
// is email verified
if(user.emailVerified) {
// show logged in user in UI
$('#loggedinUserLink').html('Logged in:' + user.email + '<span class="caret"></span>');
} else {
// user e-mail is not verified - send verification mail and redirect
alert('Please check your inbox for a verification e-mail and follow the instructions');
// handle firebase promise and don't redirect until complete i.e. .then
user.sendEmailVerification().then(function() {
window.location.replace('index.html');
});
}
} else {
// no user object - go back to index
window.location.replace("index.html");
}
}, function(error) {
console.log(error);
});
};
window.onload = function() {
checkLoggedInUser()
};
All good so far - Firebase is doing what I want! Thanks guys :)
However, in the Firebase Console UI there doesn't appear to be a way of seeing if a user actually went to their inbox and clicked on the link to perform the verification. The UI looks like this:
I've run basic tests and the User UID doesn't change before and after verification has taken place.
So, here's my question - did I go about the e-mail verification correctly? If so (and therefore the UI doesn't show me verified vs unverified) is there an accepted method of accessing these two sets of users in the Auth module? I can't see the way to access the underlying table of UIDs and their properties (including the emailVerified property). I don't mind having to write more code if the functionality isn't in the Console - just looking to get nudged in the correct direction for my next step.
There is currently no way in the Firebase Console to see whether a specific user's email address has been verified. There is an API to get a list of users, but you can't filter on whether they're verified or not.
You can check whether the currently authenticated user's email address is verified with:
firebase.auth().currentUser.emailVerified
You cannot prevent who signs up. But you can easily ensure that only users with a verified email address can access (certain) data. For example:
{
"rules": {
".read": "auth != null && auth.token.email_verified",
"gmailUsers": {
"$uid": {
".write": "auth.token.email_verified == true &&
auth.token.email.matches(/.*#gmail.com$/)"
}
}
}
}
The above rules ensure that only users with a verified email address can read any data and only users with a verified gmail address can write under gmailUsers.

Categories

Resources