If You Can, Avoid Saving User Passwords

About 20 years ago, I predicted that JavaScript would fail, as it was such as poor programming language (and not really Java), and that it…

Photo by Matthew Brodeur on Unsplash

If You Can, Avoid Saving User Passwords

About 20 years ago, I predicted that JavaScript would fail, as it was such as poor programming language (and not really Java), and that it would struggle with updates. How wrong was I? It is now often the go-to language for the front end, and with Node.js, it now powers many back-end infrastructures. So let’s do a bit of JavaScript and federated login. And for those who study Cybersecurity, I cannot recommend highly enough, to study JavaScript and its integration into RESTful Web services, and many Web sites can be broken with a bit of experience in understanding how JavaScript integrates into a page, and in how OAuth 2.0 is used to pass rights tokens.

With the rising number of data breaches, we need to build systems which do not store passwords. Even storing a hashed version of the password is not secure, especially has Hashcat could be running at over 1 Tera Hashes per second (1,000,000,000,000 Hashes per second). One method is to use a federated approach, and where we ask the user to log into a trusted federated service, and then examine the token that is returned. A great advantage of this is that the user just selects the identity provider that they trust, and can manage their password with that provider.

So let’s look at any integration into my Web site:

The Google integration is fairly simple, and where we go to the Google developer page, and create an OAuth Client ID and secret:

Then we two JavaScript imports into the page and a meta tag for the ClientID:

<meta name="google-signin-client_id" content="509758399917-fasnmofnjirqv0912hqk8pc2k5pfmbjn.apps.googleusercontent.com">
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src=”https://apis.google.com/js/platform.js?onload=init" async defer></script>

And then the button is added as:

<div class=”g-signin2" data-longtitle=”true” data-onsuccess=”onSignIn””></div>

And then we hook the button with the onSign() function:

<script>
function onSignIn(googleUser) {

var profile = googleUser.getBasicProfile();
var user_uname = profile.getName();
var user_email = profile.getEmail();
var id_token = googleUser.getAuthResponse().id_token;
var xhr = new XMLHttpRequest();
xhr.open('POST', '/account/ll', false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

xhr.send('idtoken=' + id_token);
var response = xhr.responseText;
document.getElementById("res").innerHTML = response;
}
function signOut() {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/account/ll3', false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function () {
console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('idtoken=');
var response = xhr.responseText;
document.getElementById("t1").innerHTML = "";
document.getElementById("t2").innerHTML = "";
document.getElementById("res").innerHTML = "";
}</script>

In this case, we determine the Google name and email address, and then perform a Post of the email address to the back-end, so that it can be checked. Next for Facebook, we create your ClientID from the Facebook Developer site:

We gain the token from Facebook, and then use the call to /me?fields=name,email’, in order to resolve the name and the email address:

<script async defer crossorigin="anonymous"
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v7.0&appId=158691244298921"></script>
<div id=”fb-root”></div>
<script>window.fbGetLoginStatus = function () {
FB.getLoginStatus(function (response) {
if (isConnected(response)) {
fbLogIn(response);
}
});
}
function isConnected(res) {
return res.status === ‘connected’;
}
function fbLogIn(response) {
let loginForm = document.querySelector(‘.login-form’);
let input = getHiddenInput(“fbAuthResponse”, JSON.stringify(response.authResponse));
loginForm.appendChild(input);
loginForm.submit();
}
function getHiddenInput(name, value) {
let input = document.createElement(“input”);
input.setAttribute(“type”, “hidden”);
input.setAttribute(“name”, name);
input.setAttribute(“value”, value);
var url = ‘/me?fields=name,email’;
FB.api(url, function (response) {
resp = response.email;
document.getElementById(“t1”).innerHTML = response.name;
document.getElementById(“t2”).innerHTML = response.email;
var xhr = new XMLHttpRequest();
xhr.open(‘POST’, ‘/account/ll2’, false);
xhr.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’);
xhr.onload = function () {
console.log(‘Signed in as: ‘ + xhr.responseText);
};
xhr.send(‘idtoken=’ + resp);
var fbr = xhr.responseText;
document.getElementById(“res”).innerHTML = fbr;
});
}</script>

The button is added with:

<div class=”fb-login-button”
data-size=”large”
data-button-type=”continue_with”
data-layout=”default”
data-auto-logout-link=”false”
data-use-continue-as=”false”
data-scope=”public_profile,email”
onlogin=”fbGetLoginStatus();”
data-width=””></div>

And finally, one of the most popular accounts is a Microsoft one, so let’s integrate through Microsoft. In this case, we generate to create an App:

and then generate a clientID:


<script>const loginRequest = {
scopes: ["openid", "profile", "User.Read"],
};

myMSALObj.loginPopup(loginRequest)
.then((loginResponse) => {
//Login Success callback code here
}).catch(function (error) {
console.log(error);
});</script>


<script>const msalConfig = {
auth: {
clientId: "bfbfea91-b3ff-47e4-8fa6-c811d7476da6",
authority: "https://login.microsoftonline.com/common",
redirectUri: "https://asecuritysite.com/account/logon",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored

storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
}
};




const myMSALObj = new Msal.UserAgentApplication(msalConfig);

function signIn() {

myMSALObj.loginPopup(loginRequest)
.then(loginResponse => {
console.log('id_token acquired at: ' + new Date().toString());
console.log(loginResponse);

if (myMSALObj.getAccount()) {
test = myMSALObj.getAccount();




res2 = test.idToken;


resp = res2.preferred_username;

var xhr = new XMLHttpRequest();
xhr.open('POST', '/account/ll2', false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function () {
console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('idtoken=' + resp);
var fbr = xhr.responseText;
document.getElementById("res").innerHTML = fbr;

document.getElementById("t1").innerHTML = res2.name;
document.getElementById("t2").innerHTML = res2.preferred_username;



}
}).catch(error => {
console.log(error);
});
}</script>

and with a button to activate:

<button type="button" id="signIn" class="btn btn-primary" onclick="signIn()">Sign In Microsoft</button></td>

I can now go passwordless. So, here it is in action:

Conclusions

Get federate your login, and focus on your App, rather than identifying the user. For those into Cybersecurity, you really need to understand JavaScript and GET and POST, so go learn them.