Firebase anonymous auth is triggering both sides of a JS if statement - javascript

I have followed the Google reference documents but find that the firebase auth triggers both sides of the if statement.
calcbtn.addEventListener('click', e => {
//Auth
firebase.auth().signInAnonymously().catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(`${errorCode}: ${errorMessage}`);
// ...
});
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
var isAnonymous = user.isAnonymous;
uid = user.uid;
console.log(`UserID: ${uid}`);
// ...
} else {
// User is signed out.
console.log('Error: User is not authenticated');
// ...
}
// ...
});
Returns both the userID and the Error: User is not authenticated?

Initially when you attach the onAuthStateChanged, no user will be signed in yet. So at that point your callback will be called with null.
Then the user sign in triggered by signInAnonymously() completes and another call is made to your callback with that user object.
This is normal operation for an auth state listener in Firebase: it will usually initially be called with null, and then with the actual user object.

Related

Javascript scope not updating global variable

I'm sitting in my js file trying to upload but for some reason, I don't understand entirely. My :: myUid isn't updating. Can anyone tell me how to fix it and maybe even why it's the case that myUid isn't updating?
var myUid = '33';
firebase.auth().signInAnonymously().catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
var isAnonymous = user.isAnonymous;
window.myUid = user.uid;
// ...
} else {
// User is signed out.
// ...
}
// ...
});
function writeUserData() {
database.ref('users/').set({
profileID: myUid,
});
};
writeUserData();
Best regards, Sam
myUid value is updated only when the callback for onAuthStateChanged is executed asynchronously. By the time you invoke the writeUserData() function on your main execution flow, the above callback may not be have been completed yet.
So a better approach would be to get rid of the global variable and invoke the writeUserData function from the onAuthStateChanged callback, passing the user uid as a parameter.
function writeUserData(userUid) {
database.ref('users/').set({
profileID: userUid,
});
};
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
var isAnonymous = user.isAnonymous;
writeUserData(user.uid);
// ...
} else {
// User is signed out.
// ...
}
// ...
});

Firebase javascript auth user displaying null even though user is signed in

I followed the Firebase docs for web development and I used the user.updateProfile method to add a display name to the user's profile. After signing in, I used console.log(user) and it worked but when I call updateProfile(), the value of user is null. Any solutions?
Here is the relevant code:
var button = document.getElementById("profile-button");
var username = document.getElementById("username-Box").value;
var user = firebase.auth().currentUser;
auth.onAuthStateChanged(user =>{
console.log(user);
})
function updateProfile(){
if (user != null){
user.updateProfile({
displayName : username
}).then(user => {
console.log("Updated Successfully");
window.location.href="chatpage.html";
}).catch(err =>{
console.log(err);
window.alert(err);
});
}else if(user == null){
console.log("No user signed in");
}
}
You need to wait for onAuthStateChanged to fire before assigning the user variable, otherwise the Auth object may be in an intermediate state. This is documented:
By using an observer, you ensure that the Auth object isn't in an intermediate state—such as initialization—when you get the current user. When you use signInWithRedirect, the onAuthStateChanged observer waits until getRedirectResult resolves before triggering.
You can also get the currently signed-in user by using the currentUser property. If a user isn't signed in, currentUser is null:
It's worth explicitly pointing out that the user variable you console.log in onAuthStateChanged is not the same user variable that's used in your updateProfile method. While the user maybe "logged in" when onAuthStateChanged fires, they are likely not logged in when you set your outer user variable. Therein lies your problem.
It's not clear from your code where updateProfile is called, but Peter Haddad's answer is likely the solution I would implement. However, note that with the code snippet supplied in that answer you'll also need to change your updateProfile method to accept a user parameter. Another approach would be to assign the user variable inside of onAuthStateChanged.
let user;
auth.onAuthStateChanged(u => user = u);
With that approach your updateProfile method should work as is. Just keep in mind that you may have a race condition depending on when you call updateProfile.
Since console.log(user) is returning the correct user, then inside the authstatechanged call the updateProfile:
auth.onAuthStateChanged(user =>{
console.log(user);
updateProfile(user);
})
Based on your snippet, I am assuming that updateProfile() is your onClick/onSubmit event handler for clicking your "update profile" button.
Because of this, your user is likely to have logged in by the time they press the button and therefore it is safe to use firebase.auth().currentUser in your event handler rather than maintain a user object in the global scope.
var eleButton = document.getElementById("profile-button");
var eleUsername = document.getElementById("username-Box");
function updateProfile() {
let user = firebase.auth().currentUser;
if (!user) {
alert('Please log in before clicking update profile!')
return;
}
// get current value of '#username-Box'
let username = eleUsername.value.trim();
if (!username) {
alert('Please enter a valid username!')
return;
}
user.updateProfile({
displayName : username
}).then(user => {
console.log("Updated Successfully");
window.location.href="chatpage.html";
}).catch(err =>{
console.log(err);
window.alert(err);
});
}
In your original code, you had the following:
var user = firebase.auth().currentUser; // <-- global-scope variable
auth.onAuthStateChanged(user =>{ // <-- function-scope variable
console.log(user);
})
When onAuthStateChanged fires, it does log the value of user, but it sets and logs it's own version of user, not the user variable in the global-scope.
If you wanted to update the global-scope version, you need to rename the variable used in your onAuthStateChanged handler so that it doesn't shadow the user global-scope variable.
var user = firebase.auth().currentUser; // <-- global-scope variable
auth.onAuthStateChanged(_user =>{ // <-- function-scope variable
user = _user;
console.log(user);
})

