Add auth flow

Add persistent storage.


Project: 
http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/commit/f27fba99
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/tree/f27fba99
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/diff/f27fba99

Branch: refs/heads/add-documentation
Commit: f27fba9930db5358259b56a9737b1c6b89c209e8
Parents: 67ed8eb
Author: Boyan Bakov <[email protected]>
Authored: Wed Dec 2 11:57:51 2015 +0200
Committer: Vladislav Mitov <[email protected]>
Committed: Fri Dec 18 18:55:25 2015 +0200

----------------------------------------------------------------------
 .gitignore         |   1 +
 index.js           |   2 +-
 lib/mpin.js        | 341 ++++++++++++++++++++++++++++++++++++++++-----
 test/coverage.html | 360 ------------------------------------------------
 test/index.js      | 150 ++++++++++++++++++--
 test/mocha.opts    |   2 +-
 6 files changed, 454 insertions(+), 402 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/blob/f27fba99/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 727ae60..eb737b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ npm-debug.log
 bower_components/
 nbproject/
 index.html
+test/coverage.html

http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/blob/f27fba99/index.js
----------------------------------------------------------------------
diff --git a/index.js b/index.js
index 3a12526..e7cdcee 100644
--- a/index.js
+++ b/index.js
@@ -1 +1 @@
-module.exports = require('./lib/mpin');
\ No newline at end of file
+module.exports = require('./lib/mpin');
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/blob/f27fba99/lib/mpin.js
----------------------------------------------------------------------
diff --git a/lib/mpin.js b/lib/mpin.js
index 285d749..143916b 100644
--- a/lib/mpin.js
+++ b/lib/mpin.js
@@ -1,11 +1,20 @@
 var mpinjs = (function () {
-  var Mpin, Users = {}, Errors = [];
-
-  Errors[1] = "Invalid userId.";
-  Errors[2] = "UserId does not exist.";
-  Errors[3] = "Missing parameter.";
-
-  Errors[4] = "UserId are not verified.";
+  var Mpin, Users = {}, Errors = [], Status = {};
+
+  Errors[0] = "MISSING_USERID";
+  Errors[1] = "INVALID_USERID";
+  Errors[2] = "MISSING_PARAMETERS";
+  Errors[3] = "IDENTITY_NOT_VERIFIED";
+  Errors[4] = "IDENTITY_MISSING";
+  Errors[5] = "WRONG_PIN";
+  Errors[6] = "WRONG_FLOW";
+
+  ///
+  Status.invalid = "INVALID";
+  Status.start = "STARTED";
+  Status.active = "ACTIVATED";
+  Status.register = "REGISTERED";
+  Status.block = "BLOCKED";
 
   Mpin = function (options) {
     if (!options || !options.settingsUrl) {
@@ -15,7 +24,7 @@ var mpinjs = (function () {
     this.opts = options;
   };
 
-  Mpin.prototype.debug = true;
+  Mpin.prototype.storageKey = "mpinLib";
 
   Mpin.prototype.init = function (cb) {
     var self = this;
@@ -34,13 +43,13 @@ var mpinjs = (function () {
 
   Mpin.prototype.makeNewUser = function (userId, deviceId) {
     if (!userId) {
-      return {error: 1};
+      return {code: 0, type: Errors[0]};
     }
 
     Users[userId] = {
       userId: userId,
       deviceId: deviceId || "",
-      status: "INVALID"
+      status: Status.invalid
     };
 
     return this;
@@ -48,15 +57,15 @@ var mpinjs = (function () {
 
   Mpin.prototype.startRegistration = function (userId, cb) {
     var _reqData = {}, self = this;
-    if (!userId || typeof userId != "string") {
-      return cb ? cb({error: 1}, null) : {error: 1};
+    if (!userId || typeof userId !== "string") {
+      return cb ? cb({code: 0, type: Errors[0]}, null) : {error: 1};
     } else if (!this.checkUser(userId)) {
-      return cb({error: 2}, null);
+      return cb({code: 1, type: Errors[1]}, null);
     } else if (!this.opts.registerURL) {
-      return cb({error: 3, message: "Missing registerURL"}, null);
+      return cb({code: 2, type: Errors[2], message: "Missing registerURL"}, 
null);
     }
 
-    _reqData.url = this.opts.registerURL;
+    _reqData.url = this.generateUrl("register");
     _reqData.type = "PUT";
     _reqData.data = {
       userId: userId,
@@ -68,11 +77,13 @@ var mpinjs = (function () {
         return cb(err, null);
       }
 
-      self.addToUser(userId, {regOTT: data.regOTT, mpinId: data.mpinId,status: 
"STARTED"});
+      self.identity = data.mpinId;
+
+      self.addToUser(userId, {regOTT: data.regOTT, mpinId: data.mpinId, 
status: Status.start});
 
       //force activate
       if (data.active) {
-        self.addToUser(userId, {status: "ACTIVATED"});
+        self.addToUser(userId, {status: Status.active});
       }
 
       cb && cb(null, true);
@@ -82,27 +93,41 @@ var mpinjs = (function () {
 
   //request cs1 + cs2
   Mpin.prototype.confirmRegistration = function (userId, cb) {
-    var _cs1Url = "", self = this;
+    var _cs1Url = "", self = this, _userStatus;
+
+    if (!userId || typeof userId !== "string") {
+      return cb ? cb({code: 0, type: Errors[0]}, null) : {error: 0, type: 
Errors[0]};
+    } else if (!this.checkUser(userId)) {
+      return cb({code: 1, type: Errors[1]}, null);
+    } else if (!this.opts.signatureURL) {
+      return cb({code: 2, type: Errors[2], message: "Missing signatureURL"}, 
null);
+    }
 
-    _cs1Url = this.opts.signatureURL + "/";
-    _cs1Url += Users[userId].mpinId; //identity
-    _cs1Url += "?regOTT=" + Users[userId].regOTT;
+    //started || activated
+    _userStatus = this.getUser(userId, "status");
+    if (_userStatus !== Status.start && _userStatus !== Status.active) {
+      return cb({code: 3, type: Errors[3]}, null);
+    }
 
+    _cs1Url = this.generateUrl('signature', userId);
     //req cs1
     this.request({url: _cs1Url}, function (err, cs1Data) {
       var _cs2Url = "";
       if (err) {
         if (err.status == 401) {
-          return cb({error: 4, message: "Identity not activate."}, null);
+          return cb({code: 3, type: Errors[3], message: "Identity not 
activate."}, null);
+        } else if (err.status == 400) {
+          return cb({code: 4, type: Errors[4]}, null);
         }
       }
 
-      self.addToUser(userId, {cs1: cs1Data.clientSecretShare, csParams: 
cs1Data.params, status: "ACTIVATED"});
+      self.addToUser(userId, {cs1: cs1Data.clientSecretShare, csParams: 
cs1Data.params, status: Status.active});
       _cs2Url = self.opts.certivoxURL + "clientSecret?" + cs1Data.params;
 
       //req cs2
       self.request({url: _cs2Url}, function (err, cs2Data) {
         var csHex;
+
         self.addToUser(userId, {cs2: cs2Data.clientSecret});
         csHex = MPINAuth.addShares(cs2Data.clientSecret, 
cs1Data.clientSecretShare);
 
@@ -118,14 +143,18 @@ var mpinjs = (function () {
   Mpin.prototype.finishRegistration = function (userId, pin) {
     var _user, tokenHex;
 
+    if (!userId || typeof userId !== "string") {
+      return {error: 0, type: Errors[0]};
+    }
+
     _user = this.getUser(userId);
 
-    if (_user.status !== "ACTIVATED") {
-      return {error: 3};
+    if (_user.status !== Status.active) {
+      return {code: 3, type: Errors[3]};
     }
 
     tokenHex = MPINAuth.calculateMPinToken(Users[userId].mpinId, pin, 
Users[userId].csHex);
-    this.addToUser(userId, {tokenHex: tokenHex, status: "REGISTER"});
+    this.addToUser(userId, {tokenHex: tokenHex, status: Status.register});
 
     return tokenHex;
   };
@@ -137,6 +166,209 @@ var mpinjs = (function () {
   };
 
 
+
+  Mpin.prototype.startAuthentication = function (userId, cb) {
+    var _tp1Url, self = this;
+
+    _tp1Url = this.generateUrl('permit1');
+    this.request({url: _tp1Url}, function (err, data) {
+      var _signature, _tp2Url, _timePermit1;
+      _signature = data["signature"];
+      _timePermit1 = data["timePermit"];
+
+      self.addToUser(userId, {currentDate: data['date']});
+
+      _tp2Url = self.generateUrl('permit2');
+      _tp2Url += "&signature=" + _signature;
+
+
+      self.request({url: _tp2Url}, function (err2, data2) {
+        var _timePermit2, timePermitHex;
+        _timePermit2 = data2["timePermit"];
+        timePermitHex = MPINAuth.addShares(_timePermit1, _timePermit2);
+
+        self.addToUser(userId, {timePermitHex: timePermitHex});
+
+        cb && cb(null, true);
+      });
+
+    });
+  };
+
+  Mpin.prototype.finishAuthentication = function (userId, aPin, cb) {
+    var self = this, _reqData = {};
+
+    _reqData.url = this.generateUrl("pass1");
+    _reqData.type = "POST";
+    _reqData.data = this.getAuthData(userId, aPin);
+
+    //  pass1
+    this.request(_reqData, function (pass1Err, pass1Data) {
+      var _req2Data = {};
+      _req2Data.url = self.generateUrl("pass2");
+      _req2Data.type = "POST";
+
+      _req2Data.data = MPINAuth.pass2Request(pass1Data.y, false, "0");
+      _req2Data.data.mpin_id = Users[userId].mpinId;
+
+      // pass 2
+      self.request(_req2Data, function (pass2Err, pass2Data) {
+        var _req3Data = {};
+
+        if (pass2Data["OTP"]) {
+          delete pass2Data["OTP"];
+        }
+
+        _req3Data.url = self.generateUrl("auth");
+        _req3Data.type = "POST";
+        _req3Data.data = {mpinResponse: pass2Data};
+
+        self.request(_req3Data, function (authErr, authData) {
+          if (authErr) {
+            if (authErr.status === 401) {
+              return cb({code: 5, type: Errors[5]}, null);
+            }
+          }
+
+          console.log("auth Data :::", authData);
+        });
+      });
+    });
+  };
+
+  Mpin.prototype.getAuthData = function (userId, aPin) {
+    var _auth = {};
+
+    _auth.mpin = this.identity;
+    _auth.token = Users[userId].tokenHex;
+    _auth.timePermit = Users[userId].timePermitHex;
+    _auth.date = Users[userId].currentDate;
+
+    return MPINAuth.pass1Request(_auth.mpin, _auth.token, _auth.timePermit, 
aPin, _auth.date, null);
+  };
+
+  Mpin.prototype.fromHex = function (strData) {
+    if (!strData || strData.length % 2 != 0)
+      return '';
+    strData = strData.toLowerCase();
+    var digits = '0123456789abcdef';
+    var result = '';
+    for (var i = 0; i < strData.length; ) {
+      var a = digits.indexOf(strData.charAt(i++));
+      var b = digits.indexOf(strData.charAt(i++));
+      if (a < 0 || b < 0)
+        return '';
+      result += String.fromCharCode(a * 16 + b);
+    }
+    return result;
+  };
+
+  Mpin.prototype.getAccessNumber = function (cb) {
+    var self = this, _reqData = {};
+
+    _reqData.url = this.generateUrl("getnumber");
+    _reqData.type = "POST";
+
+    this.request(_reqData, function (err, data) {
+      if (err) {
+        return cb(err, null);
+      }
+      console.log("OTT :::", data.webOTT);
+      self.webOTT = data.webOTT;
+      cb(null, {accessNumber: data.accessNumber});
+
+    });
+  };
+
+  Mpin.prototype.getAccess = function (cb) {
+    var self = this, _reqData = {};
+
+    if (!this.webOTT) {
+      return cb({code: 6, type: Errors[6], message: "Need to call 
getAccessNumber method before this."}, null);
+    }
+
+    _reqData.url = this.generateUrl("getaccess");
+    _reqData.type = "POST";
+    _reqData.data = {webOTT: this.webOTT};
+
+    this.request(_reqData, function (err, data) {
+      if (err) {
+        if (err.status === 401) {
+          self.intervalID2 = setTimeout(function () {
+            self.getAccess.call(self, cb);
+          }, 3000);
+          return;
+        }
+      }
+
+      /*
+       *                                       self.intervalID2 = 
setTimeout(function () {
+       self.getAccess.call(self);
+       }, 3000);
+       * 
+       */
+      console.log("data :::", err, data);
+      
+      cb(null, true);
+
+
+    });
+  };
+
+  Mpin.prototype.stopAccess = function () {
+    if (this.intervalID2) {
+      clearInterval(this.intervalID2);
+    }
+  };
+
+
+  Mpin.prototype.generateUrl = function (type, userId) {
+    var url, mpData, mpin_id_bytes, hash_mpin_id_bytes = [], hash_mpin_id_hex;
+
+    switch (type) {
+      case "register":
+        url = this.opts.registerURL;
+        break;
+      case "signature":
+        url = this.opts.signatureURL + "/";
+        url += Users[userId].mpinId;
+        url += "?regOTT=" + Users[userId].regOTT;
+        break;
+      case "permit1":
+        url = this.opts.timePermitsURL + "/";
+        url += this.identity;
+        break;
+      case "permit2":
+        mpData = this.fromHex(this.identity);
+        mpin_id_bytes = MPIN.stringtobytes(mpData);
+        hash_mpin_id_bytes = MPIN.HASH_ID(mpin_id_bytes);
+        hash_mpin_id_hex = MPIN.bytestostring(hash_mpin_id_bytes);
+        url = this.opts.certivoxURL + "timePermit";
+        url += "?app_id=" + this.opts.appID;
+        url += "&mobile=0";
+        url += "&hash_mpin_id=" + hash_mpin_id_hex;
+        break;
+      case "pass1":
+        url = this.opts.mpinAuthServerURL + "/pass1";
+        break;
+      case "pass2":
+        url = this.opts.mpinAuthServerURL + "/pass2";
+        break;
+      case "auth":
+        url = this.opts.authenticateURL;
+        break;
+      case "getnumber":
+        url = this.opts.getAccessNumberURL;
+        break;
+      case "getaccess":
+        url = this.opts.accessNumberURL;
+        break;
+    }
+
+    return url;
+  };
+
+
   Mpin.prototype.listUsers = function () {
     var listUsers = {};
     for (var uKey in Users) {
@@ -153,13 +385,12 @@ var mpinjs = (function () {
     return (Users[userId]) ? true : false;
   };
 
-
-  Mpin.prototype.getUser = function (userId) {
+  Mpin.prototype.getUser = function (userId, property) {
     var _user = {};
     if (!userId) {
-      return {error: 0};
+      return {code: 0, type: Errors[0]};
     } else if (!this.checkUser(userId)) {
-      return {error: 2};
+      return {code: 1, type: Errors[1]};
     }
 
     _user = {
@@ -168,7 +399,11 @@ var mpinjs = (function () {
       status: Users[userId].status
     };
 
-    return _user;
+    if (!property) {
+      return _user;
+    } else if (property && _user[property]) {
+      return _user[property];
+    }
   };
 
   Mpin.prototype.addToUser = function (userId, userProps) {
@@ -179,12 +414,55 @@ var mpinjs = (function () {
     for (var uKey in userProps) {
       Users[userId][uKey] = userProps[uKey];
     }
+
+    this.setData(userId, userProps);
   };
 
   Mpin.prototype.restore = function () {
     Users = {};
   };
 
+  Mpin.prototype.setData = function (userId, upData) {
+    var mpinData;
+    mpinData = JSON.parse(localStorage.getItem(this.storageKey));
+
+    if (!mpinData) {
+      mpinData = {
+        defaultIdentity: "",
+        version: "0.3",
+        accounts: {}
+      };
+    }
+
+    //update Default Identity
+    if (upData.mpinId) {
+      mpinData.defaultIdentity = upData.mpinId;
+      mpinData.accounts[upData.mpinId] = {};
+    }
+
+    if (upData.regOTT) {
+      mpinData.accounts[upData.mpinId].regOTT = upData.regOTT;
+    }
+
+    if (upData.timePermitHex) {
+      var mpinId = Users[userId].mpinId;
+      mpinData.accounts[mpinId].MPinPermit = upData.timePermitHex;
+    }
+
+    if (upData.tokenHex) {
+      var mpinId = Users[userId].mpinId;
+      mpinData.accounts[mpinId].token = upData.tokenHex;
+    }
+
+    localStorage.setItem(this.storageKey, JSON.stringify(mpinData));
+  };
+
+  Mpin.prototype.getData = function () {
+    var mpinData;
+    mpinData = JSON.parse(localStorage.getItem(this.storageKey));
+    return mpinData;
+  };
+
 //{url: url, type: "get post put", data: data}
   Mpin.prototype.request = function (options, cb) {
     var _request = new XMLHttpRequest(), _url, _type, _data;
@@ -201,6 +479,7 @@ var mpinjs = (function () {
     };
 
     _request.open(_type, _url, true);
+    _request.setRequestHeader("Content-Type", "application/json");
     _request.send(JSON.stringify(_data || ""));
   };
 

http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/blob/f27fba99/test/coverage.html
----------------------------------------------------------------------
diff --git a/test/coverage.html b/test/coverage.html
deleted file mode 100644
index 8d6f974..0000000
--- a/test/coverage.html
+++ /dev/null
@@ -1,360 +0,0 @@
-err  { error: 3, message: 'Missing registerURL' } null
-<!DOCTYPE html><html><head><title>Coverage</title><meta 
charset="utf-8"><script>
-
-headings = [];
-
-onload = function(){
-  headings = document.querySelectorAll('h2');
-};
-
-onscroll = function(e){
-  var heading = find(window.scrollY);
-  if (!heading) return;
-  var links = document.querySelectorAll('#menu a')
-    , link;
-
-  for (var i = 0, len = links.length; i < len; ++i) {
-    link = links[i];
-    link.className = link.getAttribute('href') == '#' + heading.id
-      ? 'active'
-      : '';
-  }
-};
-
-function find(y) {
-  var i = headings.length
-    , heading;
-
-  while (i--) {
-    heading = headings[i];
-    if (y >= heading.offsetTop) {
-      return heading;
-    }
-  }
-}
-</script>
-<style>
-
-body {
-  font: 14px/1.6 "Helvetica Neue", Helvetica, Arial, sans-serif;
-  margin: 0;
-  color: #2C2C2C;
-  border-top: 2px solid #ddd;
-}
-
-#coverage {
-  padding: 60px 400px 60px 60px;
-}
-
-h1 a {
-  color: inherit;
-  font-weight: inherit;
-}
-
-h1 a:hover {
-  text-decoration: none;
-}
-
-.onload h1 {
-  opacity: 1;
-}
-
-h2 {
-  width: 80%;
-  margin-top: 80px;
-  margin-bottom: 0;
-  font-weight: 100;
-  letter-spacing: 1px;
-  border-bottom: 1px solid #eee;
-}
-
-a {
-  color: #8A6343;
-  font-weight: bold;
-  text-decoration: none;
-}
-
-a:hover {
-  text-decoration: underline;
-}
-
-ul {
-  margin-top: 20px;
-  padding: 0 15px;
-  width: 100%;
-}
-
-ul li {
-  float: left;
-  width: 40%;
-  margin-top: 5px;
-  margin-right: 60px;
-  list-style: none;
-  border-bottom: 1px solid #eee;
-  padding: 5px 0;
-  font-size: 12px;
-}
-
-ul::after {
-  content: '.';
-  height: 0;
-  display: block;
-  visibility: hidden;
-  clear: both;
-}
-
-code {
-  font: 12px monaco, monospace;
-}
-
-pre {
-  margin: 30px;
-  padding: 30px;
-  border: 1px solid #eee;
-  border-bottom-color: #ddd;
-  -webkit-border-radius: 2px;
-  -moz-border-radius: 2px;
-  border-radius: 2px;
-  -webkit-box-shadow: inset 0 0 10px #eee;
-  -moz-box-shadow: inset 0 0 10px #eee;
-  box-shadow: inset 0 0 10px #eee;
-  overflow-x: auto;
-}
-
-img {
-  margin: 30px;
-  padding: 1px;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-  -webkit-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
-  -moz-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
-  box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
-  max-width: 100%;
-}
-
-footer {
-  background: #eee;
-  width: 100%;
-  padding: 50px 0;
-  text-align: right;
-  border-top: 1px solid #ddd;
-}
-
-footer span {
-  display: block;
-  margin-right: 30px;
-  color: #888;
-  font-size: 12px;
-}
-
-#menu {
-  position: fixed;
-  font-size: 12px;
-  overflow-y: auto;
-  top: 0;
-  right: 0;
-  margin: 0;
-  height: 100%;
-  padding: 15px 0;
-  text-align: right;
-  border-left: 1px solid #eee;
-  max-width: 400px;
-  overflow: auto;
-  white-space: nowrap;
-  
-  -moz-box-shadow: 0 0 2px #888
-     , inset 5px 0 20px rgba(0,0,0,.5)
-     , inset 5px 0 3px rgba(0,0,0,.3);
-  -webkit-box-shadow: 0 0 2px #888
-     , inset 5px 0 20px rgba(0,0,0,.5)
-     , inset 5px 0 3px rgba(0,0,0,.3);
-  box-shadow: 0 0 2px #888
-     , inset 5px 0 20px rgba(0,0,0,.5)
-     , inset 5px 0 3px rgba(0,0,0,.3);
-  -webkit-font-smoothing: antialiased;
-  background: 
url("data:image/png;base64,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");
-}
-
-#menu::after {
-  display: block;
-  content: '';
-  padding-top: 80px;
-}
-
-#logo {
-  position: fixed;
-  bottom: 10px;
-  right: 10px;
-  background: rgba(255,255,255,.1);
-  font-size: 11px;
-  display: block;
-  width: 20px;
-  height: 20px;
-  line-height: 20px;
-  text-align: center;
-  -webkit-border-radius: 20px;
-  -moz-border-radius: 20px;
-  border-radius: 20px;
-  -webkit-box-shadow: 0 0 3px rgba(0,0,0,.2);
-  -moz-box-shadow: 0 0 3px rgba(0,0,0,.2);
-  box-shadow: 0 0 3px rgba(0,0,0,.2);
-  color: inherit;
-}
-
-#menu li a {
-  display: block;
-  color: white;
-  padding: 0 35px 0 25px;
-  -webkit-transition: background 300ms;
-  -moz-transition: background 300ms;
-}
-
-#menu li {
-  position: relative;
-  list-style: none;
-}
-
-#menu a:hover,
-#menu a.active {
-  text-decoration: none;
-  background: rgba(255,255,255,.1);
-}
-
-#menu li:hover .cov {
-  opacity: 1;
-}
-
-#menu li .dirname {
-  opacity: .60;
-  padding-right: 2px;
-}
-
-#menu li .basename {
-  opacity: 1;
-}
-
-#menu .cov {
-  background: rgba(0,0,0,.4);
-  position: absolute;
-  top: 0;
-  right: 8px;
-  font-size: 9px;
-  opacity: .6;
-  text-align: left;
-  width: 17px;
-  -webkit-border-radius: 10px;
-  -moz-border-radius: 10px;
-  border-radius: 10px;
-  padding: 2px 3px;
-  text-align: center;
-}
-
-#stats:nth-child(2n) {
-  display: inline-block;
-  margin-top: 15px;
-  border: 1px solid #eee;
-  padding: 10px;
-  -webkit-box-shadow: inset 0 0 2px #eee;
-  -moz-box-shadow: inset 0 0 2px #eee;
-  box-shadow: inset 0 0 2px #eee;
-  -webkit-border-radius: 5px;
-  -moz-border-radius: 5px;
-  border-radius: 5px;
-}
-
-#stats div {
-  float: left;
-  padding: 0 5px;
-}
-
-#stats::after {
-  display: block;
-  content: '';
-  clear: both;
-}
-
-#stats .sloc::after {
-  content: ' SLOC';
-  color: #b6b6b6;
-}
-
-#stats .percentage::after {
-  content: ' coverage';
-  color: #b6b6b6;
-}
-
-#stats .hits,
-#stats .misses {
-  display: none;
-}
-
-.high {
-  color: #00d4b4;
-}
-.medium {
-  color: #e87d0d;
-}
-.low {
-  color: #d4081a;
-}
-.terrible {
-  color: #d4081a;
-  font-weight: bold;
-}
-
-table {
-  width: 80%;
-  margin-top: 10px;
-  border-collapse: collapse;
-  border: 1px solid #cbcbcb;
-  color: #363636;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-}
-
-table thead {
-  display: none;
-}
-
-table td.line,
-table td.hits {
-  width: 20px;
-  background: #eaeaea;
-  text-align: center;
-  font-size: 11px;
-  padding: 0 10px;
-  color: #949494;
-}
-
-table td.hits {
-  width: 10px;
-  padding: 2px 5px;
-  color: rgba(0,0,0,.2);
-  background: #f0f0f0;
-}
-
-tr.miss td.line,
-tr.miss td.hits {
-  background: #e6c3c7;
-}
-
-tr.miss td {
-  background: #f8d5d8;
-}
-
-td.source {
-  padding-left: 15px;
-  line-height: 15px;
-  white-space: pre;
-  font: 12px monaco, monospace;
-}
-
-code .comment { color: #ddd }
-code .init { color: #2F6FAD }
-code .string { color: #5890AD }
-code .keyword { color: #8A6343 }
-code .number { color: #2F6FAD }
-</style>
-</head><body><div id="coverage"><h1 id="overview">Coverage</h1><div 
id="menu"><li><a href="#overview">overview</a></li><li><span class="cov 
medium">52</span><a href="#/var/www/mpinjs/lib/mpin.js"><span 
class="dirname">/var/www/mpinjs/lib/</span><span 
class="basename">mpin.js</span></a></li><a id="logo" 
href="http://mochajs.org/";>m</a></div><div id="stats" class="medium"><div 
class="percentage">52%</div><div class="sloc">109</div><div 
class="hits">57</div><div class="misses">52</div></div><div id="files"><div 
class="file"><h2 
id="/var/www/mpinjs/lib/mpin.js">/var/www/mpinjs/lib/mpin.js</h2><div 
id="stats" class="medium"><div class="percentage">52%</div><div 
class="sloc">109</div><div class="hits">57</div><div 
class="misses">52</div></div><table 
id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr
 class="hit"><td class="line">1</td><td class="hits">1</td><td 
class="source">var mpinjs = (function () {</td></tr><tr class="hit"><td 
class="line">2</td><t
 d class="hits">1</td><td class="source">  var Mpin, Users = {}, Errors = 
[];</td></tr><tr><td class="line">3</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">4</td><td 
class="hits">1</td><td class="source">  Errors[1] = &quot;Invalid 
userId.&quot;;</td></tr><tr class="hit"><td class="line">5</td><td 
class="hits">1</td><td class="source">  Errors[2] = &quot;UserId does not 
exist.&quot;;</td></tr><tr class="hit"><td class="line">6</td><td 
class="hits">1</td><td class="source">  Errors[3] = &quot;Missing 
parameter.&quot;;</td></tr><tr><td class="line">7</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">8</td><td 
class="hits">1</td><td class="source">  Errors[4] = &quot;UserId are not 
verified.&quot;;</td></tr><tr><td class="line">9</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">10</td><td 
class="hits">1</td><td class="source">  Mpin = function (options) {</td></tr><t
 r class="hit"><td class="line">11</td><td class="hits">5</td><td 
class="source">    if (!options || !options.settingsUrl) {</td></tr><tr 
class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">  
    return new Error(&quot;Missing settings URL&quot;);</td></tr><tr><td 
class="line">13</td><td class="hits"></td><td class="source">    
}</td></tr><tr><td class="line">14</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">15</td><td 
class="hits">4</td><td class="source">    this.opts = options;</td></tr><tr><td 
class="line">16</td><td class="hits"></td><td class="source">  
};</td></tr><tr><td class="line">17</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">18</td><td 
class="hits">1</td><td class="source">  Mpin.prototype.debug = 
true;</td></tr><tr><td class="line">19</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">20</td><td 
class="hits">1</td><td cla
 ss="source">  Mpin.prototype.init = function (cb) {</td></tr><tr 
class="hit"><td class="line">21</td><td class="hits">5</td><td class="source">  
  var self = this;</td></tr><tr><td class="line">22</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">23</td><td class="hits">5</td><td class="source">    
this.request({url: this.opts.settingsUrl}, function (err, data) {</td></tr><tr 
class="hit"><td class="line">24</td><td class="hits">3</td><td class="source">  
    if (err &amp;&amp; cb) {</td></tr><tr class="hit"><td 
class="line">25</td><td class="hits">1</td><td class="source">        return 
cb(err, null);</td></tr><tr><td class="line">26</td><td class="hits"></td><td 
class="source">      }</td></tr><tr><td class="line">27</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">28</td><td class="hits">2</td><td class="source">      self.ready 
= true;</td></tr><tr class="hit"><td class="line">29</td><td class="hits">2
 </td><td class="source">      self.opts = data;</td></tr><tr><td 
class="line">30</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="hit"><td class="line">31</td><td class="hits">2</td><td class="source">  
    cb &amp;&amp; cb(null, data);</td></tr><tr><td class="line">32</td><td 
class="hits"></td><td class="source">    });</td></tr><tr><td 
class="line">33</td><td class="hits"></td><td class="source">  
};</td></tr><tr><td class="line">34</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">35</td><td 
class="hits">1</td><td class="source">  Mpin.prototype.makeNewUser = function 
(userId, deviceId) {</td></tr><tr class="hit"><td class="line">36</td><td 
class="hits">4</td><td class="source">    if (!userId) {</td></tr><tr 
class="hit"><td class="line">37</td><td class="hits">1</td><td class="source">  
    return {error: 1};</td></tr><tr><td class="line">38</td><td 
class="hits"></td><td class="source">    }</td></tr><tr><td class="line">3
 9</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">40</td><td class="hits">3</td><td class="source">    Users[userId] 
= {</td></tr><tr><td class="line">41</td><td class="hits"></td><td 
class="source">      userId: userId,</td></tr><tr><td class="line">42</td><td 
class="hits"></td><td class="source">      deviceId: deviceId || 
&quot;&quot;,</td></tr><tr><td class="line">43</td><td class="hits"></td><td 
class="source">      status: &quot;STARTED&quot;</td></tr><tr><td 
class="line">44</td><td class="hits"></td><td class="source">    
};</td></tr><tr><td class="line">45</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">46</td><td 
class="hits">3</td><td class="source">    return this;</td></tr><tr><td 
class="line">47</td><td class="hits"></td><td class="source">  
};</td></tr><tr><td class="line">48</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">49</td><td 
class="hits">1
 </td><td class="source">  Mpin.prototype.startRegistration = function (userId, 
cb) {</td></tr><tr class="hit"><td class="line">50</td><td 
class="hits">4</td><td class="source">    var _reqData = {}, self = 
this;</td></tr><tr class="hit"><td class="line">51</td><td 
class="hits">4</td><td class="source">    if (!userId || typeof userId != 
&quot;string&quot;) {</td></tr><tr class="hit"><td class="line">52</td><td 
class="hits">1</td><td class="source">      return cb ? cb({error: 1}, null) : 
{error: 1};</td></tr><tr class="hit"><td class="line">53</td><td 
class="hits">3</td><td class="source">    } else if (!this.checkUser(userId)) 
{</td></tr><tr class="hit"><td class="line">54</td><td class="hits">1</td><td 
class="source">      return cb({error: 2}, null);</td></tr><tr class="hit"><td 
class="line">55</td><td class="hits">2</td><td class="source">    } else if 
(!this.opts.registerURL) {</td></tr><tr class="hit"><td class="line">56</td><td 
class="hits">1</td><td class="source">      retu
 rn cb({error: 3, message: &quot;Missing registerURL&quot;}, 
null);</td></tr><tr><td class="line">57</td><td class="hits"></td><td 
class="source">    }</td></tr><tr><td class="line">58</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">59</td><td class="hits">1</td><td class="source">    _reqData.url 
= this.opts.registerURL;</td></tr><tr class="hit"><td class="line">60</td><td 
class="hits">1</td><td class="source">    _reqData.type = 
&quot;PUT&quot;;</td></tr><tr class="hit"><td class="line">61</td><td 
class="hits">1</td><td class="source">    _reqData.data = {</td></tr><tr><td 
class="line">62</td><td class="hits"></td><td class="source">      userId: 
userId,</td></tr><tr><td class="line">63</td><td class="hits"></td><td 
class="source">      mobile: 0</td></tr><tr><td class="line">64</td><td 
class="hits"></td><td class="source">    };</td></tr><tr><td 
class="line">65</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="hit"><td clas
 s="line">66</td><td class="hits">1</td><td class="source">    
this.request(_reqData, function (err, data) {</td></tr><tr class="hit"><td 
class="line">67</td><td class="hits">1</td><td class="source">      if (err) 
{</td></tr><tr class="miss"><td class="line">68</td><td class="hits">0</td><td 
class="source">        return cb(err, null);</td></tr><tr><td 
class="line">69</td><td class="hits"></td><td class="source">      
}</td></tr><tr><td class="line">70</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">71</td><td 
class="hits">1</td><td class="source">      self.addToUser(userId, {regOTT: 
data.regOTT, mpinId: data.mpinId});</td></tr><tr><td class="line">72</td><td 
class="hits"></td><td class="source"> </td></tr><tr><td class="line">73</td><td 
class="hits"></td><td class="source">      //force activate</td></tr><tr 
class="hit"><td class="line">74</td><td class="hits">1</td><td class="source">  
    if (data.active) {</td></tr><tr class="miss"><td c
 lass="line">75</td><td class="hits">0</td><td class="source">        
self.addToUser(userId, {status: &quot;ACTIVATED&quot;});</td></tr><tr><td 
class="line">76</td><td class="hits"></td><td class="source">      
}</td></tr><tr><td class="line">77</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">78</td><td 
class="hits">1</td><td class="source">      cb &amp;&amp; cb(null, 
true);</td></tr><tr><td class="line">79</td><td class="hits"></td><td 
class="source">    });</td></tr><tr><td class="line">80</td><td 
class="hits"></td><td class="source">  };</td></tr><tr><td 
class="line">81</td><td class="hits"></td><td class="source"> </td></tr><tr><td 
class="line">82</td><td class="hits"></td><td class="source"> </td></tr><tr><td 
class="line">83</td><td class="hits"></td><td class="source">  //request cs1 + 
cs2</td></tr><tr class="hit"><td class="line">84</td><td class="hits">1</td><td 
class="source">  Mpin.prototype.confirmRegistration = function (userId, c
 b) {</td></tr><tr class="miss"><td class="line">85</td><td 
class="hits">0</td><td class="source">    var _cs1Url = &quot;&quot;, self = 
this;</td></tr><tr><td class="line">86</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="miss"><td class="line">87</td><td 
class="hits">0</td><td class="source">    _cs1Url = this.opts.signatureURL + 
&quot;/&quot;;</td></tr><tr class="miss"><td class="line">88</td><td 
class="hits">0</td><td class="source">    _cs1Url += Users[userId].mpinId; 
//identity</td></tr><tr class="miss"><td class="line">89</td><td 
class="hits">0</td><td class="source">    _cs1Url += &quot;?regOTT=&quot; + 
Users[userId].regOTT;</td></tr><tr><td class="line">90</td><td 
class="hits"></td><td class="source"> </td></tr><tr><td class="line">91</td><td 
class="hits"></td><td class="source">    //req cs1</td></tr><tr 
class="miss"><td class="line">92</td><td class="hits">0</td><td class="source"> 
   this.request({url: _cs1Url}, function (err, cs1Data) {</td></tr><tr c
 lass="miss"><td class="line">93</td><td class="hits">0</td><td class="source"> 
     var _cs2Url = &quot;&quot;;</td></tr><tr class="miss"><td 
class="line">94</td><td class="hits">0</td><td class="source">      if (err) 
{</td></tr><tr class="miss"><td class="line">95</td><td class="hits">0</td><td 
class="source">        if (err.status == 401) {</td></tr><tr class="miss"><td 
class="line">96</td><td class="hits">0</td><td class="source">          return 
cb({error: 2, message: &quot;Identity not activate.&quot;}, 
null);</td></tr><tr><td class="line">97</td><td class="hits"></td><td 
class="source">        }</td></tr><tr><td class="line">98</td><td 
class="hits"></td><td class="source">      }</td></tr><tr><td 
class="line">99</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="miss"><td class="line">100</td><td class="hits">0</td><td 
class="source">      self.addToUser(userId, {cs1: cs1Data.clientSecretShare, 
csParams: cs1Data.params, status: &quot;ACTIVATED&quot;});</td></t
 r><tr class="miss"><td class="line">101</td><td class="hits">0</td><td 
class="source">      _cs2Url = self.opts.certivoxURL + 
&quot;clientSecret?&quot; + cs1Data.params;</td></tr><tr><td 
class="line">102</td><td class="hits"></td><td class="source"> 
</td></tr><tr><td class="line">103</td><td class="hits"></td><td 
class="source">      //req cs2</td></tr><tr class="miss"><td 
class="line">104</td><td class="hits">0</td><td class="source">      
self.request({url: _cs2Url}, function (err, cs2Data) {</td></tr><tr 
class="miss"><td class="line">105</td><td class="hits">0</td><td 
class="source">        var csHex;</td></tr><tr class="miss"><td 
class="line">106</td><td class="hits">0</td><td class="source">        
self.addToUser(userId, {cs2: cs2Data.clientSecret});</td></tr><tr 
class="miss"><td class="line">107</td><td class="hits">0</td><td 
class="source">        csHex = MPINAuth.addShares(cs2Data.clientSecret, 
cs1Data.clientSecretShare);</td></tr><tr><td class="line">108</td><td 
class="hits
 "></td><td class="source"> </td></tr><tr class="miss"><td 
class="line">109</td><td class="hits">0</td><td class="source">        
self.addToUser(userId, {csHex: csHex});</td></tr><tr><td 
class="line">110</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="miss"><td class="line">111</td><td class="hits">0</td><td 
class="source">        cb(null, true);</td></tr><tr><td 
class="line">112</td><td class="hits"></td><td class="source">      
});</td></tr><tr><td class="line">113</td><td class="hits"></td><td 
class="source">    });</td></tr><tr><td class="line">114</td><td 
class="hits"></td><td class="source">  };</td></tr><tr><td 
class="line">115</td><td class="hits"></td><td class="source"> 
</td></tr><tr><td class="line">116</td><td class="hits"></td><td 
class="source"> </td></tr><tr><td class="line">117</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">118</td><td class="hits">1</td><td class="source">  
Mpin.prototype.finishRegistration
  = function (userId, pin) {</td></tr><tr class="miss"><td 
class="line">119</td><td class="hits">0</td><td class="source">    var _user, 
tokenHex;</td></tr><tr><td class="line">120</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="miss"><td class="line">121</td><td 
class="hits">0</td><td class="source">    _user = 
this.getUser(userId);</td></tr><tr><td class="line">122</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="miss"><td 
class="line">123</td><td class="hits">0</td><td class="source">    if 
(_user.status !== &quot;ACTIVATED&quot;) {</td></tr><tr class="miss"><td 
class="line">124</td><td class="hits">0</td><td class="source">      return 
{error: 3};</td></tr><tr><td class="line">125</td><td class="hits"></td><td 
class="source">    }</td></tr><tr><td class="line">126</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="miss"><td 
class="line">127</td><td class="hits">0</td><td class="source">    tokenHex = 
MPINAuth.calculateMPinToken
 (Users[userId].mpinId, pin, Users[userId].csHex);</td></tr><tr 
class="miss"><td class="line">128</td><td class="hits">0</td><td 
class="source">    this.addToUser(userId, {tokenHex: tokenHex, status: 
&quot;REGISTER&quot;});</td></tr><tr><td class="line">129</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="miss"><td 
class="line">130</td><td class="hits">0</td><td class="source">    return 
tokenHex;</td></tr><tr><td class="line">131</td><td class="hits"></td><td 
class="source">  };</td></tr><tr><td class="line">132</td><td 
class="hits"></td><td class="source"> </td></tr><tr><td 
class="line">133</td><td class="hits"></td><td class="source">  //Put user / 
mpinId</td></tr><tr class="hit"><td class="line">134</td><td 
class="hits">1</td><td class="source">  Mpin.prototype.restartRegistration = 
function (userId, deviceId, cb) {</td></tr><tr class="miss"><td 
class="line">135</td><td class="hits">0</td><td class="source">    var err = 
null, data = {};</td></tr><tr class="miss
 "><td class="line">136</td><td class="hits">0</td><td class="source">    
cb(err, data);</td></tr><tr><td class="line">137</td><td class="hits"></td><td 
class="source">  };</td></tr><tr><td class="line">138</td><td 
class="hits"></td><td class="source"> </td></tr><tr><td 
class="line">139</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="hit"><td class="line">140</td><td class="hits">1</td><td class="source"> 
 Mpin.prototype.listUsers = function () {</td></tr><tr class="miss"><td 
class="line">141</td><td class="hits">0</td><td class="source">    var 
listUsers = {};</td></tr><tr class="miss"><td class="line">142</td><td 
class="hits">0</td><td class="source">    for (var uKey in Users) 
{</td></tr><tr class="miss"><td class="line">143</td><td class="hits">0</td><td 
class="source">      listUsers[uKey] = {</td></tr><tr><td 
class="line">144</td><td class="hits"></td><td class="source">        userId: 
Users[uKey].userId,</td></tr><tr><td class="line">145</td><td class="hits"
 ></td><td class="source">        deviceId: 
 >Users[uKey].deviceId,</td></tr><tr><td class="line">146</td><td 
 >class="hits"></td><td class="source">        status: 
 >Users[uKey].status</td></tr><tr><td class="line">147</td><td 
 >class="hits"></td><td class="source">      };</td></tr><tr><td 
 >class="line">148</td><td class="hits"></td><td class="source">    
 >}</td></tr><tr class="miss"><td class="line">149</td><td 
 >class="hits">0</td><td class="source">    return listUsers;</td></tr><tr><td 
 >class="line">150</td><td class="hits"></td><td class="source">  
 >};</td></tr><tr><td class="line">151</td><td class="hits"></td><td 
 >class="source"> </td></tr><tr class="hit"><td class="line">152</td><td 
 >class="hits">1</td><td class="source">  Mpin.prototype.checkUser = function 
 >(userId) {</td></tr><tr class="hit"><td class="line">153</td><td 
 >class="hits">5</td><td class="source">    return (Users[userId]) ? true : 
 >false;</td></tr><tr><td class="line">154</td><td class="hits"></td><td 
 >class="source">  };</td><
 /tr><tr><td class="line">155</td><td class="hits"></td><td class="source"> 
</td></tr><tr><td class="line">156</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="hit"><td class="line">157</td><td 
class="hits">1</td><td class="source">  Mpin.prototype.getUser = function 
(userId) {</td></tr><tr class="miss"><td class="line">158</td><td 
class="hits">0</td><td class="source">    var _user = {};</td></tr><tr 
class="miss"><td class="line">159</td><td class="hits">0</td><td 
class="source">    if (!userId) {</td></tr><tr class="miss"><td 
class="line">160</td><td class="hits">0</td><td class="source">      return 
{error: 4};</td></tr><tr class="miss"><td class="line">161</td><td 
class="hits">0</td><td class="source">    } else if (!this.checkUser(userId)) 
{</td></tr><tr class="miss"><td class="line">162</td><td class="hits">0</td><td 
class="source">      return {error: 2};</td></tr><tr><td 
class="line">163</td><td class="hits"></td><td class="source">    
}</td></tr><tr><td cla
 ss="line">164</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="miss"><td class="line">165</td><td class="hits">0</td><td 
class="source">    _user = {</td></tr><tr><td class="line">166</td><td 
class="hits"></td><td class="source">      userId: 
Users[userId].userId,</td></tr><tr><td class="line">167</td><td 
class="hits"></td><td class="source">      deviceId: 
Users[userId].deviceId,</td></tr><tr><td class="line">168</td><td 
class="hits"></td><td class="source">      status: 
Users[userId].status</td></tr><tr><td class="line">169</td><td 
class="hits"></td><td class="source">    };</td></tr><tr><td 
class="line">170</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="miss"><td class="line">171</td><td class="hits">0</td><td 
class="source">    return _user;</td></tr><tr><td class="line">172</td><td 
class="hits"></td><td class="source">  };</td></tr><tr><td 
class="line">173</td><td class="hits"></td><td class="source"> </td></tr><tr 
class="hit"><td class="li
 ne">174</td><td class="hits">1</td><td class="source">  
Mpin.prototype.addToUser = function (userId, userProps) {</td></tr><tr 
class="hit"><td class="line">175</td><td class="hits">1</td><td class="source"> 
   if (!this.checkUser(userId)) {</td></tr><tr class="miss"><td 
class="line">176</td><td class="hits">0</td><td class="source">      return 
false;</td></tr><tr><td class="line">177</td><td class="hits"></td><td 
class="source">    }</td></tr><tr><td class="line">178</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">179</td><td class="hits">1</td><td class="source">    for (var 
uKey in userProps) {</td></tr><tr class="hit"><td class="line">180</td><td 
class="hits">2</td><td class="source">      Users[userId][uKey] = 
userProps[uKey];</td></tr><tr><td class="line">181</td><td 
class="hits"></td><td class="source">    }</td></tr><tr><td 
class="line">182</td><td class="hits"></td><td class="source">  
};</td></tr><tr><td class="line">183</td><td cla
 ss="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">184</td><td class="hits">1</td><td class="source">  
Mpin.prototype.restore = function () {</td></tr><tr class="hit"><td 
class="line">185</td><td class="hits">1</td><td class="source">    Users = 
{};</td></tr><tr><td class="line">186</td><td class="hits"></td><td 
class="source">  };</td></tr><tr><td class="line">187</td><td 
class="hits"></td><td class="source"> </td></tr><tr><td 
class="line">188</td><td class="hits"></td><td class="source">//{url: url, 
type: &quot;get post put&quot;, data: data}</td></tr><tr class="hit"><td 
class="line">189</td><td class="hits">1</td><td class="source">  
Mpin.prototype.request = function (options, cb) {</td></tr><tr class="miss"><td 
class="line">190</td><td class="hits">0</td><td class="source">    var _request 
= new XMLHttpRequest(), _url, _type, _data;</td></tr><tr class="miss"><td 
class="line">191</td><td class="hits">0</td><td class="source">    _url = 
options.url || &q
 uot;&quot;;</td></tr><tr class="miss"><td class="line">192</td><td 
class="hits">0</td><td class="source">    _type = options.type || 
&quot;GET&quot;;</td></tr><tr class="miss"><td class="line">193</td><td 
class="hits">0</td><td class="source">    _data = options.data || 
&quot;&quot;;</td></tr><tr><td class="line">194</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="miss"><td class="line">195</td><td 
class="hits">0</td><td class="source">    _request.onreadystatechange = 
function () {</td></tr><tr class="miss"><td class="line">196</td><td 
class="hits">0</td><td class="source">      if (_request.readyState === 4 
&amp;&amp; _request.status === 200) {</td></tr><tr class="miss"><td 
class="line">197</td><td class="hits">0</td><td class="source">        cb(null, 
JSON.parse(_request.responseText));</td></tr><tr class="miss"><td 
class="line">198</td><td class="hits">0</td><td class="source">      } else if 
(_request.readyState === 4) {</td></tr><tr class="miss"><td class="l
 ine">199</td><td class="hits">0</td><td class="source">        cb({status: 
_request.status}, null);</td></tr><tr><td class="line">200</td><td 
class="hits"></td><td class="source">      }</td></tr><tr><td 
class="line">201</td><td class="hits"></td><td class="source">    
};</td></tr><tr><td class="line">202</td><td class="hits"></td><td 
class="source"> </td></tr><tr class="miss"><td class="line">203</td><td 
class="hits">0</td><td class="source">    _request.open(_type, _url, 
true);</td></tr><tr class="miss"><td class="line">204</td><td 
class="hits">0</td><td class="source">    _request.send(JSON.stringify(_data || 
&quot;&quot;));</td></tr><tr><td class="line">205</td><td class="hits"></td><td 
class="source">  };</td></tr><tr><td class="line">206</td><td 
class="hits"></td><td class="source"> </td></tr><tr class="hit"><td 
class="line">207</td><td class="hits">1</td><td class="source">  return 
Mpin;</td></tr><tr><td class="line">208</td><td class="hits"></td><td 
class="source">})();</td>
 </tr><tr><td class="line">209</td><td class="hits"></td><td class="source"> 
</td></tr><tr><td class="line">210</td><td class="hits"></td><td 
class="source"> </td></tr><tr><td class="line">211</td><td 
class="hits"></td><td class="source">//module.exports = 
mpinjs;</td></tr><tr><td class="line">212</td><td class="hits"></td><td 
class="source">//http://www.matteoagosti.com/blog/2013/02/24/writing-javascript-modules-for-both-browser-and-node/</td></tr><tr
 class="hit"><td class="line">213</td><td class="hits">1</td><td 
class="source">if (typeof module !== 'undefined' &amp;&amp; typeof 
module.exports !== 'undefined')</td></tr><tr class="hit"><td 
class="line">214</td><td class="hits">1</td><td class="source">  module.exports 
= mpinjs;</td></tr><tr><td class="line">215</td><td class="hits"></td><td 
class="source">else</td></tr><tr class="miss"><td class="line">216</td><td 
class="hits">0</td><td class="source">  window.mpinjs = 
mpinjs;</td></tr></tbody></table></div></div></div></body></html
 >
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/blob/f27fba99/test/index.js
----------------------------------------------------------------------
diff --git a/test/index.js b/test/index.js
index 55ea634..818368f 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,11 +1,18 @@
 //if browser
 if (typeof require !== 'undefined') {
   var expect = require('chai').expect;
-  var mpinjs = require('../index');
   var sinon = require('sinon');
   var sinonChai = require('sinon-chai');
+  var mpinjs = require('../index');
+//  var mpinjs = require('../dist/mpinjs');
 }
 
+var Errors = [];
+Errors[0] = "MISSING_USERID";
+Errors[1] = "INVALID_USERID";
+Errors[2] = "MISSING_PARAMETERS";
+Errors[3] = "IDENTITY_NOT_VERIFIED";
+
 describe('# Constructor initialization without settingsUrl.', function () {
   it('should throw Error', function () {
     var mpin = new mpinjs();
@@ -83,9 +90,9 @@ describe('# makeNewUser checks.', function () {
     mpin.restore();
   });
 
-  it('should makeNewUser return error when call without userId', function () {
+  it('should makeNewUser return error type ' + Errors[0] + ' when call without 
userId', function () {
     var user = mpin.makeNewUser();
-    expect(user).to.deep.equal({error: 1});
+    expect(user).to.deep.equal({code: 0, type: Errors[0]});
   });
 
   it('should store user into internal list', function () {
@@ -108,22 +115,27 @@ describe('# startRegistration.', function () {
     };
   });
 
-  it('should return error 1, call without userId', function (done) {
+  after(function () {
+    mpin.restore();
+  });
+
+
+  it('should return error type ' + Errors[0] + ', call without userId', 
function (done) {
     mpin.startRegistration(null, function (err, data) {
-      expect(err).to.deep.equal({error: 1});
+      expect(err).to.deep.equal({code: 0, type: Errors[0]});
       done();
     });
   });
 
-  it('should return error 2 if skip makeNewUser method.', function (done) {
+  it('should return error type ' + Errors[1] + ' if skip makeNewUser method.', 
function (done) {
     var userId = "[email protected]";
     mpin.startRegistration(userId, function (err, data) {
-      expect(err).to.deep.equal({error: 2});
+      expect(err).to.deep.equal({code: 1, type: Errors[1]});
       done();
     });
   });
 
-  it('should return error 3 if skip init method.', function (done) {
+  it('should return error type ' + Errors[2] + ' if skip init method.', 
function (done) {
     var userId = "[email protected]";
     mpin.makeNewUser(userId);
     mpin.startRegistration(userId, function (err, data) {
@@ -134,7 +146,7 @@ describe('# startRegistration.', function () {
 
   it('should return OK.', function (done) {
     var userId = "[email protected]";
-    
+
     //mock for init method
     sinon.stub(mpin, 'request').yields(null, this.fakeRes);
 
@@ -150,4 +162,124 @@ describe('# startRegistration.', function () {
 
 
 
+});
+
+
+describe('# confirmRegistration.', function () {
+  var mpin, settingsUrl = "http://192.168.10.63:8005/rps/clientSettings";;
+
+  before(function () {
+    mpin = new mpinjs({settingsUrl: settingsUrl});
+
+    this.fakeRes = {
+      requestOTP: false,
+      mpinAuthServerURL: "http://192.168.10.63:8011/rps";,
+      registerURL: "http://192.168.10.63:8011/rps/user";,
+      signatureURL: "http://192.168.10.63:8011/rps/signature";,
+      certivoxURL: "https://community-api.certivox.net/v3/";
+    };
+
+    this.fakeCs1 = {
+      clientSecretShare: 
"0421e379eb45e56ce699f0a7a83b683e84944b63fcc93a2834a4769ea40a28dc3f2064cd9d64846304999e00008b0838e246d3ea06d0013f1080c1027d54630ca9",
+      params: 
"mobile=0&expires=2015-12-03T12%3A47%3A23Z&app_id=e340a9f240e011e5b23b06df5546c0ed&hash_mpin_id=07a9af5af89d66b969be31d3d4e29c2a0a5ad4d3e30432eed9b3915dbf52230a&signature=33e8e987b07a2d9c9f3d98f68268870ef104cd0e0b9e02ba2c55e8bbf5190913&hash_user_id="
+    };
+    this.fakeCs2 = {
+      clientSecret: 
"0409ba1a247561ab16c35df3ad0ca9846db9968fa28757005335dc2ca35188b4f51521ac97d45bbdb3a8d1c0fdfe79ab29031054534df8b7cbac12e67e4e99d685"
+    };
+  });
+
+  afterEach(function () {
+    mpin.restore();
+    mpin.request.restore && mpin.request.restore();
+  });
+
+
+  it('should return error type ' + Errors[0] + ' call without userId', 
function (done) {
+    mpin.confirmRegistration(null, function (err, data) {
+      expect(err).to.deep.equal({code: 0, type: Errors[0]});
+      done();
+    });
+  });
+
+  it('should return error type ' + Errors[1] + '  if skip makeNewUser 
method.', function (done) {
+    var userId = "[email protected]";
+    mpin.confirmRegistration(userId, function (err, data) {
+      expect(err).to.have.deep.property('type', Errors[1]);
+      done();
+    });
+  });
+
+  it('should return error type ' + Errors[2] + ' if skip init method.', 
function (done) {
+    var userId = "[email protected]";
+    mpin.makeNewUser(userId);
+    mpin.confirmRegistration(userId, function (err, data) {
+      expect(err).to.have.deep.property('type', Errors[2]);
+      done();
+    });
+  });
+  /*
+   //start REGISTRATION >>>
+   it('should return error 4 if skip init method.', function (done) {
+   var userId = "[email protected]";
+   
+   sinon.stub(mpin, 'request').yields(null, this.fakeRes);
+   mpin.init(function (err, data) {
+   mpin.makeNewUser(userId);
+   //      sinon.stub(mpin, 'request').yields({status: 401}, null);
+   mpin.confirmRegistration(userId, function (err, data) {
+   console.log("err", err);
+   expect(err).to.have.deep.property('error', 4);
+   done();
+   });
+   });
+   });
+   */
+
+  it('should return error type ' + Errors[3] + ' identity not verify.', 
function (done) {
+    var userId = "[email protected]", stub;
+
+    stub = sinon.stub(mpin, 'request');
+    stub.onCall(0).yields(null, this.fakeRes);
+    stub.onCall(1).yields(null, {});
+    stub.onCall(2).yields({status: 401}, null);
+
+    mpin.init(function (err, data) {
+      mpin.makeNewUser(userId);
+      mpin.startRegistration(userId, function (err1, data1) {
+        mpin.confirmRegistration(userId, function (err2, data3) {
+          expect(err2).to.have.deep.property('type', Errors[3]);
+          done();
+        });
+      });
+    });
+  });
+
+  //start REGISTRATION >>> OK
+  it('should return OK', function (done) {
+    var userId = "[email protected]", stub;
+
+    stub = sinon.stub(mpin, 'request');
+    stub.onCall(0).yields(null, this.fakeRes);
+    stub.onCall(1).yields(null, {});//mpinId
+    stub.onCall(2).yields(null, this.fakeCs1);//cs1
+    stub.onCall(3).yields(null, this.fakeCs2);//cs2
+
+    mpin.init(function (err, data) {
+      mpin.makeNewUser(userId);
+      mpin.startRegistration(userId, function (err1, data1) {
+        mpin.confirmRegistration(userId, function (err2, data3) {
+          expect(data3).to.exist;
+          done();
+        });
+      });
+    });
+  });
+  
+  
+
+});
+
+
+describe('# confirmRegistration.', function () { 
+  
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-milagro-mfa-js-lib/blob/f27fba99/test/mocha.opts
----------------------------------------------------------------------
diff --git a/test/mocha.opts b/test/mocha.opts
index 7179157..47d63c7 100644
--- a/test/mocha.opts
+++ b/test/mocha.opts
@@ -1,2 +1,2 @@
---recursive
+--recursive
 --timeout 5000
\ No newline at end of file

Reply via email to