I am updating the Firebase user profile photoURL
it's upadted in the store but not in the Firebase users db...
when I signout then signin with this user, the photoURL is NOT changed
here is my store action
updateProfilePhotoURL ({commit}, payload) {
const updateFBUserProfile = async (commit, payload) => {
commit(types.SET_LOADING, true)
let db = firebase.database()
const updatedData = {
photoURL: payload.photoURL
}
// Update the Firebase user profile too...
await db.ref('users/' + payload.uid).update(updatedData)
// Update the store profile too...
commit(types.UPDATE_PROFILE_PHOTO_URL, updatedData.photoURL)
return 'ok'
}
return new Promise((resolve, reject) => {
updateFBUserProfile(commit, payload)
.then(result => {
commit(types.SET_LOADING, false)
resolve(result)
}, error => {
console.log('ERROR: ', error)
reject(error)
})
})
}
where am I wrong ?
thanks for feedback
According to question comments - I think your user has no permissions to write to db. You can check it with firebase.auth().currentUser or check your Rules for database in Firebase Console.
Related
please help.
I'm trying to ensure that the user does not log out after reloading the page.
const signUp = async (loginEmail, loginPassword) => {
const auth = getAuth()
setPersistence(auth, browserSessionPersistence)
.then(({ user }) => {
dispatch(
setUser({
email: user.email,
id: user.uid,
token: user.accessToken,
})
)
return signInWithEmailAndPassword(auth, loginEmail, loginPassword)
})
.catch((error) => {
console.log(error.message)
})
}
This is what the example looks like in the official documentation
const auth = getAuth();
setPersistence(auth, browserSessionPersistence)
.then(() => {
// Existing and future Auth states are now persisted in the current
// session only. Closing the window would clear any existing state even
// if a user forgets to sign out.
// ...
// New sign-in will be persisted with session persistence.
return signInWithEmailAndPassword(auth, email, password);
})
.catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
});
The recommended way to get the current user is by setting an observer on the Auth object:
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
documentation
When the user clicks on the "sign-in" button and if user.challangeName === 'NEW_PASSWORD_REQUIRED'is true, I redirect the user to a page (form screen) where he can provide the input for required attributes and a new password. Even tho the user object is getting populated upon clicking on the sign-in button, using Auth.currentsession on the form screen will print "No user found"
Can someone let me know why I'm seeing no user? What am I doing wrong here?
Here's my login function (triggered when clicked on sign-in button) where I direct the user to the change password screen (form screen) if user.challangeName === 'NEW_PASSWORD_REQUIRED' is true.
const login = async (email, password) => {
try {
const user = await Auth.signIn(email, password);
if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
navigate('/change-password');
return;
}
if (user) {
setToken(user.signInUserSession.idToken.jwtToken);
const userDetails = await getAccountDetails();
dispatch({
type: LOGIN,
payload: {
user: {
attributes: user.attributes,
username: user.username
},
client: userDetails
}
});
}
} catch (error) {
await logout();
throw error;
}
};
Here's my onSubmit function on the change password screen where eventually I want to use Auth.completeNewPassword to update the user's password in Cognito
const onSubmitClick = (e) => {
e.preventDefault();
if (validateFields());
Auth.currentSession()
.then((user) => console.log(user))
.catch((err) => console.log(err));
};
Here's the documentation provided by AWS https://docs.amplify.aws/lib/auth/manageusers/q/platform/js/#forgot-password, and the code provided by AWS to update password
Auth.signIn(username, password)
.then(user => {
if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
const { requiredAttributes } = user.challengeParam; // the array of required attributes, e.g ['email', 'phone_number']
Auth.completeNewPassword(
user, // the Cognito User Object
newPassword, // the new password
// OPTIONAL, the required attributes
{
email: 'xxxx#example.com',
phone_number: '1234567890'
}
).then(user => {
// at this time the user is logged in if no MFA required
console.log(user);
}).catch(e => {
console.log(e);
});
} else {
// other situations
}
}).catch(e => {
console.log(e);
});
New updated answer to reflect your updated post, change the onSubmitClick to the following:
const onSubmitClick = (e) => {
e.preventDefault();
if (validateFields());
Auth.currentAuthenticatedUser()
.then(user => {
console.log(user))
})
.then((data) => console.log(data))
.catch((err) => console.log(err));
};
You are looking at the documentation which shows how to use the user after Auth.signIn() (which was my previous answer), compared to what you use: Auth.currentAuthenticatedUser(). The proper documentation example you have to look at is this one: https://www.docs.amplify.aws/lib/auth/manageusers/q/platform/js
I am receiving the following error when triggering this cloud function: "Error unauthenticated".
I do not wish to allow unauthenticated calls to this cloud function.
The workflow is as follows:
User registers in app via firebase password authentication
Firebase Auth Credentials are created (firebase signs in user upon success)
Once the credentials have been created, the cloud function is triggered in the firebase auth callback.
At this point, the call should be authenticated, given it's being triggered in the firebase auth response.
However, it keeps erroring with
Error: unauthenticated
The user is authenticated at this point.
Any suggestions?
CLIENT CODE ->
const onRegisterPress = () => {
if (password !== confirmPassword) {
alert("Passwords don't match.")
return
}
setLoading(true);
//CREATE'S USER'S AUTH CREDENTIALS
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((response) => {
const data = {
...
}
//console.log(response);
return new Promise(async function(resolve, reject) {
await firebase.functions().httpsCallable('writeAccountUser')({
data
}).then((response) => {
console.log("Write Account User Response: ", response);
resolve(setLoading(false));
}).catch((error) => {
console.error("Cloud Function Error: ", error);
setLoading(false);
reject(error)
})
});
})
.catch((error) => {
alert(error)
});
}
CLOUD FUNCTION ->
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const firestore = admin.firestore();
exports.writeAccountUser = functions.https.onCall((data, context) => {
console.log("Incoming Data: ", data);
console.log("Incoming Context: ", context);
const clientData = data.data;
console.log("Client Data: ", clientData);
console.log("Account ID: ", clientData.accountID);
return new Promise(async function (resolve, reject) {
const accountsRef = firestore.collection('Accounts');
const usersRef = firestore.collection('users');
const now = new Date();
if (clientData.accountExists === true) {
console.log("Account Exists");
await accountsRef
.doc(clientData.accountID)
.update({
users: admin.firestore.FieldValue.arrayUnion(clientData.uid)
}).catch((error) => { console.error(error); reject(false) });
}
else {
console.log("Account Does Not Exist!");
const account_data = clientData.accountData;
const product_lines = clientData.productLines;
await accountsRef
.doc(clientData.accountID)
.set({
account_data,
product_lines,
users: [clientData.uid],
dateCreated: {
date: now,
timestamp: now.getTime()
}
}).catch((error) => { console.error(error); reject(false)});
};
const email = clientData.email;
const fullName = clientData.fullName;
const acceptTerms = clientData.acceptTerms;
const userData = {
id: clientData.uid,
email,
fullName,
accountID: clientData.accountID,
dateCreated: {
date: now,
timestamp: now.getTime()
},
lastUpdateFetch: {
date: now,
timestamp: now.getTime()
},
termsConditionsAccepted: acceptTerms
};
await usersRef
.doc(clientData.uid)
.set(userData)
.catch((error) => { console.error(error); reject(false) });
resolve(true);
});
});
Error ->
[Unhandled promise rejection: Error: unauthenticated]
at node_modules/#firebase/firestore/dist/rn/prebuilt.rn-f9cd27ba.js:12199:33 in
at http:///node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:169743:29 in _errorForResponse
at node_modules/#firebase/firestore/dist/rn/prebuilt.rn-f9cd27ba.js:12747:31 in yu
at node_modules/tslib/tslib.js:77:12 in
at http://REDACTED/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:120748:21 in
at http://REDACTED/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:120702:31 in fulfilled
You can try refactoring the function as shown below:
const onRegisterPress = async () => {
if (password !== confirmPassword) {
alert("Passwords don't match.")
return
}
setLoading(true);
const response = await firebase.auth().createUserWithEmailAndPassword(email, password)
const data = {...}
const fnResponse = await firebase.functions().httpsCallable('writeAccountUser')({data})
console.log("Write Account User Response: ", response);
}
You can also create the account using the Admin SDK in the same function and log the user on your web app after the response. That'll ensure the Cloud function's action has been executed as well (just in case the function is not called after user sign up for any reason).
I am facing to asynchronism problem :
I create a user in firebase, generating a unique ID for it.
I get this unique ID.
I call an async function to persist this ID with AsyncStorage method.
Problem : The asyncStorage method is called before I get back the generated ID from my user creation. How to deal with this ?
This is my code :
class Subscription extends Component {
constructor() {
super();
this.state = {
email: '',
password: ''
}
}
persistUserId = (userID) => {
try {
AsyncStorage.setItem('userId', userID); // Here, user ID is undefined
} catch (error) {
console.log(error.message);
}
};
updateInputValue = (value, prop) => {
const state = this.state;
state[prop] = value;
this.setState(state);
}
registerUser = () => {
var generatedUserId = '';
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password) // Authentication
.then((res) => {
var user = { // Set Javascript Object to insert
email: this.state.email
}
database.collection("users").add({ // Create the new user generating an ID
'email': user.email,
}).then(function(docRef) {
generatedUserId = docRef.id; // Get the generated ID (The one to persist witch asyncstorage)
}).then(function() {
this.persistUserId(generatedUserId) // Call the AsyncStorage to persist the ID
})
this.props.navigation.navigate('AppPage') // Go to next page.
})
.catch(error => {
alert(error.message)
})
}
For persisting data. According to react-native doc. You need to use async await keyword:
_storeData = async () => {
try {
await AsyncStorage.setItem(
'#MySuperStore:key',
'I like to save it.'
);
} catch (error) {
// Error saving data
}
}
for your case:
persistUserId = async (userID) => {
try {
await AsyncStorage.setItem('userId', userID); // Here, user ID is undefined
} catch (error) {
console.log(error.message);
}
};
Note: Persisting data is async process. That's why you need to use async await
You need to update your firebase then catch as well. Either use bind or use arrow function. Here is updated version:
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password) // Authentication
.then((res) => {
var user = {
// Set Javascript Object to insert
email: this.state.email,
};
database
.collection("users")
.add({
// Create the new user generating an ID
email: user.email,
})
.then( (docRef) => {
generatedUserId = docRef.id; // Get the generated ID (The one to persist witch asyncstorage)
})
.then( () => {
this.persistUserId(generatedUserId); // Call the AsyncStorage to persist the ID
});
this.props.navigation.navigate("AppPage"); // Go to next page.
})
.catch((error) => {
alert(error.message);
});
after an hour the user get disconnected from the firebase functions and gets an error.
the connection to firebase in the app work like this:
After the first connection via google, the token is sent to firebase functions.
after that a getIdToken(ture) to force a refresh in useEffect.
the token is saved in the state via mobX and every time a commend requires to send or get data from the data base it's passes the token to the firebase functions
I have noticed that I don't get a new token in .then(function (idToken) {...}
this is the error :
FirebaseAuthError: Firebase ID token has expired.
Get a fresh ID token from your client app and try again (auth/id-token-expired).
See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
...
...
...
> errorInfo: {
> code: 'auth/id-token-expired',
> message: 'Firebase ID token has expired.
Get a fresh ID token from your client app and try again (auth/id-token-expired).
See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
> },
> codePrefix: 'auth'
> }
Things that I have tried already:
is to separate the firebase.auth().currentUser.getIdToken(true).then() to a different useEffect().
call getIdToken() after I get an error from the firebase functions.
const UserSubscriber = () => {
//using mobX here
const { user } = useStores();
const token = user.store.token;
React.useEffect(() => {
if (!token.length || !firebase.auth().currentUser) return;
firebase.auth().currentUser.getIdToken(true).then(function (idToken) {
const decodedToken = jwt.decode(idToken, '', true);
if (!decodedToken.user_id) return;
const unsub = firebase.firestore().collection('users').doc(decodedToken.user_id).onSnapshot(docSnapshot => {
const data = docSnapshot.data();
//user.mergeData() is just to store data
if (!data) return user.mergeData({ noUser: true, token: idToken })
user.mergeData({ ...data, noUser: false, token: idToken })
});
return () => unsub();
}).catch(function (error) {
user.logOut();
});
}, [token, user]);
return useObserver(() => (
<div />
));
}
and in the backend
app.use(async (req, res, next) => {
try {
const decodedToken = await admin.auth().verifyIdToken(req.body.token);
let uid = decodedToken.uid;
req.uid = uid;
return next();
} catch (error) {
console.log(error);
return res.status(401).send();
}
});
I have tried firebase.auth().onAuthStateChanged(userAuth => {...}) (in side of the useEffect())
firebase.auth().onAuthStateChanged(userAuth => {
userAuth.getIdToken().then(function (idToken) {
const decodedToken = jwt.decode(idToken, '', true);
if (!decodedToken.user_id) return;
const unsub = firebase.firestore().collection('users').doc(decodedToken.user_id).onSnapshot(docSnapshot => {
const data = docSnapshot.data();
if (!data) return user.mergeData({ noUser: true, token: idToken })
user.mergeData({ ...data, noUser: false, token: idToken })
});
return () => unsub();
}).catch(function (error) {
user.logOut();
});
})
;
}