firebase $authWithPassword permission is denied - angularjs

When using the angularFire $authWithPassword method in my app I am receiving a Permission_Denied from firebase. I'm very new to the Firebase Security rules and the mistake lies somewhere in how I've set them up (as it works fine when read and write are true globally). What I'm trying to do is disallow users from deleting the major collections of my architecture but allow them to CRUD child items.
So I have gifts, events, persons, and users. And my rules look like this:
{
"rules": {
".read": true,
"events": {
"$id": {
".write": true
}
},
"gifts": {
"$id": {
".write": true
}
},
"person": {
".indexOn": "created_by",
"$id": {
".write": true
}
},
"user": {
"$id": {
".write": true
}
}
}
}
As I said, there are no issues when using the default rules of:
{
"rules": {
".read": true,
".write": true,
"person": {
".indexOn": "created_by"
}
}
}
What am I doing wrong?
EDIT:
It is not the $authWithPassword doing the write operation, it is $createUser where I create the user and in the .then I am doing a 'CreateProfile' function which uses ref.set to update properties to /users/uid.
$scope.createAccount = function(email, pass, name, confirm) {
loaderSvc.toggleOn('Creating account...');
$scope.err = null;
if( !pass ) {
$scope.err = 'Please enter a password';
}
else if ( !name ) {
$scope.err = 'Please enter a display name';
}
else if( pass !== confirm ) {
$scope.err = 'Passwords do not match';
}
else {
Auth.$createUser({email: email, password: pass})
.then(function () {
// authenticate so we have permission to write to Firebase
return Auth.$authWithPassword({email: email, password: pass}, {rememberMe: true});
})
.then(createProfile)
.then(redirect, showError);
}
function createProfile(user) {
var ref = Ref.child('users/' + user.uid), def = $q.defer();
ref.set({email: email, name: name}, function(err) {
$timeout(function() {
if( err ) {
def.reject(err);
}
else {
sendWelcomeEmail(email, name);
toastr.success('Account created');
Analytics.trackEvent('profile', 'account created', user.uid);
def.resolve(ref);
}
});
});
return def.promise;
}
};

Related

update all values in the sub array of one object in mongodb using mongoose

user = {
'userid':'111',
'mail':[{
'time':22222,
'info':'this is a info1',
'read':false,
},{
'time':33333,
'info':'this is a info2',
'read':false,
}]
}
then, I want to change one user's read flag to true, how can i do for it ?
here is my answer:
User.update({
'userid':userid,
'mails':{
'$elemMatch':{
'read':false
}
}
},{
'$set':{
'mail.$.read':false,
'newmail':0,
}
}, { multi: true }, function (err, data) {
if(err > 0){
console.log('ReadMail err', err);
}
});
here is a simple example: my model is User !! my url is the path to the DB
var newUser = new User();
newUser.name = request.body.name;
newUser.email = request.body.email;
newUser.pass = request.body.pass;
newUser.position = request.body.pos;
newUser.phone = request.body.phone;
mongoose.connection.openUri(url);
var myquery = { _id : request.params._id };
console.log("updating...")
User.findByIdAndUpdate(myquery,{$set: newUser},{new: true},function(err, result) {
if(err)
{
throw err;
}
response.json({code: 0 , result});
});

