Add a callback to Firebase Login Function - javascript

I am fairly new to React and the use of Firebase. Right now I have a Firebase.js file where I have stored and exported all of my functions and consts. This is what it looks like:
export const auth = firebase.auth();
export function performLogin(email, password) {
auth.signInWithEmailAndPassword(email, password).then(function(data) {
//Works
}).catch(function(error) {
//Does not work
});
}
So, I have this login.js that I am calling performLogin from, and I was wondering what would be the best way of doing this? How can I create a callback, or at least read any kind of return message? This is how I call performLogin:
clickLogin(e){
e.preventDefault();
performLogin(this.state.email, this.state.password);
}
And this works, as the console output tells me if the login was successful or not. However, I want to be able to use this function in order to retrieve the login status, and then determine wether or not I should prompt an error message or push the user to the admin dashboard. How would I do this?
Is it possible to call performLogin like this?
performLogin(this.state.email,this.state.password,(callback)){
if (callback == true) { //Success } else { //Error }
}

Your performLogin function could take a third parameter, a callback function that lives where your clickLogin() method lives:
export function performLogin(email, password, callback) {
auth.signInWithEmailAndPassword(email, password).then(function(data) {
//Works
callback(data);
}).catch(function(error) {
//Does not work
callback({ error });
});
}
loginResult(result) {
if(result.error) {
//failed
} else {
//logged in
}
}
clickLogin(e){
e.preventDefault();
performLogin(this.state.email, this.state.password, this.loginResult);
}
Or you could return the promise that signInWithEmailAndPassword() returns and handle it in the component.
export function performLogin(email, password) {
return auth.signInWithEmailAndPassword(email, password);
}
clickLogin(e){
e.preventDefault();
performLogin(this.state.email, this.state.password)
.then(result => //logged in)
.catch(error => //failed)
};

Take a look at the function onAuthStateChanged from "firebase/auth" package.
Here is a link to the docs:
https://firebase.google.com/docs/auth/web/manage-users

Related

Why does my async function always return false to the wrapper function?

I have an async function that triggers AWS Amplify to send a password reset code to a user.
export async function forgotPassword(username) {
console.log('forgotPassword')
return Auth.forgotPassword(username)
.then(data => {
console.log('aws reset password request success')
console.log(data)
})
.catch(error => {
console.log('error with call to AWS forgotPassword fncn')
})
}
Here is a wrapper function that calls the function above...
forgotPassword(email).then(success => {
if (success) {
console.log('successful forgot password action')
Vue.prototype.$notification.success({
message: 'Forgot password action success',
description: 'You have successfully submitted a password refresh request!',
})
}
if (!success) {
console.log('failed forgot password action')
}
})
I know that the inner function runs successfully because my console log statement for debugging that method does indeed execute and print 'aws reset password request success'.
However, my wrapper function always detects a failure in the aws function because I see that the wrapper function enters the !success clause. Why is this? I am guessing there is a syntax error with my async await code in the inner aws function
Update, based on the answers I've read so far, I updated my inner function as follows.
export async function forgotPassword(username) {
console.log('forgotPassword')
try {
const data = await Auth.forgotPassword(username)
console.log('aws password reset request success')
console.log(data)
return true
} catch (error) {
console.log('error with call to AWS forgotPassword fncn')
notification.warning({
message: error.code,
description: error.message,
})
}
}
It appears to work properly.
This is because your promise, returned by the first function, resolves to undefined. As you chain a .then to the initial promise, you should make sure to return the value that promise should resolve with:
.then(data => {
console.log('aws reset password request success')
console.log(data)
return data; // or: return true;
})

Getting current user after sign in with Firebase

I want to send a verification email after user sign up. I wrote that code but if i want to get the current user with firebase on react native it always return null. How can i fix it?
Here is the sign up function:
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(navigation.navigate("WaitingRoom"))
.catch((err) => console.log(err));
And also email verification function in WaitingRoom:
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
user
.sendEmailVerification()
.then(() => {
console.log(":)");
})
.catch((err) => {
console.log(err);
});
} else {
console.log(":(");
}
});
I also tried firebase.auth().currentUser but it return null too.
From your comment above: "It happens but there is a delay and sometimes i have to reload page. I think i have to wait for response or something like that."
Yes, as explained in the doc, you need to wait that the Auth object has finished initializing.
This is why you should:
Either use the onAuthStateChanged() observer and put the desired code/ business logic in the if (user) {} block, where you are sure user is not null.
OR, manually check that firebase.auth().currentUser is not null before triggering the code/ business logic.
Concretely, based on what I understand from your question, you could/should call navigation.navigate("WaitingRoom") in the onAuthStateChanged() observer, as follows:
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
user
.sendEmailVerification()
.then(() => {
navigation.navigate("WaitingRoom");
})
.catch((err) => {
console.log(err);
});
} else {
console.log(":(");
}
});

