| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| var mpinjs = (function () { |
| var Mpin, Users = {}, Errors = {}, States = {}; |
| |
| Errors.missingUserId = {code: 0, type: "MISSING_USERID"}; |
| Errors.invalidUserId = {code: 1, type: "INVALID_USERID"}; |
| Errors.missingParams = {code: 2, type: "MISSING_PARAMETERS"}; |
| Errors.identityNotVerified = {code: 3, type: "IDENTITY_NOT_VERIFIED"}; |
| Errors.identityMissing = {code: 4, type: "IDENTITY_MISSING"}; |
| Errors.wrongPin = {code: 5, type: "WRONG_PIN"}; |
| Errors.wrongFlow = {code: 6, type: "WRONG_FLOW"}; |
| Errors.userRevoked = {code: 7, type: "USER_REVOKED"}; |
| Errors.timeoutFinish = {code: 8, type: "TIMEOUT_FINISH"}; |
| Errors.requestExpired = {code: 9, type: "REQUEST_EXPIRED"}; |
| Errors.identityNotAuthorized = {code: 10, type: "IDENTITY_NOT_AUTHORIZED"}; |
| Errors.incorrectAccessNumber = {code: 11, type: "INCORRECT_ACCESS_NUMBER"}; |
| |
| States.invalid = "INVALID"; |
| States.start = "STARTED"; |
| States.active = "ACTIVATED"; |
| States.register = "REGISTERED"; |
| States.block = "BLOCKED"; |
| |
| Mpin = function (options) { |
| if (!options || !options.server) { |
| return new Error("Missing server URL"); |
| } |
| |
| this.opts = options; |
| this.settings = {}; |
| |
| }; |
| |
| Mpin.prototype.storageKey = "mpinjs"; |
| |
| //supportedProtocols |
| // temporary until extend with other portocols |
| // then supported should be object of objects |
| Mpin.prototype.cfg = { |
| protocols: { |
| supported: ["2pass"], |
| default: "2pass" |
| } |
| }; |
| |
| Mpin.prototype.init = function (cb) { |
| var self = this, _initUrl; |
| |
| this.recover(); |
| if (this.opts.server.slice(-1) === "/") { |
| _initUrl = this.opts.server; |
| } else { |
| _initUrl = this.opts.server + "/"; |
| } |
| _initUrl += this.opts.rpsPrefix || "rps"; |
| _initUrl += "/clientSettings"; |
| |
| this.request({url: _initUrl}, function (err, data) { |
| if (err && cb) { |
| return cb(err, null); |
| } |
| |
| self.ready = true; |
| self.settings = data; |
| |
| self.chooseAuthProtocol(); |
| cb && cb(null, true); |
| }); |
| }; |
| |
| Mpin.prototype.makeNewUser = function (userId, deviceId) { |
| if (!userId) { |
| return Errors.missingUserId; |
| } |
| |
| this.addToUser(userId, {userId: userId, deviceId: deviceId, state: States.invalid}); |
| |
| return this; |
| }; |
| |
| Mpin.prototype.startRegistration = function (userId, cb) { |
| var _reqData = {}, self = this, _userState; |
| if (!userId) { |
| return cb ? cb(Errors.missingUserId, null) : {error: 1}; |
| } else if (!this.checkUser(userId)) { |
| return cb(Errors.invalidUserId, null); |
| } else if (!this.settings.registerURL) { |
| return cb({code: Errors.missingParams.code, type: Errors.missingParams.type, message: "Missing registerURL"}, null); |
| } |
| |
| //invalid |
| _userState = this.getUser(userId, "state"); |
| if (_userState !== States.invalid) { |
| return cb(Errors.wrongFlow, null); |
| } |
| |
| _reqData.url = this.generateUrl("register"); |
| _reqData.type = "PUT"; |
| _reqData.data = { |
| userId: userId, |
| mobile: 0 |
| }; |
| |
| if (Users[userId].deviceId) { |
| _reqData.data.deviceName = Users[userId].deviceId; |
| } |
| |
| this.request(_reqData, function (err, data) { |
| if (err) { |
| return cb(err, null); |
| } |
| |
| self.addToUser(userId, {regOTT: data.regOTT, mpinId: data.mpinId, state: States.start}); |
| |
| //force activate |
| if (data.active) { |
| self.addToUser(userId, {state: States.active}); |
| } |
| |
| cb && cb(null, true); |
| }); |
| }; |
| |
| //request cs1 + cs2 |
| Mpin.prototype.confirmRegistration = function (userId, cb) { |
| var _cs1Url = "", self = this, _userState; |
| if (!userId) { |
| return cb ? cb(Errors.missingUserId, null) : Errors.missingUserId; |
| } else if (!this.checkUser(userId)) { |
| return cb(Errors.invalidUserId, null); |
| } else if (!this.settings.signatureURL) { |
| return cb({code: Errors.missingParams.code, type: Errors.missingParams.type, message: "Missing signatureURL option."}, null); |
| } |
| |
| //started || activated |
| _userState = this.getUser(userId, "state"); |
| if (_userState !== States.start && _userState !== States.active) { |
| return cb(Errors.wrongFlow, null); |
| } |
| |
| //already set. |
| if (Users[userId].csHex) { |
| return cb(null, true); |
| } |
| |
| _cs1Url = this.generateUrl('signature', {userId: userId}); |
| //req cs1 |
| this.request({url: _cs1Url}, function (err, cs1Data) { |
| var _cs2Url = ""; |
| if (err) { |
| if (err.status == 401) { |
| return cb(Errors.identityNotVerified, null); |
| } else if (err.status == 400) { |
| return cb(Errors.wrongFlow, null); |
| } |
| } |
| |
| _cs2Url = self.settings.certivoxURL + "clientSecret?" + cs1Data.params; |
| |
| //req cs2 |
| self.request({url: _cs2Url}, function (err, cs2Data) { |
| var csHex; |
| |
| csHex = MPINAuth.addShares(cs2Data.clientSecret, cs1Data.clientSecretShare); |
| |
| self.addToUser(userId, {csHex: csHex, state: States.active}); |
| |
| cb(null, true); |
| }); |
| }); |
| }; |
| |
| Mpin.prototype.finishRegistration = function (userId, pin) { |
| var _user, token; |
| |
| if (!userId) { |
| return Errors.missingUserId; |
| } |
| |
| _user = this.getUser(userId); |
| |
| if (_user.state !== States.active || !Users[userId].csHex) { |
| return Errors.wrongFlow; |
| } |
| |
| if (isNaN(pin)) { |
| pin = this.toHash(pin); |
| } |
| |
| token = MPINAuth.calculateMPinToken(Users[userId].mpinId, pin, Users[userId].csHex); |
| delete Users[userId].csHex; |
| |
| this.addToUser(userId, {token: token, state: States.register}); |
| |
| return true; |
| }; |
| |
| //Put user / mpinId |
| Mpin.prototype.restartRegistration = function (userId, cb) { |
| var _reqData = {}, self = this, _userState; |
| |
| if (!userId) { |
| return cb ? cb(Errors.missingUserId, null) : {error: 1}; |
| } else if (!this.checkUser(userId)) { |
| return cb(Errors.invalidUserId, null); |
| } else if (!this.settings.registerURL) { |
| return cb({code: Errors.missingParams.code, type: Errors.missingParams.type, message: "Missing registerURL"}, null); |
| } |
| |
| _userState = this.getUser(userId, "state"); |
| if (_userState !== States.start) { |
| return cb(Errors.wrongFlow, null); |
| } |
| |
| _reqData.url = this.generateUrl("restart", {userId: userId}); |
| _reqData.type = "PUT"; |
| _reqData.data = { |
| userId: userId, |
| mobile: 0, |
| regOTT: Users[userId].regOTT |
| }; |
| |
| this.request(_reqData, function (err, data) { |
| if (err) { |
| return cb(err, null); |
| } |
| |
| self.addToUser(userId, {regOTT: data.regOTT, mpinId: data.mpinId}); |
| |
| //force activate |
| if (data.active) { |
| self.addToUser(userId, {state: States.active}); |
| } |
| |
| cb && cb(null, true); |
| }); |
| }; |
| |
| Mpin.prototype.startAuthentication = function (userId, cb) { |
| var _tp1Url, self = this, _userState; |
| |
| if (!userId) { |
| return cb ? cb(Errors.missingUserId, null) : Errors.missingUserId; |
| } else if (!this.checkUser(userId)) { |
| return cb(Errors.invalidUserId, null); |
| } else if (!this.settings.timePermitsURL || !this.settings.certivoxURL) { |
| return cb({code: Errors.missingParams.code, type: Errors.missingParams.type, message: "Missing timePermitsURL or/and certivoxURL option."}, null); |
| } |
| |
| //registered |
| _userState = this.getUser(userId, "state"); |
| if (_userState !== States.register) { |
| return cb(Errors.wrongFlow, null); |
| } |
| |
| //checkUser |
| _tp1Url = this.generateUrl('permit1', {userId: userId}); |
| this.request({url: _tp1Url}, function (err, data) { |
| if (err) { |
| if (err.status === 401 || err.status === 403 || err.status === 410) { |
| return cb(Errors.userRevoked, null); |
| } |
| |
| return cb(err, null); |
| } |
| var _signature, _tp2Url, _timePermit1, _storageUrl; |
| _signature = data["signature"]; |
| _timePermit1 = data["timePermit"]; |
| |
| self.addToUser(userId, {currentDate: data['date']}); |
| |
| //check cache if exist |
| if (Users[userId].timePermitCache && Users[userId].timePermitCache.date === data.date) { |
| var _timePermit2 = Users[userId].timePermitCache.timePermit; |
| var timePermitHex = MPINAuth.addShares(_timePermit1, _timePermit2); |
| |
| self.addToUser(userId, {timePermitHex: timePermitHex}); |
| cb && cb(null, true); //exit with cache permit2 |
| return; |
| } else { |
| _storageUrl = self.generateUrl("storage", {date: data.date, storageId: data.storageId}); |
| |
| self.request({url: _storageUrl}, function (storErr, storData) { |
| if (storErr) { |
| _tp2Url = self.generateUrl('permit2', {userId: userId}); |
| _tp2Url += "&signature=" + _signature; |
| |
| self._getTimePermit2({userId: userId, permit1: _timePermit1, permit2Url: _tp2Url, date: data.date}, cb); //continue |
| |
| return; |
| } |
| |
| var _timePermit2 = storData; |
| var timePermitHex = MPINAuth.addShares(_timePermit1, _timePermit2); |
| |
| self.addToUser(userId, {timePermitHex: timePermitHex, timePermitCache: {date: data.date, timePermit: _timePermit2}}); |
| |
| cb && cb(null, true); //exit with storage permit2 |
| }, false); |
| } |
| }); |
| }; |
| |
| Mpin.prototype._getTimePermit2 = function (options, cb) { |
| var self = this, _timePermit1 = options.permit1; |
| |
| this.request({url: options.permit2Url}, function (err2, data2) { |
| if (err2) { |
| if (err2.status === 401 || err2.status === 403 || err2.status === 410) { |
| return cb(Errors.userRevoked, null); |
| } |
| |
| return cb(err2, null); |
| } |
| |
| var _timePermit2, timePermitHex, _permitCache = {}; |
| _timePermit2 = data2["timePermit"]; |
| timePermitHex = MPINAuth.addShares(_timePermit1, _timePermit2); |
| |
| _permitCache.date = options.date; |
| _permitCache.timePermit = data2["timePermit"]; |
| |
| self.addToUser(options.userId, {timePermitHex: timePermitHex, timePermitCache: _permitCache}); |
| |
| cb && cb(null, true); |
| }); |
| }; |
| |
| Mpin.prototype.chooseAuthProtocol = function () { |
| var self = this; |
| this.authProtocol = this.cfg.protocols.default; |
| |
| // We have 3 arrays: |
| // 1. Ordered list of protocols sent from the server - this.settings.supportedProtocols |
| // 2. List of protocols supported by the library - self.cfg.protocols.supported |
| // 3. List of protocols that the library user would like to support - this.opts.authProtocols |
| // The goal is to select the first protocol from the server's list (1) that is supported by the library (2) and selected by the lib user (3). |
| // If the lib user didn't provide any preferences, then we select the first one from the server's list that is supported by the lib. |
| if (this.settings.supportedProtocols && this.settings.supportedProtocols instanceof Array) { |
| if (this.opts.authProtocols && this.opts.authProtocols instanceof Array) { |
| |
| this.settings.supportedProtocols.some(function (value) { |
| if (self.opts.authProtocols.indexOf(value) !== -1 && self.cfg.protocols.supported.indexOf(value) !== -1) { |
| self.authProtocol = value; |
| return true; |
| } |
| }); |
| } else { |
| this.settings.supportedProtocols.some(function (value) { |
| if (self.cfg.protocols.supported.indexOf(value) !== -1) { |
| self.authProtocol = value; |
| return true; |
| } |
| }); |
| } |
| } |
| }; |
| |
| |
| Mpin.prototype.finishAuthentication = function (userId, pin, cb) { |
| var _userState; |
| |
| //registered |
| _userState = this.getUser(userId, "state"); |
| if (_userState !== States.register) { |
| return cb(Errors.wrongFlow, null); |
| } else if (!Users[userId].timePermitHex) { |
| return cb({code: Errors.wrongFlow.code, type: Errors.wrongFlow.type, message: "Need to call startAuthentication method before this."}, null); |
| } |
| |
| // The following checks that the authentication protocol is 2pass. |
| // This is temporary until the lib supports other protocols |
| if (this.authProtocol === this.cfg.protocols.default) { |
| this._pass2Requests({userId: userId, pin: pin}, cb); |
| } |
| }; |
| |
| Mpin.prototype.finishAuthenticationOtp = function (userId, pin, cb) { |
| var _userState; |
| |
| //registered |
| _userState = this.getUser(userId, "state"); |
| if (_userState !== States.register) { |
| return cb(Errors.wrongFlow, null); |
| } else if (!Users[userId].timePermitHex) { |
| return cb({code: Errors.wrongFlow.code, type: Errors.wrongFlow.type, message: "Need to call startAuthentication method before this."}, null); |
| } |
| |
| // The following checks that the authentication protocol is 2pass. |
| // This is temporary until the lib supports other protocols |
| if (this.authProtocol === this.cfg.protocols.default) { |
| this._pass2Requests({userId: userId, pin: pin, otp: true}, function (err, data) { |
| if (err) { |
| return cb(err, null); |
| } |
| |
| if (!data.expireTime || !data.ttlSeconds || !data.nowTime) { |
| return cb(null, null); |
| } |
| |
| data.expireTime = data.expireTime / 1000; |
| data.nowTime = data.nowTime / 1000; |
| |
| cb(null, data); |
| }); |
| } |
| }; |
| |
| Mpin.prototype.finishAuthenticationAN = function (userId, pin, accessNumber, cb) { |
| var _userState; |
| |
| //registered |
| _userState = this.getUser(userId, "state"); |
| if (_userState !== States.register) { |
| return cb(Errors.wrongFlow, null); |
| } else if (!Users[userId].timePermitHex) { |
| return cb({code: Errors.wrongFlow.code, type: Errors.wrongFlow.type, message: "Need to call startAuthentication method before this."}, null); |
| } |
| |
| // The following checks that the authentication protocol is 2pass. |
| // This is temporary until the lib supports other protocols |
| if (this.authProtocol === this.cfg.protocols.default) { |
| this._pass2Requests({userId: userId, pin: pin, accessNumber: accessNumber.toString()}, function (err, data) { |
| if (err) { |
| return cb(err, null); |
| } |
| |
| if (!data.expireTime || !data.ttlSeconds || !data.nowTime) { |
| return cb(null, null); |
| } |
| |
| data.expireTime = data.expireTime / 1000; |
| data.nowTime = data.nowTime / 1000; |
| |
| cb(null, data); |
| }); |
| } |
| }; |
| |
| Mpin.prototype._pass2Requests = function (opts, cb) { |
| var userId, pin, otp, accessNumber, self = this, _reqData = {}; |
| userId = opts.userId; |
| pin = isNaN(opts.pin) ? this.toHash(opts.pin) : opts.pin; |
| |
| otp = opts.otp || false; |
| accessNumber = opts.accessNumber || false; |
| |
| _reqData.url = this.generateUrl("pass1"); |
| _reqData.type = "POST"; |
| _reqData.data = this.getAuthData(userId, pin); |
| |
| // pass1 |
| this.request(_reqData, function (pass1Err, pass1Data) { |
| var _req2Data = {}, wid = "0"; |
| _req2Data.url = self.generateUrl("pass2"); |
| _req2Data.type = "POST"; |
| |
| accessNumber && (wid = accessNumber); |
| _req2Data.data = MPINAuth.pass2Request(pass1Data.y, otp, wid); |
| |
| _req2Data.data.mpin_id = Users[userId].mpinId; |
| |
| // pass 2 |
| self.request(_req2Data, function (pass2Err, pass2Data) { |
| var otpCode; |
| if (pass2Err) { |
| return cb(pass2Err, null); |
| } |
| |
| otpCode = pass2Data["OTP"] || false; |
| |
| if (pass2Data && pass2Data["OTP"]) { |
| delete pass2Data["OTP"]; |
| } |
| |
| self._authenticate({userId: userId, mpinResponse: pass2Data, otpCode: otpCode, accessNumber: accessNumber}, cb); |
| }); |
| }); |
| |
| }; |
| |
| Mpin.prototype._authenticate = function (opts, cb) { |
| var _authData = {}, self = this; |
| |
| if (opts.accessNumber) { |
| _authData.url = this.generateUrl("mobileauth"); |
| } else { |
| _authData.url = this.generateUrl("auth"); |
| } |
| |
| _authData.type = "POST"; |
| _authData.data = {mpinResponse: opts.mpinResponse}; |
| |
| this.request(_authData, function (authErr, authData) { |
| if (authErr) { |
| if (authErr.status === 401) { |
| return cb(Errors.wrongPin, null); |
| } else if (authErr.status === 403) { |
| return cb(Errors.identityNotAuthorized, null); |
| } else if (authErr.status === 408) { |
| return cb(Errors.requestExpired, null); |
| } else if (authErr.status === 410) { |
| opts.userId && self.addToUser(opts.userId, {state: States.block}); |
| return cb(Errors.wrongPin, null); |
| } else if (authErr.status === 412) { |
| return cb(Errors.incorrectAccessNumber, null); |
| } else { |
| return cb(Errors.wrongPin, null); |
| } |
| } |
| |
| if (opts.otpCode && authData) { |
| authData.otp = opts.otpCode; |
| } |
| |
| cb && cb(null, authData || null); |
| }); |
| }; |
| |
| Mpin.prototype.checkAccessNumber = function (accessNumber) { |
| accessNumber = accessNumber.toString(); |
| if (!this.settings.accessNumberUseCheckSum || accessNumber.length != this.settings.accessNumberDigits) { |
| return true; |
| } else { |
| if (this.settings.cSum === 1) { |
| return this.checkAccessNumberSum2(accessNumber, 6); |
| } else { |
| return this.checkAccessNumberSum(accessNumber); |
| } |
| } |
| }; |
| |
| Mpin.prototype.checkAccessNumberSum = function (accNumber, accLen) { |
| accLen || (accLen = 1); |
| |
| var n = parseInt(accNumber.slice(0, accNumber.length - accLen), 10); |
| var cSum = parseInt(accNumber.slice(accNumber.length - accLen, accNumber.length), 10); |
| |
| var p = 99991; |
| var g = 11; |
| var checkSum = ((n * g) % p) % Math.pow(10, accLen); |
| |
| return (checkSum === cSum); |
| }; |
| |
| Mpin.prototype.checkAccessNumberSum2 = function (accNumber, accLen) { |
| var cSum, checksum, x, w, wid, wid_len, g = 11, sum_d = 0; |
| wid = accNumber.toString(); |
| wid = wid.substring(0, accNumber.toString().length - 1); |
| w = accLen + 1; |
| sum_d = 0; |
| wid_len = wid.length; |
| |
| for (var i = 0; i < wid_len; i++) { |
| x = parseInt(wid[i]); |
| sum_d += (x * w); |
| w -= 1; |
| } |
| checksum = (g - (sum_d % g)) % g; |
| checksum = (checksum === 10) ? 0 : checksum; |
| |
| //get last one digit and compare with checksum result |
| cSum = accNumber.substr(-1); |
| cSum = parseInt(cSum); |
| return (cSum === checksum); |
| }; |
| |
| Mpin.prototype.getAuthData = function (userId, pin) { |
| var _auth = {}; |
| |
| _auth.mpin = Users[userId].mpinId; |
| _auth.token = Users[userId].token; |
| _auth.timePermit = Users[userId].timePermitHex; |
| _auth.date = Users[userId].currentDate; |
| |
| return MPINAuth.pass1Request(_auth.mpin, _auth.token, _auth.timePermit, pin, _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.toHash = function (strData) { |
| var hash = 0; |
| for (var i = 0; i < strData.length; i++) { |
| hash = ((hash << 5) - hash) + strData.charCodeAt(i); |
| } |
| return hash; |
| }; |
| |
| 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); |
| } |
| self.webOTT = data.webOTT; |
| |
| var returnData = { |
| accessNumber: data.accessNumber, |
| ttlSeconds: data.ttlSeconds, |
| localTimeStart: data.localTimeStart / 1000, |
| localTimeEnd: data.localTimeEnd / 1000 |
| }; |
| |
| cb && cb(null, returnData); |
| }); |
| }; |
| |
| Mpin.prototype.getQrUrl = function (userId, cb) { |
| var self = this, _reqData = {}; |
| |
| _reqData.url = this.generateUrl("getqrurl"); |
| _reqData.type = "POST"; |
| _reqData.data = { |
| prerollid: userId || "" |
| }; |
| |
| this.request(_reqData, function (err, data) { |
| if (err) { |
| return cb(err, null); |
| } |
| self.webOTT = data.webOTT; |
| |
| var returnData = { |
| qrUrl: data.qrUrl, |
| ttlSeconds: data.ttlSeconds, |
| localTimeStart: data.localTimeStart / 1000, |
| localTimeEnd: data.localTimeEnd / 1000 |
| }; |
| |
| cb && cb(null, returnData); |
| }); |
| }; |
| |
| Mpin.prototype.waitForMobileAuth = function (timeoutSeconds, requestSeconds, cb, cbStatus) { |
| var self = this, _reqData = {}; |
| if (!this.webOTT) { |
| return cb({code: Errors.wrongFlow.code, type: Errors.wrongFlow.type, message: "Need to call getAccessNumber method before this."}, null); |
| } else if (!timeoutSeconds) { |
| return cb({code: Errors.missingParams.code, type: Errors.missingParams.type, message: "Missing timeout/expiration period(in seconds)."}, null); |
| } |
| |
| self.mobileStatus = self.mobileStatus || ''; |
| |
| if (typeof this.timeoutPeriod === "undefined") { |
| this.timeoutPeriod = timeoutSeconds * 1000; |
| } |
| |
| _reqData.url = this.generateUrl("getaccess"); |
| _reqData.type = "POST"; |
| _reqData.data = {webOTT: this.webOTT}; |
| |
| this.request(_reqData, function (err, data) { |
| var _requestPeriod; |
| |
| if (err) { |
| cb && cb(err, null); |
| } else { |
| authOTT = data.authOTT |
| delete data.authOTT |
| |
| if (data.status === 'authenticate') { |
| cbStatus && cbStatus(data); |
| self._authenticate({mpinResponse: {authOTT: authOTT}}, cb); |
| } else { |
| if (self.timeoutPeriod > 0) { |
| _requestPeriod = requestSeconds ? requestSeconds * 1000 : 3000; |
| self.timeoutPeriod -= _requestPeriod; |
| if (data.status !== self.mobileStatus) { |
| self.mobileStatus = data.status; |
| cbStatus && cbStatus(data); |
| } |
| self.intervalID2 = setTimeout(function () { |
| self.waitForMobileAuth.call(self, timeoutSeconds, requestSeconds, cb, cbStatus); |
| }, _requestPeriod); |
| return; |
| } else if (self.timeoutPeriod <= 0) { |
| delete self.timeoutPeriod; |
| cb && cb(Errors.timeoutFinish, null); |
| return; |
| } |
| } |
| } |
| }); |
| }; |
| |
| Mpin.prototype.cancelMobileAuth = function () { |
| if (this.intervalID2) { |
| clearInterval(this.intervalID2); |
| } |
| |
| if (this.timeoutPeriod) { |
| delete this.timeoutPeriod; |
| } |
| }; |
| |
| |
| Mpin.prototype.generateUrl = function (type, options) { |
| var url, mpData, mpin_id_bytes, hash_mpin_id_bytes = [], hash_mpin_id_hex; |
| |
| switch (type) { |
| case "register": |
| url = this.settings.registerURL; |
| break; |
| case "restart": |
| url = this.settings.registerURL + "/"; |
| url += Users[options.userId].mpinId; |
| break; |
| case "signature": |
| url = this.settings.signatureURL + "/"; |
| url += Users[options.userId].mpinId; |
| url += "?regOTT=" + Users[options.userId].regOTT; |
| break; |
| case "permit1": |
| url = this.settings.timePermitsURL + "/"; |
| url += Users[options.userId].mpinId; |
| break; |
| case "permit2": |
| mpData = this.fromHex(Users[options.userId].mpinId); |
| 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.settings.certivoxURL + "timePermit"; |
| url += "?app_id=" + this.settings.appID; |
| url += "&mobile=0"; |
| url += "&hash_mpin_id=" + hash_mpin_id_hex; |
| break; |
| case "pass1": |
| url = this.settings.mpinAuthServerURL + "/pass1"; |
| break; |
| case "pass2": |
| url = this.settings.mpinAuthServerURL + "/pass2"; |
| break; |
| case "auth": |
| url = this.settings.authenticateURL; |
| break; |
| case "mobileauth": |
| url = this.settings.mobileAuthenticateURL; |
| break; |
| case "getnumber": |
| url = this.settings.getAccessNumberURL; |
| break; |
| case "getqrurl": |
| url = this.settings.getQrUrl; |
| break; |
| case "getaccess": |
| url = this.settings.accessNumberURL; |
| break; |
| case "storage": |
| url = this.settings.timePermitsStorageURL + "/" + this.settings.appID + "/"; |
| url += options.date + "/" + options.storageId; |
| break; |
| } |
| |
| return url; |
| }; |
| |
| Mpin.prototype.listUsers = function () { |
| var listUsers = []; |
| for (var uKey in Users) { |
| listUsers.push({ |
| userId: Users[uKey].userId, |
| deviceId: Users[uKey].deviceId || "", |
| state: Users[uKey].state || "" |
| }); |
| } |
| return listUsers; |
| }; |
| |
| Mpin.prototype.checkUser = function (userId) { |
| return (Users[userId]) ? true : false; |
| }; |
| |
| Mpin.prototype.getUser = function (userId, property) { |
| var _user = {}; |
| if (!userId) { |
| return Errors.missingUserId; |
| } else if (!this.checkUser(userId)) { |
| return Errors.invalidUserId; |
| } |
| |
| _user = { |
| userId: Users[userId].userId, |
| deviceId: Users[userId].deviceId || "", |
| state: Users[userId].state |
| }; |
| |
| if (!property) { |
| return _user; |
| } else if (property && _user[property]) { |
| return _user[property]; |
| } |
| }; |
| |
| |
| Mpin.prototype.deleteUser = function (userId) { |
| var mpinData = this.getData(), delMpinId; |
| |
| if (!userId) { |
| return Errors.missingUserId; |
| } else if (!this.checkUser(userId)) { |
| return Errors.invalidUserId; |
| } |
| |
| delMpinId = Users[userId].mpinId; |
| |
| //memory |
| delete Users[userId]; |
| |
| //store |
| delete mpinData.accounts[delMpinId]; |
| |
| this.storeData(mpinData); |
| }; |
| |
| Mpin.prototype.addToUser = function (userId, userProps, skipSave) { |
| if (!this.checkUser(userId)) { |
| //create |
| Users[userId] = {}; |
| } |
| |
| //If mpinId has changed, we need to delete the object withthe previous one |
| if (Users[userId].mpinId && userProps.mpinId && Users[userId].mpinId != userProps.mpinId) { |
| this.deleteData(userId); |
| } |
| |
| for (var uKey in userProps) { |
| if (userProps[uKey]) { |
| Users[userId][uKey] = userProps[uKey]; |
| } |
| } |
| |
| var _save = !skipSave; |
| _save && this.setData(userId, userProps); |
| }; |
| |
| Mpin.prototype.restore = function () { |
| Users = {}; |
| }; |
| |
| Mpin.prototype.deleteData = function (userId) { |
| var mpinData = this.getData(); |
| |
| var mpinId = Users[userId].mpinId; |
| if (!mpinData || !mpinData.accounts[mpinId]) { |
| return; |
| } |
| |
| delete mpinData.accounts[mpinId]; |
| |
| this.storeData(mpinData); |
| }; |
| |
| Mpin.prototype.setData = function (userId, upData) { |
| var mpinData = this.getData(); |
| |
| if (!mpinData) { |
| mpinData = { |
| version: "4", |
| accounts: {} |
| }; |
| } |
| |
| var mpinId = upData.mpinId || Users[userId].mpinId; |
| |
| if (!mpinId) { |
| return false; |
| } |
| |
| //update Default Identity |
| if (!mpinData.accounts[mpinId]) { |
| mpinData.accounts[mpinId] = {}; |
| } |
| |
| if (upData.regOTT) { |
| mpinData.accounts[mpinId].regOTT = upData.regOTT; |
| } |
| |
| if (upData.timePermitHex) { |
| mpinData.accounts[mpinId].MPinPermit = upData.timePermitHex; |
| } |
| |
| if (upData.token) { |
| mpinData.accounts[mpinId].token = upData.token; |
| } |
| |
| if (upData.state && Users[userId].mpinId) { |
| mpinData.accounts[mpinId].state = upData.state; |
| } |
| |
| //cache cache |
| if (upData.timePermitCache) { |
| mpinData.accounts[mpinId].timePermitCache = upData.timePermitCache; |
| } |
| |
| this.storeData(mpinData); |
| }; |
| |
| Mpin.prototype.storeData = function (mpinData, key) { |
| var storageKey = key || this.storageKey; |
| localStorage.setItem(storageKey, JSON.stringify(mpinData)); |
| }; |
| |
| Mpin.prototype.recover = function () { |
| var userId, userData = {}, mpinData = this.getData(), isOldData = false; |
| |
| if (!mpinData) { |
| mpinData = this.getData("mpin"); |
| isOldData = true; |
| } |
| |
| if (mpinData && "accounts" in mpinData) { |
| for (var mpinId in mpinData.accounts) { |
| userId = (JSON.parse(this.fromHex(mpinId))).userID; |
| |
| userData = {}; |
| userData.userId = userId; |
| userData.mpinId = mpinId; |
| |
| mpinData.accounts[mpinId].regOTT && (userData.regOTT = mpinData.accounts[mpinId].regOTT); |
| mpinData.accounts[mpinId].token && (userData.token = mpinData.accounts[mpinId].token); |
| mpinData.accounts[mpinId].MPinPermit && (userData.MPinPermit = mpinData.accounts[mpinId].MPinPermit); |
| mpinData.accounts[mpinId].timePermitCache && (userData.timePermitCache = mpinData.accounts[mpinId].timePermitCache); |
| |
| if (isOldData || !mpinData.accounts[mpinId].state) { |
| if (mpinData.accounts[mpinId].token) { |
| userData.state = States.register; |
| } else if (mpinData.accounts[mpinId].regOTT) { |
| userData.state = States.start; |
| } else { |
| userData.state = States.invalid; |
| } |
| } else { |
| userData.state = mpinData.accounts[mpinId].state; |
| } |
| |
| //call add To user & skip Save |
| this.addToUser(userId, userData, !isOldData); |
| } |
| } |
| |
| if (isOldData && mpinData && "accounts" in mpinData) { |
| delete mpinData.accounts; |
| this.storeData(mpinData, "mpin"); |
| } |
| }; |
| |
| Mpin.prototype.getData = function (getKey) { |
| var localKey, mpinData; |
| localKey = getKey || this.storageKey; |
| mpinData = localStorage.getItem(localKey); |
| mpinData = JSON.parse(mpinData); |
| return mpinData; |
| }; |
| |
| //{url: url, type: "get post put", data: data} |
| Mpin.prototype.request = function (options, cb, jsonResponse) { |
| var _request = new XMLHttpRequest(), _url, _type, _parseJson; |
| _url = options.url || ""; |
| _type = options.type || "GET"; |
| |
| _parseJson = (typeof jsonResponse !== "undefined") ? jsonResponse : true; |
| |
| _request.onreadystatechange = function () { |
| if (_request.readyState === 4 && _request.status === 200) { |
| if (_parseJson && _request.responseText) { |
| cb(null, JSON.parse(_request.responseText)); |
| } else { |
| cb(null, _request.responseText); |
| } |
| } else if (_request.readyState === 4) { |
| cb({status: _request.status}, null); |
| } |
| }; |
| |
| _request.open(_type, _url, true); |
| if (options.data) { |
| _request.setRequestHeader("Content-Type", "application/json"); |
| _request.send(JSON.stringify(options.data)); |
| } else { |
| _request.send(); |
| } |
| }; |
| |
| return Mpin; |
| })(); |
| |
| |
| //module.exports = mpinjs; |
| //http://www.matteoagosti.com/blog/2013/02/24/writing-javascript-modules-for-both-browser-and-node/ |
| if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') |
| module.exports = mpinjs; |
| else |
| window.mpinjs = mpinjs; |