undefined is not an object (evaluating 'storeDataFactory.createUser(user).then

the function runs and console.log shows the user object on the backend. I don't understand why it's telling me there is an issue here, and really need some guidance.
vm.register = function() {
//check that passwords match
if(vm.password != vm.passwordRepeat) {
vm.registerError = "Passwords must match.";
return;
} else {
var username = vm.username;
// console.log("Valid form. Checking for existing user",username);
storeDataFactory.userExists(username).then(function(response){
//if user exists, return error
if(response.data.length > 0) {
vm.registerError = "A user with email " + username + " already exists. Please login.";
return;
} else {
//if no user exists
if(response.data.length == 0) {
// console.log("No user exists. Continue with registration.");
}
//assign info to user object
var user = {
username: vm.username,
password: vm.password,
name: vm.name,
phone: vm.phone
};
**storeDataFactory.createUser(user).then(function(response){**
vm.user = response.data;
console.log("Created user", vm.user);
if(response.data.length > 0) {
console.log("Created user", vm.user);
vm.registerMessage = "Successful registration, please login";
vm.registerError = null;
vm.user = response.data;
}
}).catch(function(error){
console.log(error);
vm.registerError = "There was an error creating your account.";
vm.registerMessage = null;
});
}
});
}
};
The backend code:
module.exports.register = function(req, res) {
console.log('registering user', req.body);
//create the user object with hashed pass
User
.create({
username: req.body.username,
name: req.body.name,
phone: req.body.phone,
password: bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(10))
}, function(err, user) {
if (err) {
console.log("Error creating account");
res
.status(400)
.json(err);
} else {
console.log("Account created!", user);
res
.status(201)
.json(user);
}
});
};
Account created! and the user object are logged on the backend. It just won't display that damn Successful Registration! Please login. message.
storeDataFactory code:
/* global angular */ angular.module('rumbleApp').factory('storeDataFactory', storeDataFactory);
function storeDataFactory($http) {
return {
userExists: userExists,
createUser: createUser
};
function userExists(username) {
return $http.get('/api/users/userExists/' + username).then(complete).catch(failed);
}
function createUser(user) {
$http.post('/api/users/register', user).then(complete).catch(failed);
}
function complete(response) {
return response;
}
function failed(error) {
console.log(error.statusText);
return "There was an error with the API call.";
}
}
Are you sure you're returning from storeDataFactory.createUser()? Can you post the code for that method?
Sounds like the code is executing, but you're not returning anything from it (hence why it thinks it's undefined)

Node.js API - Works with Postman but not works in Angular.js

I am trying to create a mean application. as a sample, if I post the request through postman the data created at mlab.
in case if I post the same using $http way, it's not working getting the error as :
{
"message": "Family validation failed",
"name": "ValidationError",
"errors": {
"username": {
"message": "Path `username` is required.",
"name": "ValidatorError",
"properties": {
"type": "required",
"message": "Path `{PATH}` is required.",
"path": "username"
},
"kind": "required",
"path": "username"
},
"password": {
"message": "Path `password` is required.",
"name": "ValidatorError",
"properties": {
"type": "required",
"message": "Path `{PATH}` is required.",
"path": "password"
},
"kind": "required",
"path": "password"
}
}
}
and the node with mongoose :
.post(function( req, res ){
var family = new Family();
family.username = req.body.username,
family.password = req.body.password,
family.familyLeader = req.body.familyLeader,
family.husband = req.body.husband,
family.wife = req.body.wife,
family.kids = req.body.kids;
family.save(function( err, newFamily ) {
if( err ) {
if ( err.code == 11000) {
return res.json({ success: false, message: 'A user with that username already exists. '});
}
else {
return res.send( err );
}
}
res.json({ message: 'Family created!', newFamily: newFamily });
});
})
here is my angular code :
vm.createNewFamily = function() {
$http({
method : 'POST',
url : '/api/family',
data : vm.form,
headers : {'Content-Type': 'application/x-www-form-urlencoded'}
}).success( function ( data ) {
console.log('retured!', data );
})
}
my full api.js ( node )
var Family = require('../models/model_family');
module.exports = function( app, express ) {
var apiRoute = express.Router();
apiRoute.use(function( req, res, next ) {
console.log( 'some one using the app!' );
next();
})
apiRoute.get('/', function( req, res ) {
res.json({"namea" : "Arif"})
});
apiRoute.route('/family')
.get(function( req, res ){
res.send('family get processing');
})
.post(function( req, res ){
var family = new Family();
family.username = req.body.username,
family.password = req.body.password,
family.familyLeader = req.body.familyLeader,
family.husband = req.body.husband,
family.wife = req.body.wife,
family.kids = req.body.kids;
family.save(function( err, newFamily ) {
if( err ) {
if ( err.code == 11000) {
return res.json({ success: false, message: 'A user with that username already exists. '});
}
else {
return res.send( err );
}
}
res.json({ message: 'Family created!', newFamily: newFamily });
});
})
return apiRoute;
}
EDIT
If you only have problems with username then check you angular data bindings. Im thinking you have typo somewhere like this
<input ng-model="useranme">
Hope this helps.
Put your this code above all the routes in your main server file
----------
----------
var app=express();
app.use(bodyParser.urlencoded({
extended: true
}));
---------
---------