Throwing error from subscription/observable - Angular2+

I am attempting to throw an error from my observed function, such that I can access the err part of my subscription, but I cannot seem to get through to that part.
I'm not sure how to do it via the map way, but here is a different way:
import { Observable, of, throwError } from 'rxjs';
ngOnInit() {
this.getObs().subscribe(data => {
console.log(data);
}, err => {
console.log(err);
});
}
getObs() {
const observable = of(6);
const isError = true;
if (isError) {
return throwError("Is an error!");
}
return observable;
}
Working example: https://stackblitz.com/edit/angular-mqp4qv?file=src/app/app.component.ts
Couple ways I would handle this. Heres one way using _throw
// auth.ts
import { _throw } from 'rxjs/observable/throw';
login( username, password ) {
const body = this.jsonifyData( username, password );
return this.http.post<any>( this.apiBaseUrl + '/auth', body )
.pipe(
map( res => {
if ( 'token' in res ) {
return res.token;
}
// Unsuccessful login returns error
return _throw( res ); // from import {_throw} from 'rxjs/observable/throw';
} )
);
}
The other would be to just catch the error in the login component
I think in your component's test spec you need to mock up your authService and the login procedure. This mock does not make http call, just throw an error via throwError(new Error('Either username or password were incorrect')) immediately if, say, password does not match some predefined value. Here is some example of how to test component-http service stuff, it is a bit outdated but still actual conceptually.

Create an asynchronous function with react native

I have a function that retrieve asynchronously a password from the keychain.
In MyStaticClass
static getPassword() {
Keychain
.getGenericPassword()
.then(function(credentials) {
console.log('Credentials successfully loaded for user ' + credentials.username);
return credentials.password
}).catch(function(error) {
console.log('Keychain couldn\'t be accessed! Maybe no value set?', error);
});
}
I tried to call the function and receive the promise with this code but the result is undefined
MyStaticClass.getPassword().then((data) => {
alert(data);
});
I also tried this code and password was also undefined
static async login(){
const password = await MyStaticClass.getPassword();
alert(password);
}
You are missing return statement in getPassword method
return Keychain ...

How to consume a Promise from a service (native script / firebase)?

I have a userService that calls firebase.login() which returns a result of type Promise<User>. It is called like this:
firebase.login( { config...
})
.then (function (result) { /* use result */},
function (error) { /* use error */ });
Elsewhere in the project, in a UI component, I need to react to the outcome of the login (i.e. success or error). I do not want to put any routing or similar logic into the user service, but rather have the other component control what happens next.
How is that done? How can another component react to it?
The component mentioned above is a class called LoginPage. It has a Login button which should start the Firebase login. The button is linked to a function (also called login()) in the LoginPage class.
XML:
<Button [text]="Sign in" class="submit-button" (tap)="login()"></Button>
login.component.ts
login() {
this._userService.login(this.user)
.then(function (data) {
/* go to next screen */
});
}
The above is obviously wrong, as the then clause will always be executed, regardless of the success or failure of the login.
Here is the login function in the userService:
#Injectable()
export class UserService {
login(user: User) {
firebase.login({
type: firebase.LoginType.PASSWORD,
email: user.email,
password: user.password
})
.then( function (result) {
console.log("Successful Login! " + JSON.stringify(result));
return result;
}, function (error) {
console.log(error);
return error;
});
}
UserService login method should return a promise to make it available for chaining:
login(user: User) {
return firebase.login(...)...
}
In the original above login error was already caught, this leaves the promise with resolution that can be either a result or an error.
The preferable way to handle errors here is to not catch the error in the service itself. We always want to know if login request was successful or not.
login(user: User) {
return firebase.login(...)
// second callback is optional if we don't need to log errors
.then( function (result) { ... }, function (error) {
console.log(error);
return firebase.Promise.reject(error);
});
}
This way an error can (and also should, Angular 2 will throw an error if there are uncaught promises) be caught and handled:
login() {
return this._userService.login(this.user)
.then(function (data) {
/* go to next screen */
})
.catch(function (error) { ... });
}
It is always preferable to return promises, at least for testing purposes.

Categories