Firebase - issue with Auth during registration - reactjs

I am new to firebase and have managed to successfully setup an authentication with email/ password based off what I have managed to gather from the documentation/ examples online. I've encountered some strange behaviour on register though. First of all here is my firebase auth code that sits in a React component function:
class SignupComponentInner extends Component {
toggleSignupLayoverAction(event){
this.props.toggleSignupLayover("false")
}
signUp(e) {
e.preventDefault();
var firstName = $('.signup-first-name').val();
var lastName = $('.signup-last-name').val();
var userName = $('.signup-user-name').val();
var password = $('.signup-user-password').val();
var email = $('.signup-email').val();
var auth = firebase.auth();
const promise = auth.createUserWithEmailAndPassword(email,password).then(function(user) {
var user = firebase.auth().currentUser;
user.updateProfile({
displayName: userName,
}).then(function() {
// Update successful.
// new db code here
firebase.database().ref('users/' + user.uid).set({
firstName: firstName,
lastName: lastName,
userName: userName
})
// end new db code here
}, function(error) {
// An error happened.
});
}, function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// [START_EXCLUDE]
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
console.error(error);
}
// [END_EXCLUDE]
});
promise.catch(e => console.log(e.message));
firebase.auth().onAuthStateChanged(firebaseUser => {
if(firebaseUser) {
console.log("logged in");
var sendUserId = firebaseUser.uid;
this.props.logUserIn(sendUserId)
} else {
console.log("not logged in")
}
});
}
render() {
return (
<div onClick={this.toggleSignupLayoverAction.bind(this)} className="signup-cont-par">
<div className="signup-component-inner">
<div className="signup-component-content" onClick={cancelBubble.bind(this)}>
<h2>Sign up today!</h2>
<form className="signup-form-elem">
<input placeholder="First Name" className="signup-first-name" type="text"></input>
<input placeholder="Last Name" className="signup-last-name" type="text"></input>
<input placeholder="Username" className="signup-user-name" type="text"></input>
<input placeholder="Password" className="signup-user-password" type="password"></input>
<input placeholder="Email Address" className="signup-email" type="text"></input>
<button onClick={this.signUp.bind(this)}>Sign Up</button>
</form>
</div>
</div>
</div>
)
}
}
So the registration part works exactly as it should but its the login part here that is causing issues:
firebase.auth().onAuthStateChanged(firebaseUser => {
if(firebaseUser) {
console.log("logged in");
var sendUserId = firebaseUser.uid;
this.props.logUserIn(sendUserId)
} else {
console.log("not logged in")
}
});
It's basically executing twice as im getting console.log "logged in" twice on register. Not sure why that is?
The other issue is the function that it is calling from the props this.props.logUserIn(sendUserId) This function basically hides a "create account" CTA and replaces it with a hello {{username}}. The username updates on register but very strangely it will update to whatever username I registered on the previous register! Here is the logUserIn function if it helps:
logUserIn(userId) {
var db = firebase.database();
var ref = db.ref('/users/' + userId);
console.log(userId);
ref.once('value').then((snapshot) => {
var userObject = snapshot.val();
var loggedUserName = userObject.userName;
this.setState({
LoggedInUsername:loggedUserName
})
});
this.setState({
loggedInState:true,
LoggedInId:userId,
signingUp:"inactive"
})
},
Here loggedUserNameis a state that is passed down to the main component to show the username that has just been registered (the one that is incorrectly showing the previous registered person when it should be showing the latest). Not sure where I have gone wrong.

