Skip to content

Instantly share code, notes, and snippets.

@ChrisRisner
Last active December 16, 2015 07:19
Show Gist options
  • Save ChrisRisner/5398281 to your computer and use it in GitHub Desktop.
Save ChrisRisner/5398281 to your computer and use it in GitHub Desktop.
AuthDemo Scripts
var crypto = require('crypto');
var iterations = 1000;
var bytes = 32;
var aud = "Custom";
var masterKey = "Your-Master-Key";
function insert(item, user, request) {
var accounts = tables.getTable('accounts');
if (request.parameters.login) {
// this is a login attempt
accounts.where({ username : item.username }).read({
success: function(results) {
if (results.length === 0) {
request.respond(401, "Incorrect username or password");
}
else {
var account = results[0];
hash(item.password, account.salt, function(err, h) {
var incoming = h;
if (slowEquals(incoming, account.password)) {
var expiry = new Date().setUTCDate(new Date().getUTCDate() + 30);
var userId = aud + ":" + account.id;
request.respond(200, {
userId: userId,
token: zumoJwt(expiry, aud, userId, masterKey)
});
}
else {
request.respond(401, "Incorrect username or password");
}
});
}
}
});
}
else {
// account creation - check username does not already exist
if (!item.username.match(/^[a-zA-Z0-9]{5,}$/)) {
request.respond(400, "Invalid username (at least 4 chars, alphanumeric only)");
return;
}
else if (item.password.length < 7) {
request.respond(400, "Invalid password (least 7 chars required)");
return;
}
accounts.where({ username : item.username}).read({
success: function(results) {
if (results.length > 0) {
request.respond(400, "Username already exists");
return;
}
else {
// Add your own validation - what fields do you require to
// add a unique salt to the item
item.salt = new Buffer(crypto.randomBytes(bytes)).toString('base64');
// hash the password
hash(item.password, item.salt, function(err, h) {
item.password = h;
request.execute({
success: function () {
// Remove the password and salt so they aren't returned to the user
delete item.password;
delete item.salt;
var userId = aud + ":" + item.id;
item.userId = userId;
var expiry = new Date().setUTCDate(new Date().getUTCDate() + 30);
item.token = zumoJwt(expiry, aud, userId, masterKey);
request.respond();
}
});
});
}
}
});
}
}
function hash(text, salt, callback) {
crypto.pbkdf2(text, salt, iterations, bytes, function(err, derivedKey){
if (err) { callback(err); }
else {
var h = new Buffer(derivedKey).toString('base64');
callback(null, h);
}
});
}
function slowEquals(a, b) {
var diff = a.length ^ b.length;
for (var i = 0; i < a.length && i < b.length; i++) {
diff |= (a[i] ^ b[i]);
}
return diff === 0;
}
function zumoJwt(expiryDate, aud, userId, masterKey) {
var crypto = require('crypto');
function base64(input) {
return new Buffer(input, 'utf8').toString('base64');
}
function urlFriendly(b64) {
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(new RegExp("=", "g"), '');
}
function signature(input) {
var key = crypto.createHash('sha256').update(masterKey + "JWTSig").digest('binary');
var str = crypto.createHmac('sha256', key).update(input).digest('base64');
return urlFriendly(str);
}
var s1 = '{"alg":"HS256","typ":"JWT","kid":0}';
var j2 = {
"exp":expiryDate.valueOf() / 1000,
"iss":"urn:microsoft:windows-azure:zumo",
"ver":1,
"aud":aud,
"uid":userId
};
var s2 = JSON.stringify(j2);
var b1 = urlFriendly(base64(s1));
var b2 = urlFriendly(base64(s2));
var b3 = signature(b1 + "." + b2);
return [b1,b2,b3].join(".");
}
function read(query, user, request) {
var result = {
id: query.id,
identities: user.getIdentities(),
userName: ''
};
var sep = user.userId.indexOf(':');
var provider = user.userId.substring(0, sep).toLowerCase();
//If they signed in with a custom auth account, get their information from the
//accounts table
if (provider == 'custom') {
var userId = user.userId.substring(sep + 1);
var accounts = tables.getTable('accounts');
accounts.where({ id : userId }).read({
success: function(results) {
var account = results[0];
result.UserName = account.username;
request.respond(200, [result]);
return;
}
});
return;
}
var url;
//If they used a built in provider, get hte url we should use to get more information
var identities = user.getIdentities();
if (identities.google) {
var googleAccessToken = identities.google.accessToken;
url = 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + googleAccessToken;
} else if (identities.facebook) {
var fbAccessToken = identities.facebook.accessToken;
url = 'https://graph.facebook.com/me?access_token=' + fbAccessToken;
} else if (identities.microsoft) {
var liveAccessToken = identities.microsoft.accessToken;
url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + liveAccessToken;
} else if (identities.twitter) {
//For twitter we need the twitter ID to use in the URL
var userId = user.userId;
var twitterId = userId.substring(userId.indexOf(':') + 1);
url = 'https://api.twitter.com/1/users/show/' + twitterId + '.json';
console.error(url);
}
if (url) {
var requestCallback = function (err, resp, body) {
if (err || resp.statusCode !== 200) {
console.error('Error sending data to the provider: ', err);
request.respond(statusCodes.INTERNAL_SERVER_ERROR, body);
} else {
try {
var userData = JSON.parse(body);
if (userData.name != null)
result.UserName = userData.name;
else
result.UserName = "can't get username";
request.respond(200, [result]);
} catch (ex) {
console.error('Error parsing response from the provider API: ', ex);
request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex);
}
}
}
var req = require('request');
var reqOptions = {
uri: url,
headers: { Accept: "application/json" }
};
req(reqOptions, requestCallback);
} else {
console.log('insert');
console.log(request);
// Insert with default user name
request.execute();
}
}
function insert(item, user, request) {
if (request.parameters.bypass)
request.respond(200, item);
else
request.respond(401);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment