What is the proper way to implement authentication in Firebase? - javascript

ok so I've built a few apps in Firebase and really enjoy using the simple API but always struggle with auth. see this code
I have a simple email and password "form" (not a form element, just two inputs)
and then a button (div) I click to call this function below
const logUserIn = () => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((res) => {
console.log('here') // this WORKS but the history redirect below does not work :/
localStorage.setItem('authUser', JSON.stringify(res))
history.push('/account')
})
.catch((error) => {
console.log('ERROR:', error)
})
}
however when I put the lines below
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('user logged in')
} else {
console.log('user not logged in')
}
})
it seemed to pick it up correctly
but my question is, what is the point of signInWithEmailAndPassword? and also, how do I use both functions together? do I need to call onAuthStateChanged in the .then part of my signInWithEmailAndPassword function?
it's really confusing how to have a consistent state. my app seems to work after I refresh the page but I want it to work without refreshes (obviously)
any ideas on the best approach?
EDIT
also when I click sign out
firebaseApp.auth().onAuthStateChanged((user) => {
if (user) {
history.push('/account')
} else {
console.log('NO USER')
}
})
NO USER logs but then I click on an authenticated route it takes me there until I refresh

The point of signInWithEmailAndPassword is to allow the implementation of classic username/password authentication for people who don't have a Google, Facebook, Twitter, etc. account. If you decide to add it, Google will handle the user accounts and everything, but you'll have to build the login form, complete with the I forgot my password and Register as new user links. Other than that it's simply another provider.
I have a very simple little project on my Github repo which implements user/password authentication along with several others, I suggest you to look into it. I made it as a test assignment for a job interview. It's Angular.
https://github.com/tomcatmwi/morbidomega

Related

How can I use firebase without the emulator?

I am learning firebase and I checked this tutorial regards the authentication. (https://www.youtube.com/watch?v=rbuSx1yEgV8&t=502s). In this video, the emulator seems to be essential, however I want to communicate with the server. How do I do it? If I do not initialize the auth emulator ( by removing the connectEmulator() function) I just get the error 'auth/network-request-failed'.
const firebaseConfig = {
//...
};
const user = {
email: 'user#test.me',
password: 'test1234'
}
function func() {
createUserWithEmailAndPassword(auth, user.email, user.password)
.then((userCredential) => {
const user = userCredential.user;
console.log(user)
})
.catch((error) => {
console.log(error)
// ..
});
}
As you can see from the minute 7:37 of that video, I am getting his issue! So I assume I am following the wrong approach. Can someone help me? I would be really grateful.
You should be able to authenticate with the server.
The emulator is optional. Personally I rarely use it, and essentially always use the real online Firebase server. However there are many steps before you are able to authenticate with the server.
Step 1. Check you have copied the configuration correctly
Go to this link, but replace PROJECT_ID with your actual project Id:
https://console.firebase.google.com/u/0/project/PROJECT_ID/settings/general/
Check that you have correctly copied the value of this into your app code from that page. If you have not "added an app", you may need to click "Add app", to get this config to display.
const firebaseConfig = {
... blah blah ...
};
Step 2. Check that you have enabled a "Sign-in provider"
Go to this link (again PROJECT_ID should be replaced by your project Id):
https://console.firebase.google.com/u/0/project/PROJECT_ID/authentication/providers
At least one of the providers needs to be switched on, like so:
Step 3. Your code looks good.
I assume you have set up auth correctly - we can't see that in the snippet above.
Please paste into your question the exact error message you are seeing on the console, as text.
You might want to intensify the debugging as follows:
function func() {
console.log(`user: ${JSON.stringify(user,null,2)}`)
createUserWithEmailAndPassword(auth, user.email, user.password)
.then((userCredential) => {
console.log(`userCredential.user: ${JSON.stringify(userCredential.user,null,2)}`)
})
.catch((error) => {
console.error(error)
});
}
A small thing, but I suggest avoiding using the same variable name, user, for two different things. Javascript will keep them separate, but we as programmers sometimes will get muddled when looking back at the code.
Step 4. Make sure you have authorised the domain you are using.
Go to:
https://console.firebase.google.com/u/0/project/PROJECT_ID/authentication/settings
Make sure you have authorised the domain from which you are "calling" the Firebase server.
If your app is running on "127.0.0.1" instead of "localhost", you might need to add that IP address too. Or if you have deployed, the deployed domain.

How to handle forgot password/ reset password through link

How to relay(fetch and auto submit) the code from email into our app upon launching it? Im using react native aws-amplify default forgotPasswordSubmit function.
Auth.forgotPasswordSubmit(username, code, new_password)
docs state this:
import { Auth } from 'aws-amplify';
// Send confirmation code to user's email (this sends the email, and consolelogs what is sent)
Auth.forgotPassword(username)
.then(data => console.log(data))
.catch(err => console.log(err));
// Collect confirmation code and new password, then (this lets you reset the password)
Auth.forgotPasswordSubmit(username, code, new_password)
.then(data => console.log(data))
.catch(err => console.log(err));
To have it so that it automatically goes from the email to the link would require you to do something with the "email template"
specifically, you can make it so that the template send you to a website page with a :slug for the URL that is passed into the "conformation code area" and useEffect could reload the page after this, to submit the information.
though I have not used anything AWS before.
You can refer to this doc for more information:
https://docs.amplify.aws/lib/auth/manageusers/q/platform/js#forgot-password
I guess, you already started the password recovery process with
Auth.forgotPassword(username)
so, you already know the username, and the user probably has received an email with a verification code.
You can't just "fetch" data from an email into your app. The only thing you can do is, creating a link in the email, which, when clicked opens your app. This link will for instance look like https://yourapp.com/recovery?code=12345656 This is called "Deep Linking"
This will allow you to extract the verification code from the clicked link. Now, you will have to ask the user for a new password. Once you have all necessary information (ie the preentered username, the verification code and the new password) available, you can finally call
Auth.forgotPasswordSubmit(username, code, new_password)
I won't provide any code here, as I don't see much sense in copying the docs or some tutorial. Have a look at the docs or one of the many tutorials out there. If you then have specific problems with your code, feel free to update your question with the details.

firebase auth/operation-not-allowed

This is an angular web app.
Added the screenshot of the permission page
I'm trying to authenticate mobile using firebase.
In my firebase console > Authentication > signIn Method, I've enabled the phone and saved it.
But when I try to login It throws me an error saying that
auth/operation-not-allowed
sendLoginCode() {
const appVerifier = this.windowRef.recaptchaVerifier;
const num = this.firstFormGroup.value.mobileNo
console.log('num',num);
firebase.auth().signInWithPhoneNumber(num, appVerifier)
.then(result => {
this.windowRef.confirmationResult = result;
})
.catch(error => console.log(error));
}
verifyLoginCode() {
this.windowRef.confirmationResult
.confirm(this.verificationCode)
.then(result => {
this.user = result.user;
console.log('Login Successfull')
})
.catch(error => console.log(error, "Incorrect code entered?"));
}
It looks like you haven't enabled the Google sign in method in your firebase console. To solve the issue do the following:
Enter to the firebase console (https://console.firebase.google.com/).
Select your project.
On the right side of the screen you'll see a panel, click where it says "Authentication".
Once you've entered to the Authentication menu, go to Sign-in method.
After that look for the google access provider in the list that appears below the header and click on it.
Then click on the enable button.
It is probable that you'll have to configure a secure project ID (you'll see a dropdown below the enable button). What you have to do is, enter the android and/or ios client ID from your project, and hit save. This will tell firebase that it is secure to handle sign in operations with that client.
To be able to use the phone sign in method, you need to have a paid plan active.
Original author of answer: https://stackoverflow.com/a/65598080/6310260

Check if Firebase Facebook user exists without creating a user starting from anonymous user

In Firebase I need to check if a Facebook user exists without creating the user. Initially the user is anonymous, and they try to login with Facebook. I want this to fail if the Facebook account is not already linked to a user in my system. It won't be linked to the current user because they are anonymous,
If I use Auth.signInAndRetrieveDataWithCredential I expected a "auth/user-not-found" error, but instead the user is simply created. Is this a bug or expected?
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential).then( (userCredential) => {
let user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
If I use User.reauthenticateAndRetrieveDataWithCredential instead I get the error "auth/user-mismatch" which makes sense because user is currently anonymous. However, I was expecting "auth/user-not-found" may be thrown instead if the credential doesn't exist, but that doesn't happen.
I don't see a way to take my anonymous user, have them login with Facebook and then see if another user is already linked to that Facebook credential without creating the user if it doesn't exist.
If you're wondering why? My scenario is:
The system allows anonymous users
A user logs in, then converts to a logged in user by registering with Facebook.
App uninstall
App reinstall
User starts up the app and is initially anonymous.
They try and login with Facebook again. At this point I want to stop them from creating a user if they don't have one already. If they have a user ID already, the code works fine and changes their anonymous account ID to the original user ID which is good.
I found a solution! It wasn't too hard to implement, but it does seem hacky.
So we know that when using signInAndRetrieveDataWithCredential(cred) for facebook login, the account is created even if it does not exist yet. To solve this, we need to make sure that we handle the following three things:
Detect if the account is new
Delete the current account that was created by firebase
Throw an error to get out of the current flow and return to wherever you were before.
I just implemented and tested this solution, and it seems to work great:
// ... do your stuff to do fb login, get credential, etc:
const userInfo = await firebase.auth().signInAndRetrieveDataWithCredential(credential)
// userInfo includes a property to check if the account is new:
const isNewUser = _.get(userInfo, 'additionalUserInfo.isNewUser', true)
// FIRST, delete the account we just made.
// SECOND, throw an error (or otherwise escape the current context.
if (isNewUser) {
firebase.auth().currentUser.delete()
throw new Error('Couldn\'t find an existing account.')
}
// If the user already exists, just handle normal login
return userInfo.user
The reason I did this was to ensure that users had to go through the "create account flow" in my app. Your case would be really easy to implement as well, something like the following:
let credential = firebase.auth.FacebookAuthProvider.credential(event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential)
.then(userCredential => {
const isNewUser = userCredential.additionalUserInfo.isNewUser
if (isNewUser) {
firebase.auth().currentUser.delete()
// The following error will be handled in your catch statement
throw new Error("Couldn't find an existing account.")
}
// Otherwise, handle login normally:
const user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
You can use linkAndRetrieveDataWithCredential:
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken);
anonymousUser.linkAndRetrieveDataWithCredential(credential).then( (userCredential) => {
// Firebase Auth only allows linking a credential if it is not
// already linked to another account.
// Now the anonymous account is upgraded to a permanent Facebook account.
}).catch( (err) => {
// Check for code: auth/credential-already-in-use
// When this error is returned, it means the credential is already
// used by another account.
})
You can use the method fetchSignInMethodsForEmail to check if an specific email is already associated to an specific provider or not. Doing this you will be able to check if one if the SighInMethods of the email associated to your user contains Facebook.com or not.
I show you below an example about how I manage this cases on my application. I'm using an RxJavaWrapper on my code, but you will understand the point of how to manage it:
RxFirebaseAuth.fetchSignInMethodsForEmail(authInstance, email)
.flatMap { providerResult ->
if (!providerResult.signInMethods!!.contains(credential.provider)) {
return#flatMap Maybe.error<AuthResult>(ProviderNotLinkedException(credential.provider))
} else {
return#flatMap RxFirebaseAuth.signInWithCredential(authInstance, credential)
}
}
.subscribe({ authResult ->
//Manage success
}, { error ->
//Manage error
})
First I check the providers associated to the email of the user(You can retrieve it from the provider)
If the list of SignInMethods contains my credential provider, I throw an error, if not, I call my signInWithCredential method to create the user.
Continue your workflow.
What I did to solve this problem without relying on the call to linkAndRetrieveDataWithCredential to fail and using the catch block to sign in the already existing user is to save the userID field that getCurrentAccessToken returns.
const { userID } = data;
this.props.setFacebookId(userID); // saves the userID on the server
I can later check if this userID already exists next time the user signs up with facebook.

How to handle user information using firebase simple login for facebook

I am building a webpage using AngularJS and Firebase. I want to use facebook login to connect information on the webpage with the user. Firebase has a version of simple login which I guess is supposed to simplify the login process.
My problem is that I want to access information about the logged in user in a lot of places on my webpage but I can't find a good way to do it.
This is how I started out:
var userInfo = null;
var ref = new Firebase('https://<yourfirebase>.firebaseIO.com/');
var auth = new FirebaseSimpleLogin(ref, function(error, user) {
if(error)
alert("You are not logged in");
else if(user)
{
//store userinfo in variable
userInfo = user;
}
else
//user logged out
});
//do something with userInfo
alert(userInfo.name);
My first thought was to run this at the top of my page and then use the info about the user. The problem is that the code using userInfo (as in e.g. the alert) will always run before the userInfo variable has been filled and userInfo will return undefined/null.
I then proceeded to always create a new firebasesimplelogin object when i want to retrieve user data. Which of course isn't very good. Especially since every created FirebaseSimpleLogin object will be called again whenever another is called or a user logs out, for example.
So my question is, how do I use FirebaseSimpleLogin to handle and use my user information in the best way?
I would have liked some function to getUserInfo() or check isLoggedIn() for example. How do you do this properly?
You can take a look at this example for thinkster. It's based on using simple login with userid/password. http://www.thinkster.io/angularjs/4DYrJRxTyT/7-creating-your-own-user-data-using-firebase.
You can create a function like getLoggedinUser that runs in $rootScope that will allow you to find the user throughout the application.
UPDATE:
Around the middle of October 2014, firebase made some big changes. This method might still work, but it's better to take advantage of the newer version of firebase, specifically getauth and onauth. These methods will allow you to do the same thing without running on the rootScope. https://www.firebase.com/docs/web/guide/user-auth.html#section-login
Please make a constant to use it everywhere in your App like that
.constant('facebookUrl', 'https://rjrestaurantapp.firebaseio.com');
Then in the controller inject this constant "facebookUrl & "$state" as shown below...
.controller('loginCtrl', function($scope,facebookUrl,$state){
and then you only need to give name of the state where you want to redirect after facebook authentication..
var ref = new Firebase(facebookUrl);
$scope.fbLogin = function () {
ref.authWithOAuthPopup("facebook", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
$state.go('restaurant');
}
})
}})
You can see the information in authData object after successfull authentication using facebook ....
please read this doc carefully https://www.firebase.com/docs/web/guide/login/facebook.html
The above is the example of simple login using firebase and for retrieving data for each logged in user, you have to store user information at the time of signin as you know that firebase makes every child with a unique ID .. then you only need to use the offline features of firebase that will find which user is offline and according to that remove the node with the same ID which one is offline, you can find examples in the MANAGING PRESENCE section of the below link ..
https://www.firebase.com/docs/web/guide/offline-capabilities.html

Categories