Firebase maintains persistent connections with their servers so if you registered once, Firebase detects your browser and machine to be logged in for that newly created user. So, if you close the browser and then come back to your project on the SAME browser, Firebase still thinks you are logged in and hence, the onAuthStateChanged event handler is called.
Try it out for yourself and see what happens when you open up 2 different browsers.
To complete the whole authentication system, you need to have a logout function that will tell Firebase that this user has logged out (https://firebase.google.com/docs/auth/web/password-auth#next_steps)

Related

Guidance on best practice: Posting to server to fetch SQL query data and redirect user back to UI with data

Hello I am needing some assistance with a current project I am working on. The goal is to be able to interact with SQL from the UI and display the returned data in a table.
I am using node V9.8.0, express 4.16.3 and mssql 4.1.0.
I am able to connect and fetch data and display it on the UI but I do not think my set up is best practice.
My main road block was figuring out how to send information to the server to than query SQL and redirect the user back to the UI with the table displayed with the expected data.
Front end set up, form and ajax. I know that the action in the form should match the URL for ajax but to get this to work I have two post routes on the sever side. The form submits to the first post route and writes the req.body to a file. The ajax post URL reads the file and than connects to SQL and knows what data to fetch based on the user's input and predefined query strings that can take in parameters.
<form id="myForm" action="/qa-hub/tools/wss_qa_tool/sql/data" method="POST">
<div class="row">
<div class="col-md">
<div class="form-group">
<select type="text" id="slct1" class="form-control" placeholder=" SQL query search..."
name="query" list="query">
<option value="">Query select...</option>
<option value="vacant_units">vacant_units</option>
<option value="cancelable_contracts">cancelable_contracts</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md">
<div class="form-group">
<input type="text" id="slct2" style="display: none;" class="form-control" placeholder="Entity #" name="location" />
</div>
</div>
</div>
<div class="row">
<div class="col-md">
<div class="form-group">
<input type="text" id="slct3" style="display: none;" class="form-control" placeholder="Unit #" name="unit" />
</div>
</div>
</div>
<button value="submit" id="submitBtn" class="btn btn-secondary" style="width: 25%; font-size: 14pt">Submit</button>
</form>
<script>
$.ajax({
type: "POST",
url: "/qa-hub/tools/wss_qa_tool/api/data",
dataType: "json"
}).then(addData)
function addData(data) {
// console.log(Object.keys(data.recordset[0]));
Object.keys(data.recordset[0]).forEach(function(column) {
$("#theadRow").append("<th>" + column + "</th>");
});
let master = "";
for(var i = 0; i < data.recordset.length; i++) {
for( var key in data.recordset[i]) {
var current = "<td>" + data.recordset[i][key] + "</td>";
master = master + current;
}
$("#tBody").append("<tr>" + master + "</tr>");
master = "";
}
$('#myTable').DataTable()
}
</script>
This is my set up on the server side. So this will take in the req.body from the form and write it to a file, than redirect the user back.
router.post("/sql/data", (req, res) => {
let query = req.body.query;
let sent_params = queries[query].params;
let sentArr = sent_params.map( x => req.body.hasOwnProperty(x));
// console.log(sentArr);
if(sentArr.includes(false) ) {
console.log("Missing parameter");
res.redirect("/qa-hub/tools/wss_qa_tool");
} else {
let tempObj = {};
for(var i = 0; i < sent_params.length; i++) {
tempObj[sent_params[i]] = req.body[sent_params[i]];
}
tempObj["query"] = query;
let sentObj = tempObj;
let userId = req.user.id;
fs.writeFile(`./temp/${userId}.json`, JSON.stringify(sentObj), (error) => {
if(error) console.log(error);
res.redirect("back");
});
}
});
This is where the magic happens. This post route will than read the file and pass in the information to saved SQL queries that can take in parameters and than sends the information back to the UI.
router.post("/api/data", (req, res, next) => {
let userId = req.user.id;
fs.readFile(`./temp/${userId}.json`, "utf-8", (err, info) => {
if(info === undefined || info === "" || Object.keys(JSON.parse(info)).length === 0 ) {
console.log("No data was found.");
} else {
let data = JSON.parse(info);
let query = data.query;
let location = data.location;
let unit = data.unit;
let selected_query = queries[query].arguments([location, unit]);
var config = {
user: 'xxxx',
password: 'xxxx',
server: 'xxxx',
database: 'xxxx'
};
sql.connect(config, function (err) {
if (err) console.log("Error at the config.");
console.log("Connected!");
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query( selected_query, function (err, queryData) {
if (err) console.log("There was an error")
// console.log(queryData);
res.send(JSON.stringify(queryData));
sql.close();
console.log("SQL connection closed.")
fs.unlink(`./temp/${userId}.json`, (err) => {
if(err) console.log(err);
console.log("File was deleted successfully!");
});
});
});
}
});
});
To me this seems like a hackish set up but I needed to produce results sooner than later. What I could not figure out or find was how can I produce the same set up with maybe one post route? I could not wrap my head around how to send information to the server, SQL query to fetch data and than redirect the data and user back to the same page. Wondering what the best practice would be for this type of set up? Or possibly any insightful reading to help guide me? Any assistance is appreciated.

Bad request status 400. Required String parameter 'login' is not present

I am trying to build login with react.js and connect it to my springboot.
Here is my code, react.js:
import React from 'react';
export default class Login extends React.Component {
constructor() {
super();
this.state = {
login:"",
password:""
}
}
// This will be called when the user clicks on the login button
login(e) {
e.preventDefault();
console.log(this.state.password)
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
}
var xhr = createCORSRequest('POST', "http://localhost:8080/test/login");
if (!xhr) {
throw new Error('CORS not supported');
}
fetch().then(r => r.json())
.then(data => console.log(data))
.catch(e => console.log(e))
}
render() {
return (
<form role="form">
<div>
<input type="text" name="login" placeholder="Username" />
<input type="password" name="password" placeholder="Password" />
</div>
<button type="submit"onClick={this.login.bind(this)}>Login</button>
</form>
);
}
}
And this is my springboot code that is located as TestController:
#RestController
public class TestController {
#RequestMapping(value = "/test/login", method = RequestMethod.GET )
public Boolean testLogin(#RequestParam String login, #RequestParam String password) {
if ( login.equals ("ajt"))
return true;
else {
return false;
}
}
Each of them are present in two different ports, react on :9000 and springboot on :8080.
Also, on my react page I get the error:
TypeError: Failed to execute 'fetch' on 'Window': 1 argument required, but only 0 present.(…)
Any ideas?
for info: I have only got 6 months coding behind me -_- please be kind!
There's a handful of small mistakes here. I'll try to point you in the right direction for a few of them.
First of all, I just wouldn't use fetch. It's listed as an expiremntal technology by MDN, and it's browser support is weak. For someone just starting out with web development, you're much better off using a more established and "safe" technology. Either simple XMLHTTP ajax or using the ajax method from jquery. You already seem to be going down the path of XMLHTTP, so I would suggest just replacing your fetch commands with the example I linked above.
Second, you're using two different HTTP methods. Your ajax object is going to send a POST command, but your server is listening for a GET command. This is set up on the following lines;
var xhr = createCORSRequest('POST', "http://localhost:8080/test/login");
#RequestMapping(value = "/test/login", method = RequestMethod.GET )
If you want those two pieces of code to talk to one another, they need to be set to the same method. In this case, you want a POST for both, but it's worth learning the distinction between the two for the future.
Lastly, there's the issue of getting information from your inputs and into your ajax XMLHTTP object. To do this, you're going to want to set up onChange hooks on the inputs, and attach them to a handleChange function within the react component. Use this function to save the values to the component's state, and then later take the values out of state to apply to the xmlhttp.
A simple example of what I am describing;
render() {
return (
<div>
<input type="text" name="login" onChange={this.handleChange}/>
<button onClick={this.login.bind(this)}>Login</button>
</div>
);
}
handleChange(value) {
this.setState({login: value});
}
login () {
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
xmlhttp.open("POST", "/test/login");
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send(JSON.stringify({login: this.state.login}));
}

Getting Username from Firebase after email/password login [React Native]

I have created a simple login app using react native that let's users signup, login, and logout. my signup function takes a username that is then used in the createUser callback to generate a db entry with the uid as the key, and the username entered as a value. The answer in this post is the structure I followed - How do you include a username when storing email and password using Firebase (BaaS) in an Android app?
After the user is logged in, I'd like to get the username and display it but I'm having trouble figuring this out.
This is the code I currently have to attempt and do it:
var ref = new Firebase("https://myreactapp.firebaseio.com");
module.exports = React.createClass({
getInitialState: function() {
var authData = ref.getAuth();
var user = ref.child("users/" + authData.uid + "/username");
return {
username: user
};
},
This is how the code looks when I signup and the structure of my db.
var self = this;
let ref = new Firebase("https://myreactapp.firebaseio.com");
ref.createUser({
email : this.state.email,
password : this.state.password
}, function(error, authData) {
if (error) {
return this.setState({errorMessage: 'Error creating user'});
} else {
ref.child("users").child(authData.uid).set({
username: self.state.username
});
console.log("Successfully created user account with uid:", authData.uid);
ToastAndroid.show('Account Created', ToastAndroid.SHORT)
return self.props.navigator.pop();
}
});
----------------Not actual code-------------------------------------------------
DB
+users
--<uid>
-username -> value
--<uid>
-username -> value
I try to login and get an error of maximum call stack exceeded, but I have a feeling I'm going at this the wrong way. I've looked online, but everything I found was for retrieving data that is either being added, changed or deleted. All I want is to get this username once.

Firebase Auth not updating once password reset

I can't quite seem to figure out how to reset the Firebase Auth object once it has loaded for the first time.
I am looking for the bool true value in auth.password.isTemporaryPassword which forces the user to reset their password. Once the user has carried this out this procedure and reset, the auth.password.isTemporaryPassword remains true.
The only way around this I have found is to log the user out and log them in again which refreshes the auth object.
login:
var ref = new Firebase(environment);
$firebaseAuth(ref)
.$authWithPassword({
email: email,
password: password
},sessionObj)
.then(function(authData) {
if (password.isTemporaryPassword === true) {
$state.go('resetpassword');
}
})
.catch(function(error) {
deferred.reject(error);
});
reset password:
$scope.reset.oldPassword = "oldPass";
$scope.reset.newPassword = "newPass";
$scope.reset.email = "usermail";
ref.changePassword($scope.reset, function(err) {
if(err) {
...
}
else {
$state.go('home')
}
})
password.isTemporaryPassword remains true until I log the user in again which seems hacky.
You should be able to use the onAuth function to listen for changes to authentication state:
ref.onAuth(function(authData) {
//user authenticated & needs to change her password
if(authData && authData.password.isTemporaryPassword) {
$state.go('resetpassword');
}
//else user is logged in with valid password
else if(authData) {
}
//else user is logged out
else {
}
});

Asynchronously checking if email is taken already using AngularJS

I have the following email field for taking user input for email. Once the email has been entered and focus is taken away from the field ng-blur triggers the function that checks whether the email entered by the user has already been taken:
<input type="email" class="form-control" placeholder="" ng-model="email" name="email" required ng-blur="isFound(email)">
To show the error I've got the following span:
<span class="help-block error" ng-show="blurred && isTaken">Email is taken already</span>
And here is the function isFound(email):
$scope.isFound = function(email) {
service.isEmailTaken(email).then(function(data) {
console.log(data); //this shows either null or the returned data
$scope.blurred = true;
if(data == null) {
$scope.isTaken = false;
} else {
$scope.isTaken = true;
}
});
}
Now when I try an email that has been taken, it shows the message correctly. But after that even when I enter the email that has not been taken it keeps showing the same message that the Email is taken already.
Could somebody help me understand the reason why is it happening and how to resolve it?
EDIT - Adding AngularJS and NodeJS/ExpressJS/Mongoose code below:
AngularJS
factory.isEmailTaken = function(email) {
return $http({url: "/api/queries/email/" + email, method: "GET"}).then(function(resp) {
return resp.data;
});
};
ExpressJS/NodeJS/Mongoose
app.get('/api/queries/email/:email', function (req, res) {
Query.findOne({email: req.params.email}, function (err, query) {
if (err)
res.send(err);
res.json(query);
});
});
When the email was not taken already value of data was null, and the typeof data was String.
The reason why it was not working earlier was because if(data == null) was always turning out to be false.
if(data == 'null') got things working properly.

Resources