The goal for this code is to provide a semi-secure and privileged method of user registration via a secret code that is stored as a key in the Firebase database.
Subscribers to a mailing list were all sent a 'secret' registration code. Upon completion of the registration form, RegisterFactory.register
is called from the controller. One of my biggest concerns is that I have to make 5 async calls all of which return a promise and results in a long chain of promises.
The app has been in use for a month without any major issue. Before the next session starts, I would like to improve things a bit.
Please ignore the code where I un-authenticate the user as this issue needs to be fixed elsewhere first. Once fixed, I should be able to return the last Firebase promise to the controller.
The relevant Firebase schema is:
{
"secretKey": true,
"clientList": {
"paid": {...}
},
"users": {
"$userID": {
"profile": {...}
}
}
RegisterFactory:
app.factory('RegisterFactory', ['AuthRefService', 'APP', '$q', 'appFunctions',
function(AuthRefService, APP, $q, appFunctions){
/**
* Precondition: A data structure of { 'secretKey': 'secretValue' },
* Postcondition: Returns a promise with the data at '/code' resolving with
* 'secretValue' or null.
*/
var checkCode = function(code){
code = code.trim();
var ref = new Firebase(APP.BASEURL);
return ref.child(code).once('value');
};
/**
* Precondition: checkCode(code) resolves with true.
* Postcondition: A new user is created in Firebase and returns a
* promise resolving with userData (that only contains userData.uid and does not auth user) or error.
*/
var createUser = function(credentials) {
return AuthRefService.$createUser({
email: credentials.email,
password: credentials.password
});
};
/**
* Precondition: checkCode(code) resolves with true.
* Postcondition: User is authenticated is and a promise resovles with authData or error
*/
var authUser = function(credentials) {
return AuthRefService.$authWithPassword({
email: credentials.email,
password: credentials.password
});
};
/**
* Precondition: User is authenticated.
* Postcondition: Sets data at: /user/uid/profile and returns a
* promise resoloving w/o data if success, otherwise with error
*/
var setProfile = function(authData, profile){
var ref = new Firebase(APP.BASEURL + "users/" + authData.uid);
return ref.child("profile").set(profile);
};
/**
* Precondition: User is authenticated.
* Postcondition: Sets data at: /clientList/paid and returns a
* promise resoloving w/o data if success, otherwise with error
*/
var addToClientList = function(data, uid) {
var now = appFunctions.dateToNum(new Date()); // yyyyMMdd
var info = {
first: data.profile.first,
last: data.profile.last,
email: data.credentials.email,
dateJoined: now
};
var ref = new Firebase(APP.BASEURL + "clientList/paid");
return ref.child(uid).set(info);
};
return {
/**
* Precondition: user input, data, is complete
* Postcondition: A new user is created in database
*/
register: function(data) {
return $q(function(resolve, reject) {
checkCode(data.credentials.code)
.then(function(snapshot){
if(snapshot) {
createUser(data.credentials)
.then(function(userData) {
console.log("User create with id:", userData.uid);
authUser(data.credentials)
.then(function(authData) {
setProfile(authData, data.profile)
.then(function(resp) {
// store new user in clientList index
addToClientList(data, authData.uid)
.then(function(resp) {
// TODO: deal with this better
// logout user, b/c I couldn't figure out a solution to smoothly get the new
// user to '/app' b/c of how app inits user upon login.
AuthRefService.$unauth();
resolve(true);
}, function(err){
if (typeof err !== "string") {
err = "Error";
}
reject(err);
});
}, function(err) {
if (typeof err !== "string") {
err = "Error";
}
reject(err);
});
}, function(err) {
if (typeof err !== "string") {
err = "Error";
}
reject(err);
});
}, function(err) {
if (typeof err !== "string") {
err = "Error";
}
reject(err);
});
} else {
reject("Error");
}
}, function(err){
reject("Invalid Registration Code");
});
});
}
};
}]);