Firebase logout user all sessions

I am using Firebase authentication in my iOS app. Is there any way in Firebase when user login my app with Firebase then logout that user all other devices(sessions)? Can I do that with Firebase admin SDK?
When i had this issue i resolved it with cloud functions
Please visit this link for more details https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens
Do the following;
Set up web server with firebase cloud functions (if none exists)
use the admin sdk(thats the only way this method would work) - [Visit this link] (
(https://firebase.google.com/docs/admin/setup#initialize_the_sdk).
Create an api that receives the uid and revokes current sessions as specified in the first link above
admin.auth().revokeRefreshTokens(uid)
.then(() => {
return admin.auth().getUser(uid);
})
.then((userRecord) => {
return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
})
.then((timestamp) => {
//return valid response to ios app to continue the user's login process
});
Voila users logged out. I hope this gives insight into resolving the issue
Firebase doesn't provide such feature. You need to manage it yourself.
Here is the Firebase Doc and they haven't mentioned anything related to single user sign in.
Here is what you can do for this-
Take one token in User node (Where you save user's other data) in Firebase database and regenerate it every time you logged in into application, Match this token with already logged in user's token (Which is saved locally) in appDidBecomeActive and appDidFinishLaunching or possibly each time you perform any operation with Firebase or may be in some fixed time interval. If tokens are different logged out the user manually and take user to authenticate screen.
What i have done is:
Created collection in firestore called "activeSessions".User email as an id for object and "activeID" field for holding most recent session id.
in sign in page code:
Generating id for a user session every time user is logging in.
Add this id to localstorage(should be cleaned everytime before adding).
Replace "activeID" by generated id in collection "activeSessions" with current user email.
function addToActiveSession() {
var sesID = gen();
var db = firebase.firestore();
localStorage.setItem('userID', sesID);
db.collection("activeSessions").doc(firebase.auth().currentUser.email).set({
activeID: sesID
}).catch(function (error) {
console.error("Error writing document: ", error);
});
}
function gen() {
var buf = new Uint8Array(1);
window.crypto.getRandomValues(buf);
return buf[0];
}
function signin(){
firebase.auth().signInWithEmailAndPassword(email, password).then(function (user) {
localStorage.clear();
addToActiveSession();
}
}), function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode === 'auth/wrong-password') {
alert('wrong pass');
} else {
alert(errorMessage);
}
console.log(error);
};
}
Then i am checking on each page if the id session in local storage is the same as "activeID" in firestore,if not then log out.
function checkSession(){
var db = firebase.firestore();
var docRef = db.collection("activeSessions").doc(firebase.auth().currentUser.email);
docRef.get().then(function (doc) {
alert(doc.data().activeID);
alert(localStorage.getItem('userID'));
if (doc.data().activeID != localStorage.getItem('userID')) {
alert("bie bie");
firebase.auth().signOut().then(() => {
window.location.href = "signin.html";
}).catch((error) => {
// An error happened.
});
window.location.href = "accountone.html";
} else{alert("vse ok");}
}).catch(function (error) {
console.log("Error getting document:", error);
});
}
PS: window has to be refreshed to log inactive session out.

