var User = require('./user'), SEA = User.SEA, Gun = User.GUN, noop = function(){}; // Well first we have to actually create a user. That is what this function does. User.prototype.create = function(...args){ var pair = typeof args[0] === 'object' && (args[0].pub || args[0].epub) ? args[0] : typeof args[1] === 'object' && (args[1].pub || args[1].epub) ? args[1] : null; var alias = pair && (pair.pub || pair.epub) ? pair.pub : typeof args[0] === 'string' ? args[0] : null; var pass = pair && (pair.pub || pair.epub) ? pair : alias && typeof args[1] === 'string' ? args[1] : null; var cb = args.filter(arg => typeof arg === 'function')[0] || null; // cb now can stand anywhere, after alias/pass or pair var opt = args && args.length > 1 && typeof args[args.length-1] === 'object' ? args[args.length-1] : {}; // opt is always the last parameter which typeof === 'object' and stands after cb var gun = this, cat = (gun._), root = gun.back(-1); cb = cb || noop; opt = opt || {}; if(false !== opt.check){ var err; if(!alias){ err = "No user." } if((pass||'').length < 8){ err = "Password too short!" } if(err){ cb({err: Gun.log(err)}); return gun; } } if(cat.ing){ (cb || noop)({err: Gun.log("User is already being created or authenticated!"), wait: true}); return gun; } cat.ing = true; var act = {}, u; act.a = function(pubs){ act.pubs = pubs; if(pubs && !opt.already){ // If we can enforce that a user name is already taken, it might be nice to try, but this is not guaranteed. var ack = {err: Gun.log('User already created!')}; cat.ing = false; (cb || noop)(ack); gun.leave(); return; } act.salt = String.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it. SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks. } act.b = function(proof){ act.proof = proof; pair ? act.c(pair) : SEA.pair(act.c) // generate a brand new key pair or use the existing. } act.c = function(pair){ var tmp act.pair = pair || {}; if(tmp = cat.root.user){ tmp._.sea = pair; tmp.is = {pub: pair.pub, epub: pair.epub, alias: alias}; } // the user's public key doesn't need to be signed. But everything else needs to be signed with it! // we have now automated it! clean up these extra steps now! act.data = {pub: pair.pub}; act.d(); } act.d = function(){ act.data.alias = alias; act.e(); } act.e = function(){ act.data.epub = act.pair.epub; SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f, {raw:1}); // to keep the private key safe, we AES encrypt it with the proof of work! } act.f = function(auth){ act.data.auth = JSON.stringify({ek: auth, s: act.salt}); act.g(act.data.auth); } act.g = function(auth){ var tmp; act.data.auth = act.data.auth || auth; root.get(tmp = '~'+act.pair.pub).put(act.data).on(act.h); // awesome, now we can actually save the user with their public key as their ID. var link = {}; link[tmp] = {'#': tmp}; root.get('~@'+alias).put(link).get(tmp).on(act.i); // next up, we want to associate the alias with the public key. So we add it to the alias list. } act.h = function(data, key, msg, eve){ eve.off(); act.h.ok = 1; act.i(); } act.i = function(data, key, msg, eve){ if(eve){ act.i.ok = 1; eve.off() } if(!act.h.ok || !act.i.ok){ return } cat.ing = false; cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack) if(noop === cb){ pair ? gun.auth(pair) : gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up. } root.get('~@'+alias).once(act.a); return gun; } User.prototype.leave = function(opt, cb){ var gun = this, user = (gun.back(-1)._).user; if(user){ delete user.is; delete user._.is; delete user._.sea; } if(SEA.window){ try{var sS = {}; sS = window.sessionStorage; delete sS.recall; delete sS.pair; }catch(e){}; } return gun; }