How to structure my application to use Firebase, Braintree, Ionic / AngularJS and a minimal nodejs server

Refer to this question: The Braintree Dropin user interface does not work with the Ionic Framework if the force update

My current Ionic / Angular / Firebase + a very simple Node server application has a security issue when using Braintree to charge a user's credit card. The problem, according to @RaymondBerg, is that the client can publish any client identifier and create tokens with symbols and charge this client. Since all authorization of my user occurred in Firebase / Angular - client side. Therefore, when a user makes $ HTTP.post from my AngularJS / Ionic to my Node server, I don’t want to allow them again (since I don’t even know how to do this, so I use Firebase).

So, what is the strategy here to set up Firebase and my Node server to work with a payment system like braintree?

One thing I can think of is to first create a Node in my firebase before the HTTP request, and then pass the client id $ id for the request on the client side (Ionic app):

$scope.getToken = function () {
       var ref = new Firebase('[FirebaseURL]/braintreePaymentToken');
       var tokenObj = $firebaseObject(ref.child(posterId));
       tokenObj.tokenGenerated = true; 
       tokenObj.$save().then(function(){
        $http({
          method: 'POST',
          url: 'http://localhost:3000/api/v1/token',
          data: {
            //user $id from Firebase
            userId: snapshot.key(),
          }
        })
       }

In Firebase, I set a security rule as:

  "braintreePayment": {
      ".read": false,
      ".write": false,
    },
   "braintreePaymentToken": {
      "$uid": {
       ".read": "auth != null",
       ".write": "auth != null && auth.uid == $uid",
      }
    },

Thus, temp Node braintreePaymentToken can ONLY be recorded by the current application login user. Another login user (unholy user) cannot write on this Node b / c their auth.uid will not equal posterId, which posterId is the user to pay.

At the end of the server, I use it once to find out if I can find the value:

var ref = new Firebase('[FirebaseURL]');
app.post('/api/v1/token', jsonParser, function (request, response) {

  var userId = request.body.userId;
  console.log (userId);

  //customerId from braintree is stored here so no one except the server can read it
  ref.child('braintreePayment').child(userId).once("value", function(snapshot){
    var exists = (snapshot.val() !== null);
    console.log (exists);
    if (exists) {
    console.log ("using exsiting customer!");

    //If braintreePaymentToken with userId child exsited, it mean this request is come from my Ionic client, not from anywhere else.

    ref.child('braintreePaymentToken').child(userId).once("value", function(snap) {
    if (snap.val()) {
      gateway.clientToken.generate({
        customerId: snapshot.val().customerId
      }, function (err, res) {
        if (err) throw err;
        response.json({
          "client_token": res.clientToken
        });
        //After I return the clientToken, I delete the braintreePaymentToken node. It is like using Firebase to send email with Zaiper. More secue I guess?
        ref.child('braintreePaymentToken').child(userId).remove();
      });
    else {
      response.json({
          "client_token": "Unauthorized Access!"
        });
    }
 } else {
    console.log ("using no customer!");
    gateway.clientToken.generate({}, function (err, res) {
      if (err) throw err;
      response.json({
        "client_token": res.clientToken
      });
    });
 }
 }); 
});

( ), Firebase Once, , firebase/braintreePayment. , return transaction customerId, braintree.

app.post('/api/v1/process', jsonParser, function (request, response) {

  var transaction = request.body;
  ref.child('braintreePayment').child(transaction.userId).once("value",        function(snapshot){
 var exists = (snapshot.val() !== null);
 console.log (exists);
 if (exists) {
    console.log ("Return customer!");
    gateway.transaction.sale({
      amount: transaction.amount,
      paymentMethodNonce: transaction.payment_method_nonce,
      options: {
        submitForSettlement: true
      },

    }, function (err, result) {
      if (err) throw err;
      response.json(result);
    });
 } else {
    console.log ("First time customer!");
    gateway.transaction.sale({
      amount: transaction.amount,
      paymentMethodNonce: transaction.payment_method_nonce,

      options: {
        store_in_vault_on_success: true,
        submitForSettlement: true
      },

    }, function (err, result) {
      if (err) throw err;

      console.log ("Customer Id: " + result.transaction.customer.id);
      var customerId = result.transaction.customer.id;

           ref.child('braintreePayment').child(transaction.userId).update({customerId: customerId});

      response.json(result);
    });
    }
   });

  });

, . , , ... Firebase, Node Braintree? OWASP? , , ?

!

+4

Source: https://habr.com/ru/post/1607349/


All Articles