Handling linking accounts in Firebase

I am following Firebase's instruction on social login. Below is an example of what I am using and it is all working fine from a login authentication perspective.
I have, however, both Google and Facebook login working independently.
What I would like now to be able to do is link the accounts. You can see below in fact where this might go (see the comment):
If you are using multiple auth providers on your app you should handle linking the user's accounts here.
I have tried many variations of what I think should go here, but to no avail. Can anyone guide me in relation to what they think should go here? Thanks!
function initFBApp() {
// Result from Redirect auth flow.
// [START getidptoken]
firebase.auth().getRedirectResult().then(function (result) {
if (result.credential) {
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
var token = result.credential.accessToken;
// [START_EXCLUDE]
document.getElementById('FBquickstart-oauthtoken').textContent = token;
}
else {
document.getElementById('FBquickstart-oauthtoken').textContent = 'null';
// [END_EXCLUDE]
}
// The signed-in user info.
var user = result.user;
}).catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// [START_EXCLUDE]
if (errorCode === 'auth/account-exists-with-different-credential') {
alert('You have already signed up with a different auth provider for that emails.');
// If you are using multiple auth providers on your app you should handle linking
// the user's accounts here.
}
else {
console.error(error);
}
// [END_EXCLUDE]
});
// [END getidptoken]
// Listening for auth state changes.
// [START authstatelistener]
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
var displayName = user.displayName;
var email = user.email;
var emailVerified = user.emailVerified;
var photoURL = user.photoURL;
var isAnonymous = user.isAnonymous;
var uid = user.uid;
var providerData = user.providerData;
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in-status').textContent = 'Signed in';
document.getElementById('FBquickstart-sign-in').textContent = 'Log out';
document.getElementById('FBquickstart-account-details').textContent = JSON.stringify(user, null, ' ');
// [END_EXCLUDE]
}
else {
// User is signed out.
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in-status').textContent = 'Signed out';
document.getElementById('FBquickstart-sign-in').textContent = 'Log in with Facebook';
document.getElementById('FBquickstart-account-details').textContent = 'null';
document.getElementById('FBquickstart-oauthtoken').textContent = 'null';
// [END_EXCLUDE]
}
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in').disabled = false;
// [END_EXCLUDE]
});
// [END authstatelistener]
document.getElementById('FBquickstart-sign-in').addEventListener('click', toggleFBSignIn, false);
}
These are roughly the steps on how to handle auth/account-exists-with-different-credential:
You will get that error if you are signing in to a new Facebook account that uses the email of another account that already exists. Let's say the existing account is a google account.
You will get that error in getRedirectResult().catch(function(error) {})
The error will also contain an email and credential field.
You will need to save the credential (using the recommended sessionStorage). Check this post for more on that:
Firebase Authentication Javascript: setCookie for pending Credential for redirect
You then call firebase.auth().fetchProvidersForEmail(error.email) to determine the providers that already exist for that email.
You will then sign in to one of those existing providers and assert that the email is the same as error.email. On success, you will load the pending credential from sessionStorage, re-initialize as described in the other post and link it to the currentUser:
firebase.auth().currentUser.linkWithCredential(savedCred);
You will now have both accounts linked. Keep in mind the existing provider could be a password type. In that case you don't need to save the credential, you just ask the user for the password and sign them in using the same email error.email. You can then call link directly with the error.credential.
BTW, I recommend firebaseui-web which takes care of all this for you:
https://github.com/firebase/firebaseui-web
I think the Firebase API changed a bit and firebase.auth().currentUser.link(savedCred);
is now firebase.auth().currentUser.linkWithRedirect(provider). In my implementation I'm saving the initially selected provider to sessionStorage and use that with the above method in case account linking is required.
You can also do linkWithPopUp if that suits your needs better.
read example carefully https://firebase.google.com/docs/auth/web/google-signin
section "Handling account-exists-with-different-credential Errors"
Redirect mode This error is handled in a similar way in the redirect
mode, with the difference that the pending credential has to be cached
between page redirects (for example, using session storage).
Below is the relevant snippet of working code (this sits inside an async function). Note that "apples" is just a simplified test record in Firestore representing a shopping cart.
if(error.code === "auth/email-already-in-use"){
// REMEMBER AUTH CURRENT USER OBJECT
previousUser = firebase.auth().currentUser;
// WE MUST HANDLE DB READ AND DELETE WHILE SIGNED IN AS PREVIOUS USER PER FIRESTORE SECURITY RULES
if(localUserDoc){ //this was saved from .snapshot firing
if(localUserDoc.data().apples){
apples = localUserDoc.data().apples;
}
}
//DELETE CURRENT USER RECORD WHILE STILL SIGNED IN
await firebase.firestore().collection("users").doc(previousUser.uid).delete();
// CLEAN UP DONE. NOW SIGN IN USING EMAIL LINK CREDENTIAL
try {
var firebaseUserObj = await firebase.auth().signInAndRetrieveDataWithCredential(credential);
// FIRESTORE USER RECORD FOR EMAIL LINK USER WAS CREATED WHEN THEY ADDED APPLE TO CART
try {
var doc = await firebase.firestore().collection("users").doc(firebaseUserObj.user.uid).get();
if (doc.exists) {
if(doc.data().apples){
apples = apples + doc.data().apples;
}
}
await firebase.firestore().collection("users").doc(firebaseUserObj.user.uid).update({
apples: apples
});
} catch(error) {
console.log("Error getting document:", error);
}
previousUser.delete();
} catch (error) {
console.log(".signInWithCredential err ", error);
}
}

Firebase custom login fails (JavaScript)

I'm trying to get started with firebase and now with the security part of it. I'm trying to keep it as simple as possible in order to get started, using guides and code snippets from the Firebase website.
In order to keep it simple I have a webpage containing a password (id "Code") and user input field (id "Door"). How do I check if the password entered in field "Code" is equal to the password that is already stored in node https://mydatabase.firebaseio.com/loapp_users/BAAJ/password, BAAJ being a userid of one of the users stored in node loapp_users, all with a child node "password"?
The code below doesn't seem to do the trick.
$(document).ready(function(){
// Monitoring User Authentication State
// Use the onAuth() method to listen for changes in user authentication state
// Create a callback which logs the current auth state
function authDataCallback(authData) {
if (authData) {
console.log("User " + authData.uid + " is logged in with " + authData.provider);
} else {
console.log("User is logged out");
}
}
// Register the callback to be fired every time auth state changes
var ref = new Firebase("https://mydatabase.firebaseio.com");
ref.onAuth(authDataCallback);
$("#logout").click(
function logout() {
ref.unauth();
ref.offAuth(authDataCallback);
}
);
// LOGIN
// The code to authenticate a user varies by provider and transport method, but they all have similar signatures and
// accept a callback function. Use it to handle errors and process the results of a successful login.
// Create a callback to handle the result of the authentication
function authHandler(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
};
$("#login").click(
function() {
var usersRef = new Firebase("https://mydatabase.firebaseio.com/loapp_users");
// Authenticate users with a custom Firebase token
var _user = $("#Door").val();
var _level = "docent";
var _password = $("#Code").val();
var userRef = usersRef.child(_user);
// Attach an asynchronous callback to read the data at our user reference
userRef.on("value", function(snapshot) {
console.log(snapshot.val());
if (snapshot.val().child("password").text() == _password) {
ref.authWithCustomToken("eyJ0e....etc...mlhdCI6MTQyOTM4Mzc0M30.Vn1QF7cRC6nml8HB9NAzpQXJgq5lDrAie-zIHxtOmFk", authHandler);
} else {
console.log("Gebruikersnaam en code komen niet overeen")
}
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
);
});
snapshot.val().child("password").text()
should instead be:
snaphot.val().password
Then it works.

Categories