Simple Firebase Rules

This is my model on firebase :
Texts
-KAa-aU_RjZwM7FLQcWt
id: "5f9fe424-4323-4370-a280-9cb216dc6410"
text: "gregegreg"
-KAa-bZC2ouIRQ54YWWr
id: "5f9fe424-4323-4370-a280-9cb216dc6410"
text: "gregegreg"
And these are my rules :
"rules": {
"Texts": {
".read": "auth.id === data.child('id').val()",
".write": true
}
}
Unfortunatly, i'm still able to read All of my model TEXTS while i'm logged with another UID than 5f9fe424-4323-4370-a280-9cb216dc6410
Any idea appreciated thank you !
The authenticating part works well with angularJs and login-password :
var firebaseObj = new Firebase("https://blinding-heat-xxxx.firebaseio.com");
$scope.authObj = $firebaseAuth(firebaseObj);
$scope.login = function() {
$scope.authData = null;
$scope.error = null;
$scope.authObj.$authWithPassword({
email: $scope.email,
password: $scope.password
}).then(function(authData) {
console.log("Logged with id:", authData.uid);
$scope.loadData();
}).catch(function(error) {
console.error("Auth Failed :", error);
$scope.error = error;
});
};
Data is finally loaded on the web page like that :
$scope.loadData = function(){
var ref = new Firebase("https://blinding-heat-xxxx.firebaseio.com");
$scope.Texts = $firebaseArray(ref.child('Texts'));
}
And into the HTML , the angularJs is there :
<div ng-repeat="text in Texts">text.text</div>
What is really strange is that the following rule works very well, for example :
"rules": {
"Texts": {
".read": " auth != null ",
".write": true
}
}
Almost there! It's actually auth.uid not auth.id.
Also, you need to provide a wildcard, a $ variable, that applies the rule to any child below /Texts.
"rules": {
"Texts": {
"$text_id": {
".read": "auth.uid === data.child('id').val()",
".write": true
}
}
}
Without the wildcard, the rule is applied to the static path of /Texts.
Here's the output in the Simulator.

How to write to a parent node but not a child in firebase security rules

I'm using firebase via angularFire. Can anyone help me figure out how the best way to write a new node to /users but then prevent anyone with a different id from writing?
Here's my unfortunate security rules. Was thinking if the data doesn't exist AND there's new data present, that would cover this use case. it doesn't.
{
"rules": {
".read": true,
"users": {
".write": "!data.exists() && newData.exists()",
"$user": {
".write": "auth.uid == $user"
}
}
}
}
Here's the register method that's creating a user and then updating the db:
register: function(obj){
var authRef = new fb(fbRef);
var authObj = $firebaseAuth(authRef);
authObj.$createUser({
email: obj.email,
password: obj.pass
}).then(function(userData) {
console.log("User " + userData.uid + " created successfully!");
return authObj.$authWithPassword({
email: obj.email,
password: obj.pass
});
}).then(function(authData) {
var fbObjRef = authRef.child('users');
var fbObj = $firebaseObject(fbObjRef).$loaded().then(function(data){
obj.pass = null;
data[authData.uid] = obj;
data.$save();
});
console.log("Logged in as:", authData.uid);
}).catch(function(error) {
console.error("Error: ", error);
});
}
Somewhere in the threads was mentioned that there's a bug with angularFire but that thread was from 2013. I recall coming across this use case before but cannot find the post. Security rules are the scariest part. Thank you for helping!
I'm open to learning a better way but here's a solution. Basically, I'm able to check if the user's id exists, if not then write is allowed. It's probably not the safest method so feel free to point out my misunderstanding or proper validating :)
{
"rules": {
".read": true,
// ".write": true,
"users": {
".write": "(auth !== null && !root.child('users/'+auth.uid).exists())",
"$user": {
".write": "auth.uid == $user"
}
}
}
}

Resources