I am trying to update the email of the current user, I went to documentation and I copied the example code that they are given there,
But nothing worked, this from doc didn't work
var user = firebase.auth().currentUser;
user.updateEmail("newemail#example.com").then(function() {
console.log('success')
}).catch(function(error) {
console.log('failed')
});
I have also tried :
try {
await user.updateEmail('test#test.com').then(function() {
console.log('success')
}).catch(function(error) {
handleErrors(dispatch, error.message);
});
} catch(e) {
handleErrors(dispatch, e.message);
}
could you help to solve this issue?
update :
sorry i didn't include the exact error that come out in the console , here is the error :
This operation is sensitive and requires recent authentication. Log in
again before retrying this request.
2)
I should add that I am using asyncstorage of react native , and I store that Item( profile of user => email , name , ect..) in reduxPersist
that how my login code is :
export const login = ( email, password ) => {
return async (dispatch) => {
dispatch({ type: ATTEMPTING });
try {
await firebase.auth().signInWithEmailAndPassword(email, password)
.then(resp => handleLoginSuccess(dispatch, resp.uid,resp.name,email))
.catch(error => handleErrorLogin(dispatch, error.message));
}catch(e){
handleErrorLogin(dispatch, e.message);
}
};
};
const handleLoginSuccess = async(dispatch , userId,name,email) => {
try{
const profile = { userId, name, email };
await AsyncStorage.setItem('userProfile', JSON.stringify(profile));
dispatch({ type: LOGIN_SUCCESS, payload: profile });
}catch(e){
alert(e.message);
}
}
The error message is telling you that there is no user logged in at the time you call updateEmail. The SDK doesn't know which user you're trying to modify. You'll need to wait until the login is fully complete before calling that method.
Note that all of the Firebase APIs are asynchronous, so you'll need to make use of their returned promises to make sure the order of calls is valid.
Related
I need to get the jwtToken from the Auth.signUp. Is this possible if i enable autoSignIn:{enabled:true}?
const signUp = async () => {
await Auth.signUp({
username: email,
password,
attributes: {
email, // optional
name,
},
autoSignIn:{
enabled: true
}
})
.then((data) => {
console.log(data.user); //user.signInUserSession is null
})
.catch((err) => {
if (err.message) {
setInvalidMessage(err.message);
}
console.log(err);
});
await Auth.currentAuthenticatedUser()
.then(user =>{
console.log(user)
})
.catch(error => {
console.log(error) //"User is not authenticated"
})
};
I call I want the jwttoken from the userSession data for conditional rendering and I store the token in my router.js. The response object from Auth.signUp contains a CognitoUser which has a signInUserSession value but its's null.
EDIT: Tried to call Auth.currentAuthenticatedUser() after but yields an error that user is not authenticated. But when i restart my app, the user will be authenticated. I still cant authenticate user on the same app "instance"
import { Auth, Hub } from 'aws-amplify';
const listener = (data) => {
switch (data.payload.event) {
case 'autoSignIn':
console.log('auto sign in successful');
console.log(data.payload) //returns user data including session and tokens.
//other logic with user data
break;
}
};
Above is the code to initalize the Hub listener provided by amplify api. Ater user presses sign up, I called to get user session data when user is automatically signed in.
Hub.listen('auth', listener)
I've created a simple createUser function which is executed on call. I have one problem though. The function is crashing when the user is trying to register with an already existing email. I mean, it's ok, since no one wants to have 2 users with the same email address but I want to prevent crushing function, instead, I want to send an error message as a response.
export const createUserTest = functions.https.onCall((data, context) => {
const {email, password} = data;
return new Promise((resolve, reject)=>{
try{
admin
.auth()
.createUser({
email: email,
emailVerified: false,
password: password,
disabled: false,
})
.then((user) => {
resolve({
result: 'success',
user: user,
}) ;
})
.catch((error) => {
reject(error) ;
});
}catch(error) {
reject (error)
}
})
});
I tried to put the function in to try/catch block but it didn't help. Do you have an idea of how I can achieve my goal?
As explained in the doc for Callable Cloud Functions, "to ensure the client gets useful error details, return errors from a callable by throwing (or returning a Promise rejected with) an instance of functions.https.HttpsError".
The error has a code attribute that can be one of the values listed here. In your case, the most appropriate seems to be already-exists.
On, the other hand, you'll find here the Admin SDK Authentication errors list and you'll see that in case the provided email is already in use by an existing user the error code is auth/email-already-exists.
So you can adapt your code as follows:
export const createUserTest = functions.https.onCall((data, context) => {
const { email, password } = data;
return admin
.auth()
.createUser({
email: email,
emailVerified: false,
password: password,
disabled: false,
})
.then((user) => {
return {
result: 'success',
user: user,
}
})
.catch((error) => {
if (error.code === 'auth/email-already-exists') {
throw new functions.https.HttpsError('already-exists', 'The provided email is already in use by an existing user');
} else {
throw new functions.https.HttpsError('...other code....', '...');
// If an error other than HttpsError is thrown, your client instead receives an error with the message INTERNAL and the code internal.
}
});
});
See here in the doc, how to handle errors on the client side. If error.code == 'already-exists' you know that it's because the email is already in use.
I'm coding a "delete account" functionality on my app and I want the user to enter their password again before triggering the deletion.
What would be the best way to implement this? I was thinking on using the "signInWithEmailAndPassword" method and capturing the result to check if the credentials are correct, but I'm afraid that would overwrite the current session.
Any tips?
If the session is too old or whatever, an error is thrown by the 'delete account' function anyways. Then you need to re-auth the user. There is a special function for that too: reauthenticateWithCredential().
Here I have an example to show the difference of the login and re-authenticate functions (copied from a project of mine and cut down a bit because there was some analytics and stuff):
public async reAuthenticate(user: User, { email, password }: IEmailLoginData): Promise<UserCredential> {
const credentials = firebase.auth.EmailAuthProvider.credential(email, password);
return user.reauthenticateWithCredential(credentials)
.catch(e => {
console.error(e);
throw e;
});
}
public async login({ email, password }: IEmailLoginData): Promise<UserCredential> {
return firebase.auth().signInWithEmailAndPassword(email, password)
.catch(e => {
console.error(e);
throw e;
});
}
// PS: IEmailLoginData is a custom interface from me, but it just contains email and password
Also, here is the code for the 'delete account'. It should be pretty self-explanatory - hope it helps a bit:
async delete(): Promise<void> {
const dialogRef = this.dialog.open(YesNoDialogComponent, {
data: {
yes: 'Yes',
no: 'No',
title: 'Are you sure that you want to delete your account?'
}
});
const result = await dialogRef.afterClosed().pipe(take(1)).toPromise();
if (result === IYesNoDialogResult.YES) {
try {
const authUser = await this.auth.nextAuthUser(); // Getting the current firebase user from my custom service
await authUser.delete();
await this.router.navigateByUrl('login');
} catch(e) {
const toast = await this.toast.create({
duration: 3000,
message: 'This is a sensitive operation. Please login again to do this'
});
await toast.present();
await this.router.navigateByUrl('reauth');
});
}
}
For different auth provider it might be slightly different, but in the essence it is still the same. Just for example with google (if you want to use the Ionic Native Google Plus Login Plugin), you need to create the re-authenticate credentials from the plugin result:
public async reAuthenticate(user: User): Promise<UserCredential> {
try {
if (this.platform.is('cordova')) {
try {
const gUser = await this.gPlus.login({
webClientId: environment.googleWebClientId,
offline: true,
scopes: 'profile email'
});
const credential = firebase.auth.GoogleAuthProvider.credential(gUser.idToken);
return await user.reauthenticateWithCredential(credential);
} catch (nativeE) { // If login failed via native method, fallback to redirects
if (nativeE == 12501 || nativeE == 13) { // User cancelled login
return null;
}
console.error(nativeE);
// In constructor:
// this._provider = new firebase.auth.GoogleAuthProvider();
await user.reauthenticateWithRedirect(this._provider);
return await firebase.auth().getRedirectResult();
}
}
else {
return await user.reauthenticateWithPopup(this._provider);
}
} catch (e) {
console.error(e);
throw e;
}
}
I have a Google Cloud Function which I am calling from my RN app but it is returning
[Error: Internal]
I have set the permission to Unauthenticated users so anyone can call it - for testing purposes only. When I set to Authenticated users permission, it throws another error [Error: Unauthenticated] eventhough I am authenticated and I can get the currentUser id in my app.
Tried searching for this error but it didnt send me to any possible solutions so decided to post here and hopefully recieve responses that will help me fix it.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createUser = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
//Checking that the user calling the Cloud Function is authenticated
if (!context.auth) {
throw new UnauthenticatedError('The user is not authenticated. Only authenticated Admin users can create new users.');
}
const newUser = {
email: data.email,
emailVerified: false,
password: data.password,
disabled: false
}
const role = data.role;
const userRecord = await admin
.auth()
.createUser(newUser);
const userId = userRecord.uid;
const claims = {};
claims[role] = true;
await admin.auth().setCustomUserClaims(userId, claims);
return { result: 'The new user has been successfully created.' };
} catch (error) {
if (error.type === 'UnauthenticatedError') {
throw new functions.https.HttpsError('unauthenticated', error.message);
} else if (error.type === 'NotAnAdminError' || error.type === 'InvalidRoleError') {
throw new functions.https.HttpsError('failed-precondition', error.message);
} else {
throw new functions.https.HttpsError('internal', error.message);
}
}
});
in my RN app I am calling it like this:
var user = {
role: role
}
const defaultApp = firebase.app();
const functionsForRegion = defaultApp.functions('europe-west1');
const createUser = await functionsForRegion.httpsCallable('createUser');
createUser(user)
.then((resp) => {
//Display success
});
console.log(resp.data.result);
})
.catch((error) => {
console.log("Error on register patient: ", error)
});
I think the way I am calling it in my RN app is correct because I have tested it with a testFunction and I returned a simple string. So, I believe the problem is somewhere in the function itself.
EDIT: I just tested by simply calling the function and returning the context and it always returns Internal error:
exports.registerNewPatient = functions.region('europe-west3').https.onCall((data, context) => {
return context; //this is returned as INTERNAL error.
}
I just cant get to understand whats going on here, why does it return Internal error when I am authenticated as a user and it should return the authenticated user data, isn't that right?
Try some console.log(context) ; console.log(data) statements in your registerNewPatient function and take a look at the logs. What do they say?
Some other things to consider might include that in your client code you use europe-west1 while your function code has europe-west3. Try to have those line up and see if it works? From my experience, if a specified function isn't found to exist, the client receives an INTERNAL error.
I am using the ConnectyCube React Native SDK and have obtained an app auth token using their API. This token is required when making further requests - for example when logging in as a user. Their documentation says:
Upgrade session token (user login)
If you have an application session token, you can upgrade it to a user session by calling login method:
var userCredentials = {login: 'cubeuser', password: 'awesomepwd'};
ConnectyCube.login(userCredentials, function(error, user) {
});
The problem is it that when I use this method, I get an error in response saying 'Token is required'.
If I were interfacing with a REST API, I would put the token in the header of the request, but obviously in this instance I can't. So the question is, where do I put the token? I have it, the documentation just doesn't tell you how to use it! Any help appreciated.
Ok I came up with a fix. First of all I just tried passing the auth token in to the userCredntials object in the same way as in the documentation for social auth, that is absent from the description in my above code snippet taken from their docs.
Then I Promisified the API calls from within useEffect inside an async function to make sure everything was happening in the right order, and it works:
export default function App() {
const createAppSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
!error
? resolve(session.token)
: reject(error, '=====1=====');
});
})
}
const loginUser = (credentials) => {
return new Promise((resolve, reject) => {
ConnectyCube.login(credentials, ((error, user) => {
!error
? resolve(user)
: reject(error, '=====2=====');
}));
})
}
useEffect(() => {
const ccFunc = async () => {
ConnectyCube.init(...config)
const appSessionToken = await createAppSession();
const userCredentials = { login: 'xxxxx', password: 'xxxxxxx', keys: { token: appSessionToken } };
const user = await loginUser(userCredentials);
console.log(user);
}
ccFunc()
}, []);
Hope it works....
please implement it by yourself...just take an understanding from code below.
code says: send the username and password to api...if all ok then authenticate else throw error ...if all ok..then store the returned token is asyncStorage...you can create the storage by any name you like...and use the token eveywhere in your app.
SignInUser = async () => {
this.setState({
username: this.state.username,
password:this.state.password,
})
if(this.state.username && this.state.password !== null){
try{
this.setState({
loading:true
})
const response = await fetch('YOUR API', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
});
var promiseResponse = await response.json()
console.log(promiseResponse.token);
try {
await AsyncStorage.setItem('STORE_YOUR_LOGIN_TOKEN_HERE', JSON.stringify(promiseResponse.token));
console.log('Token Stored In Async Storage');
let tokenFromAsync = await AsyncStorage.getItem('STORE_YOUR_LOGIN_TOKEN_HERE');
console.log('Getting Token From Async...')
tokenFromAsync = JSON.parse(tokenFromAsync)
if(tokenFromAsync !== null){
console.log(tokenFromAsync);
this.setState({
loading:false
})
this.props.navigation.navigate('Tabnav');
}
} catch (error) {
// saving error
console.log(`ERROR OCCURED ${error}`)
}
//this.props.navigation.navigate('Tabnav')
} catch(error){
console.log(`COULDN'T SIGN IN ${error}`)
}
} else {
this.setState({
msg:'Invalid Credentials',
label:'red'
});
}
}
This is how i got the login to work in their sample react native app 1. i created a credentials object like this in my custom login function in src>components>AuthScreen>AuthForm.js
var credentials = {id:'',login: this.state.login,password: this.state.password}
2.I used their _signIn(credentials) function and set the 'id' attribute of my credentials object after their UserService.signin(credentials) resolved with a user object. (the resolved user object contained the logged-in user's id i.e user.id). Then it worked. This is how the code looked for the signin after the little tweak.
loginUser() { //my custom signin function
var credentials = {id:'',login: this.state.login,password: this.state.password} //my credentials object
this._signIn(credentials)
}
_signIn(userCredentials) { //their signin function
this.props.userIsLogging(true);
UserService.signin(userCredentials)
.then((user) => {
userCredentials.id = user.id //setting id of my credentials object after promise resolved
ChatService.connect(userCredentials) //using my credentials object with id value set
.then((contacts) => {
console.warn(contacts)
this.props.userLogin(user);
this.props.userIsLogging(false);
Actions.videochat(); //login worked
})
.catch(e => {
this.props.userIsLogging(false);
alert(`Error.\n\n${JSON.stringify(e)}`);
})
})
.catch(e => {
this.props.userIsLogging(false);
alert(`Error.\n\n${JSON.stringify(e)}`);
})
}