UNPKG

@sexycoders/libauth.js

Version:

A full service for asymetric passwordless authentication.

650 lines (572 loc) 22.6 kB
/*jslint browser: true, sloppy: true */ //adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08#appendix-C function base64urlEncode(arg) { var s = window.btoa(arg); // Regular base64 encoder s = s.split('=')[0]; // Remove any trailing '='s s = s.replace(/\+/g, '-'); // 62nd char of encoding s = s.replace(/\//g, '_'); // 63rd char of encoding return s; } function base64urlDecode(s) { s = s.replace(/-/g, '+'); // 62nd char of encoding s = s.replace(/_/g, '/'); // 63rd char of encoding switch (s.length % 4) { // Pad with trailing '='s case 0: // No pad chars in this case break; case 2: // Two pad chars s += "=="; break; case 3: // One pad char s += "="; break; default: throw "Illegal base64url string!"; } return window.atob(s); // Standard base64 decoder } /*jslint browser: true, devel: true, sloppy: true, vars: true*/ /*globals Uint8Array, Promise */ var extractable = true; var encodePrivateKey, encodePublicKey; function wrap(text, len) { var length = len || 72, i, result = ""; for (i = 0; i < text.length; i += length) { result += text.slice(i, i + length) + "\n"; } return result; } function rsaPrivateKey(key) { return window.btoa("-----BEGIN RSA PRIVATE KEY-----\n" + key + "-----END RSA PRIVATE KEY-----"); } function arrayBufferToBase64(buffer) { var binary = '', i; var bytes = new Uint8Array(buffer); var len = bytes.byteLength; for (i = 0; i < len; i += 1) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); } function generateKeyPair(alg, size, name) { return window.crypto.subtle.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 4096, //can be 1024, 2048, or 4096 publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-256" } //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" }, extractable, ["sign", "verify"] ).then(function (key) { var privateKey = window.crypto.subtle.exportKey( "jwk", key.privateKey ).then(encodePrivateKey).then(wrap).then(rsaPrivateKey); var publicKey = window.crypto.subtle.exportKey( "jwk", key.publicKey ).then(function (jwk) { return encodePublicKey(jwk, name); }); return Promise.all([privateKey, publicKey]); }); } /*jslint browser: true, devel: true, bitwise: true, sloppy: true, vars: true*/ var base64urlDecode; function arrayToString(a) { return String.fromCharCode.apply(null, a); } function stringToArray(s) { return s.split('').map(function (c) { return c.charCodeAt(); }); } function base64urlToArray(s) { return stringToArray(base64urlDecode(s)); } function pemToArray(pem) { return stringToArray(window.atob(pem)); } function arrayToPem(a) { return window.btoa(a.map(function (c) { return String.fromCharCode(c); }).join('')); } function arrayToLen(a) { var result = 0, i; for (i = 0; i < a.length; i += 1) { result = result * 256 + a[i]; } return result; } function integerToOctet(n) { var result = []; for (true; n > 0; n = n >> 8) { result.push(n & 0xFF); } return result.reverse(); } function lenToArray(n) { var oct = integerToOctet(n), i; for (i = oct.length; i < 4; i += 1) { oct.unshift(0); } return oct; } function decodePublicKey(s) { var split = s.split(" "); var prefix = split[0]; if (prefix !== "ssh-rsa") { throw ("Unknown prefix:" + prefix); } var buffer = pemToArray(split[1]); var nameLen = arrayToLen(buffer.splice(0, 4)); var type = arrayToString(buffer.splice(0, nameLen)); if (type !== "ssh-rsa") { throw ("Unknown key type:" + type); } var exponentLen = arrayToLen(buffer.splice(0, 4)); var exponent = buffer.splice(0, exponentLen); var keyLen = arrayToLen(buffer.splice(0, 4)); var key = buffer.splice(0, keyLen); return {type: type, exponent: exponent, key: key, name: split[2]}; } function checkHighestBit(v) { if (v[0] >> 7 === 1) { // add leading zero if first bit is set v.unshift(0); } return v; } function jwkToInternal(jwk) { return { type: "ssh-rsa", exponent: checkHighestBit(stringToArray(base64urlDecode(jwk.e))), name: "name", key: checkHighestBit(stringToArray(base64urlDecode(jwk.n))) }; } function encodePublicKey(jwk, name) { var k = jwkToInternal(jwk); k.name = name; var keyLenA = lenToArray(k.key.length); var exponentLenA = lenToArray(k.exponent.length); var typeLenA = lenToArray(k.type.length); var array = [].concat(typeLenA, stringToArray(k.type), exponentLenA, k.exponent, keyLenA, k.key); var encoding = arrayToPem(array); return k.type + " " + encoding + " " + k.name; } function asnEncodeLen(n) { var result = []; if (n >> 7) { result = integerToOctet(n); result.unshift(0x80 + result.length); } else { result.push(n); } return result; } function encodePrivateKey(jwk) { var order = ["n", "e", "d", "p", "q", "dp", "dq", "qi"]; var list = order.map(function (prop) { var v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop]))); var len = asnEncodeLen(v.length); return [0x02].concat(len, v); // int tag is 0x02 }); var seq = [0x02, 0x01, 0x00]; // extra seq for SSH seq = seq.concat.apply(seq, list); var len = asnEncodeLen(seq.length); var a = [0x30].concat(len, seq); // seq is 0x30 return arrayToPem(a); } function ssh_create(e,async_flag) { var ssh=new Object(); ssh.alg="RSASSA-PKCS1-v1_5"; ssh.size="4096"; ssh.name="username"; var keys=generateKeyPair(ssh.alg, ssh.size, ssh.name); //returns array promise first cell is private and second is public keys.then(function(keys){ var privateKey=atob(keys[0]); var publicKey=keys[1]; //localStorage.setItem("SSH_Public",publicKey); window.__auth_system._rsa=publicKey; window.__auth_system.pRSA=privateKey; //hashing password with md5 for IV var md = forge.md.md5.create(); md.update(e.password); var cipher = forge.cipher.createCipher('AES-CBC',md.digest().toHex()); delete md; var md = forge.md.sha256.create(); md.update(e.password); cipher.start({iv: md.digest().toHex().substring(0,16)}); cipher.update(forge.util.createBuffer(privateKey)); cipher.finish(); var encrypted = cipher.output; delete md; window.__auth_system._enc_prsa=btoa(encrypted.data); //console.log(window.__auth_system._enc_prsa); if(async_flag) { var t=new Event("SSH_CREATE_SIG"); window.dispatchEvent(t); } }); }; function retrieve_key(data) { var json_send=new Object(); json_send.command="request_private"; json_send.user=new Object(); json_send.user.id=data.email; var send=btoa(JSON.stringify(json_send)); $.ajax({ type: 'POST', headers: {"Access-Control-Allow-Origin":"http://auth-serve.localhost"}, url: window.__auth_system.auth_server, data: send, success: function(response) { //console.log("server response: "+atob(response)); var res=JSON.parse(atob(response)); window.__auth_system.set_enc_prsa(res.pRSA); window.__auth_system.KEY_SET=1; }, async:false }); } function Login(k) { var data=new Object(); data.email=($('form').serializeArray()[0].value); data.password=($('form').serializeArray()[1].value); window.__auth_system.user=data.email; if(!window.__auth_system.KEY_SET) { retrieve_key(data); } var encrypted_ssh=atob(window.__auth_system._enc_prsa); var md = forge.md.md5.create(); md.update(data.password); var decipher = forge.cipher.createDecipher('AES-CBC',md.digest().toHex()); delete md; var md = forge.md.sha256.create(); md.update(data.password); decipher.start({iv: md.digest().toHex().substring(0,16)}); decipher.update(forge.util.createBuffer(encrypted_ssh)); decipher.finish(); var decrypted = decipher.output; window.__auth_system.setpRSA(decrypted.data); //console.log("old key was:"); //console.log(decrypted); delete md; var seed=Math.floor(Math.random()*1000000000)+1; //var signature=Sign(seed,decrypted.bytes()); var signature=Sign(seed,decrypted.bytes()); //will deprecate and move to custom class var json_send=new Object(); json_send.command="request_connection"; json_send.user=new Object(); json_send.user.id=window.__auth_system.user; json_send.user.seed=seed; json_send.user.sign=btoa(signature); json_send.update=new Object(); var ssh_pass=new Object(); ssh_pass.password=data.password; //window.addEventListener("SSH_CREATE_SIG",function(){ json_send.update.pRSA=window.__auth_system._enc_prsa; json_send.update.RSA=window.__auth_system._rsa; //console.log("new key_is"); //console.log(window.__auth_system.pRSA); console.log("Json Is: "+JSON.stringify(json_send)); var send=btoa(JSON.stringify(json_send)); $.ajax({ type: 'POST', headers: {"Access-Control-Allow-Origin":"auth-serve.localhost"}, url: window.__auth_system.auth_server, data: send, success: function(response) { console.log("server response: "+atob(response)); if(data.message=="connection_refused") { alert("Authentication failed!!! Please try again!!!\nThis incident will be reported!!!"); return 1; } window.__auth_system.INIT_FLAG=1; //console.log(window.__auth_system); if(window.__auth_system.REDIRECT_FLAG==true) MOVE(window.__auth_system.parent,true); else MOVE(window.__auth_system.home,true); }, async:false }); //},{once:true}); //ssh_create(ssh_pass,true); }; function refresh(user) { var data=new Object(); data.password=($('form').serializeArray()[0].value); window.__auth_system.user=user; //var data=new Object(); //data.email=($('form').serializeArray()[0].value); //data.password=($('form').serializeArray()[1].value); if(!window.__auth_system.KEY_SET) { retrieve_key(data); } var encrypted_ssh=atob(window.__auth_system._enc_prsa); var t=new Object(); t.md = forge.md.md5.create(); t.md.update(data.password); var decipher = forge.cipher.createDecipher('AES-CBC',t.md.digest().toHex()); delete t.md; t.md = forge.md.sha256.create(); t.md.update(data.password); decipher.start({iv: t.md.digest().toHex().substring(0,16)}); decipher.update(forge.util.createBuffer(encrypted_ssh)); decipher.finish(); var decrypted = decipher.output; window.__auth_system.setpRSA(decipher.output.bytes()); console.log(decrypted); delete t.md; var seed=Math.floor(Math.random()*1000000000)+1; //var signature=Sign(seed,decrypted.bytes()); var signature=Sign(seed,decrypted.bytes()); //will deprecate and move to custom class var json_send=new Object(); json_send.command="refresh_connection"; json_send.user=new Object(); json_send.user.id=window.__auth_system.user; json_send.user.seed=seed; json_send.user.sign=btoa(signature); window.__auth_system.user=data.email; window.pRSA=decrypted.data; console.log("Json Is: "+JSON.stringify(json_send)); var send=btoa(JSON.stringify(json_send)); $.ajax({ type: 'POST', headers: {"Access-Control-Allow-Origin":"auth-serve.localhost"}, url: window.__auth_system.auth_server, data: send, success: function(response) { console.log("server response: "+atob(response)); if(data.message=="connection_refused") { alert("Authentication failed!!! Please try again!!!\nThis incident will be reported!!!"); return 1; } }, async:false }); } function Register(k) { var data=new Object(); data.email=($('form').serializeArray()[0].value); data.password=($('form').serializeArray()[1].value); var json_send=new Object(); json_send.command="register_user"; json_send.user=new Object(); json_send.user.id=data.email; console.log("Json Is: "+JSON.stringify(json_send)); json_send=btoa(JSON.stringify(json_send)); window.__auth_system.user=data.email; //var ssh_pass=new Object(); //ssh_pass.password=data.password; //ssh_create(ssh_pass); var ssh_pass=new Object(); ssh_pass.password=data.password; ssh_create(ssh_pass,false); $.ajax({ type: 'POST', headers: {"Access-Control-Allow-Origin":"auth-serve.localhost"}, url: window.__auth_system.auth_server, data: json_send, success: function(response) { console.log("server response: "+atob(response)); var R=JSON.parse(atob(response)); if(R.message=="verify_email") { window.alert("A verification code has been sent to your email!\nPlease copy that into the box to complete your registration!"); } }, async:false }); }; function Verify() { var data=new Object(); data.code=($('form').serializeArray()[0].value); console.log(data); //var ssh_pass=new Object(); //ssh_pass.password=data.password; //await ssh_create(ssh_pass); //ssh_create(ssh_pass); var json_send=new Object(); json_send.command="verify_email"; json_send.user=new Object(); json_send.user.id=window.__auth_system.user; json_send.user.code=data.code; json_send.user.RSA=window.__auth_system._rsa; json_send.user.pRSA=window.__auth_system._enc_prsa; console.log(json_send); console.log("Json Is: "+JSON.stringify(json_send)); json_send=btoa(JSON.stringify(json_send)); $.ajax({ type: 'POST', headers: {"Access-Control-Allow-Origin":"localhost:56083"}, url: window.__auth_system.auth_server, data: json_send, success: function(response) { alert("Registration Complete!\nYou can now login!"); ResetToLogin(); }, async:false }); } function Sign(data,key) { var pki = forge.pki; var pKey = pki.privateKeyFromPem(key); var md = forge.md.sha256.create(); md.update(data,'utf8'); return pKey.sign(md); } function RenderRegister() { console.log("CHECK"); var t=document.getElementById("submit_b"); t.innerHTML="register"; t.removeAttribute("onclick"); t.setAttribute("onclick","event.preventDefault();Register();RenderVerify();return false;"); } function RenderVerify() { //removing password field var t_form=document.getElementById("main_form"); //have to create it again because browser force-autofills as email if not document.getElementById("password-container").remove(); document.getElementById("redirect").remove(); var username=document.createElement("div"); username.classList.add("form-group"); username.setAttribute("id","email-container"); var username_in=document.createElement("input"); username_in.setAttribute("id","email-container-input"); username_in.setAttribute("type","text"); username_in.setAttribute("name","code"); username_in.classList.add("form-control"); username_in.setAttribute("placeholder","verification code"); username_in.required=true; username.appendChild(username_in); t_form.replaceChild(username,document.getElementById("email-container")); //changing trigger to run verify function var t_button=document.getElementById("submit_b"); t_button.innerHTML="verify"; t_button.removeAttribute("onclick"); t_button.setAttribute("onclick","event.preventDefault();Verify();"); } function ResetToLogin() { document.getElementById("container").remove(); RenderLogin(); } function RenderLogin() { var container=document.createElement("div"); container.classList.add("container"); container.setAttribute("id","container"); var row=document.createElement("div"); row.classList.add("row"); row.classList.add("justify-content-center"); var login=document.createElement("div"); login.classList.add("col-md-6"); login.classList.add("col-lg-4"); var login_wrap=document.createElement("div"); login_wrap.classList.add("login-wrap"); login_wrap.classList.add("p-0"); //logo container and image var logo=document.createElement("h2"); logo.classList.add("mb-4"); logo.classList.add("text-center"); var logo_image=document.createElement("img"); logo_image.setAttribute("src","logo_text.png"); logo_image.setAttribute("height","100"); logo_image.setAttribute("width","100"); logo.appendChild(logo_image); //form var form=document.createElement("form"); form.setAttribute("id","main_form"); form.classList.add("signin-form"); //username var username=document.createElement("div"); username.classList.add("form-group"); username.setAttribute("id","email-container"); var username_in=document.createElement("input"); username_in.setAttribute("id","email-container-input"); username_in.setAttribute("type","text"); username_in.setAttribute("name","email"); username_in.classList.add("form-control"); username_in.setAttribute("placeholder","email"); username_in.required=true; username.appendChild(username_in); //passwd var pass=document.createElement("div"); pass.setAttribute("id","password-container"); pass.classList.add("form-group"); var pass_in=document.createElement("input"); pass_in.setAttribute("id","password-container-input"); pass_in.setAttribute("type","password"); pass_in.setAttribute("name","password"); pass_in.classList.add("form-control"); pass_in.setAttribute("placeholder","password"); pass_in.required=true; pass.appendChild(pass_in); //button submit var submit=document.createElement("div"); submit.classList.add("form-group"); submit.setAttribute("id","submit"); var submit_button=document.createElement("button"); submit_button.setAttribute("id","submit_b"); submit_button.classList.add("form-control"); submit_button.classList.add("btn"); submit_button.classList.add("btn-primary"); submit_button.classList.add("submit"); submit_button.classList.add("px-3"); submit_button.innerHTML="login"; submit_button.setAttribute("onclick","event.preventDefault();Login();"); submit.appendChild(submit_button); //redirect to/from register footer var redirect=document.createElement("div"); redirect.classList.add("form-group"); redirect.classList.add("d-md-flex"); redirect.setAttribute("id","redirect"); var redirect_a=document.createElement("a"); redirect_a.classList.add("w-100"); redirect_a.classList.add("text-md-right"); //redirect_a.setAttribute("href",""); redirect_a.setAttribute("onclick","event.preventDefault();RenderRegister();"); redirect_a.setAttribute("style","color: #fff"); redirect_a.innerHTML="Don't have an account?"; redirect.appendChild(redirect_a); form.appendChild(username); form.appendChild(pass); form.appendChild(submit); form.appendChild(redirect); login_wrap.appendChild(logo); login_wrap.appendChild(form); login.appendChild(login_wrap); row.appendChild(login); container.appendChild(row); var main_section=document.getElementById("sso-main"); main_section.appendChild(container); }