Skip to content

Instantly share code, notes, and snippets.

@jonmarkgo
Created April 13, 2012 18:22
Show Gist options
  • Save jonmarkgo/2378931 to your computer and use it in GitHub Desktop.
Save jonmarkgo/2378931 to your computer and use it in GitHub Desktop.
Two-Factor Authentication with Node.js and Twilio
var client = new TwilioClient('ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'yourwebsite.com');
var phone = client.getPhoneNumber('+18881234567');
app.configure(function () {
app.set('view engine', 'jade');
app.set('view options', {layout: false});
app.use(express.cookieParser());
app.use("/bootstrap", express['static'](__dirname + '/bootstrap'));
app.use(express.session({
secret: 'secret',
key: 'express.sid',
store: store = new MemoryStore()
}));
});
app.get('/', function (req, res) {
res.render('index', {username: req.session.username, phonenumber: req.session.phonenumber});
});
app.listen(8080);
socket.on('set username', function (val) {
sess.reload(function () {
sess.username = val;
sess.touch().save();
});
});
socket.on('set password', function (val) {
sess.reload(function () {
sess.password = val;
sess.touch().save();
});
});
socket.on('init phone', function (val) {
sess.reload(function () {
sess.phonenumber = val;
var codelength = 4; //# of digits in the verification code
sess.verify = Math.floor(Math.random() * (Math.pow(10, (codelength - 1)) * 9)) + Math.pow(10, (codelength - 1));
sess.touch().save();
socket.emit('newcode', sess.verify);
phone.setup(function () {
phone.makeCall(val, null, function (call) {
call.on('answered', function (reqParams, res) {
console.log('Call answered');
var intro = new Twiml.Say('Hello, ' + sess.username + '. Please enter your ' + String(sess.verify).length + ' digit verification code!');
res.append(getDigit(0, sess.verify).append(intro));
res.send();
});
call.on('ended', function (reqParams) {
console.log('Call ended');
});
});
});
});
});
function getDigit(num, verify) {
var getDigits = new Twiml.Gather(null, {numDigits: 1});
getDigits.on('gathered', function (reqParams, resp) {
if (reqParams.Digits === String(verify).charAt(num)) {
socket.emit('correct code', num);
if (num < (String(verify).length - 1)) {
resp.append(getDigit(num + 1, verify));
} else {
socket.emit('verified', 1);
resp.append(new Twiml.Say('Thank you, good bye!'));
}
} else {
resp.append(new Twiml.Say('You have entered the wrong code, please try logging in again.')).append(new Twiml.Hangup());
socket.emit('wrong code', num);
}
resp.send();
});
return getDigits;
}
/*jslint unparam: true, node: true, sloppy: true, nomen: true, maxerr: 50, indent: 2 */
var io = require('socket.io'), express = require('express'), util = require('util'), app = express.createServer(), connect = require('express/node_modules/connect'), parseCookie = connect.utils.parseCookie, MemoryStore = connect.middleware.session.MemoryStore, store, TwilioClient = require('twilio').Client, Twiml = require('twilio').Twiml;
var client = new TwilioClient('ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'yourwebsite.com'); //Enter your credentials and hostname here
var phone = client.getPhoneNumber('+18881234567'); //Enter your outgoing phone # here
app.configure(function () {
app.set('view engine', 'jade');
app.set('view options', {layout: false});
app.use(express.cookieParser());
app.use("/bootstrap", express['static'](__dirname + '/bootstrap'));
app.use(express.session({
secret: 'secret',
key: 'express.sid',
store: store = new MemoryStore()
}));
});
app.get('/', function (req, res) {
res.render('index', {username: req.session.username, phonenumber: req.session.phonenumber});
});
app.listen(8080);
io.listen(app).set('authorization', function (data, accept) {
if (!data.headers.cookie) {
return accept('No cookie transmitted.', false);
}
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['express.sid'];
store.load(data.sessionID, function (err, session) {
if (err || !session) {
return accept('Error', false);
}
data.session = session;
return accept(null, true);
});
}).sockets.on('connection', function (socket) {
var sess = socket.handshake.session;
socket.log.info('a socket with sessionID', socket.handshake.sessionID, 'connected');
socket.on('set username', function (val) {
sess.reload(function () {
sess.username = val;
sess.touch().save();
});
});
socket.on('set password', function (val) {
sess.reload(function () {
sess.password = val;
sess.touch().save();
});
});
function getDigit(num, verify) {
var getDigits = new Twiml.Gather(null, {numDigits: 1});
getDigits.on('gathered', function (reqParams, resp) {
if (reqParams.Digits === String(verify).charAt(num)) {
socket.emit('correct code', num);
if (num < (String(verify).length - 1)) {
resp.append(getDigit(num + 1, verify));
} else {
socket.emit('verified', 1);
resp.append(new Twiml.Say('Thank you, good bye!'));
}
} else {
resp.append(new Twiml.Say('You have entered the wrong code, please try logging in again.')).append(new Twiml.Hangup());
socket.emit('wrong code', num);
}
resp.send();
});
return getDigits;
}
socket.on('init phone', function (val) {
sess.reload(function () {
sess.phonenumber = val;
var codelength = 4; //# of digits in the verification code
sess.verify = Math.floor(Math.random() * (Math.pow(10, (codelength - 1)) * 9)) + Math.pow(10, (codelength - 1));
sess.touch().save();
socket.emit('newcode', sess.verify);
phone.setup(function () {
phone.makeCall(val, null, function (call) {
call.on('answered', function (reqParams, res) {
console.log('Call answered');
var intro = new Twiml.Say('Hello, ' + sess.username + '. Please enter your ' + String(sess.verify).length + ' digit verification code!');
res.append(getDigit(0, sess.verify).append(intro));
res.send();
});
call.on('ended', function (reqParams) {
console.log('Call ended');
});
});
});
});
});
});
loginform.submit(function (e) {
e.preventDefault();
socket.emit('set username', username.val());
socket.emit('set password', password.val());
socket.emit('init phone', phonenumber.val());
}).focus();
socket.on('newcode', function (val) {
$("#verify").show();
$("#verify").removeClass("alert-error");
$("#verify").removeClass("alert-success");
$("#verify").addClass("alert-info");
$("#verify").html('<h1>Verification Code:</h1><br><div id="code"></div><br>');
for(var i =0; i < String(val).length; i++)
{
$("#code").append('<span id="code'+i+'" class="badge" style="font-size:50px;">'+String(val).charAt(i)+'</span>');
}
});
socket.on('wrong code', function (val) {
$("#verify").removeClass("alert-info");
$("#verify").addClass("alert-error");
$("#code" + val).removeClass("badge-info");
$("#code" + val).addClass("badge-error");
$("#verify h1").html('Incorrect Code, Try Again!');
});
socket.on('verified', function (val) {
$("#verify").removeClass("alert-info");
$("#verify").removeClass("alert-error");
$("#verify").addClass("alert-success");
$("#verify").html('You have successfully logged in!');
$("#loginform").hide();
$("#nyan").show();
});
socket.on('correct code', function (val) {
$("#code" + val).removeClass("badge-info");
$("#code" + val).addClass("badge-success");
});
!!! html
html
head
title Two-Factor Authentication with Twilio, Node.js, and Socket.io
script(src='/socket.io/socket.io.js')
script(src='http://code.jquery.com/jquery-latest.js')
link(rel='stylesheet', href='/bootstrap/css/bootstrap.min.css')
link(rel='stylesheet', href='/bootstrap/css/bootstrap-responsive.min.css')
body
div.container
h1 Two-Factor Auth Demo
div.alert.alert-info#verify
h1 Verification Code:
br
div#code
br
div.row
div.span5
img#nyan(src='/bootstrap/poptart1red1.gif')
form.well#loginform
fieldset
legend Login to Access Secrets
div.control-group
input#username(placeholder='Username')
input#password(type='password',placeholder='Password')
input#phonenumber(placeholder='+18881234567')
div.form-actions
input#submit.btn.btn-primary(type='submit',value='Log In')
!!! html
html
head
title Two-Factor Authentication with Twilio, Node.js, and Socket.io
script(src='/socket.io/socket.io.js')
script(src='http://code.jquery.com/jquery-latest.js')
link(rel='stylesheet', href='/bootstrap/css/bootstrap.min.css')
link(rel='stylesheet', href='/bootstrap/css/bootstrap-responsive.min.css')
body
div.container
h1 Two-Factor Auth Demo
div.alert.alert-info#verify
h1 Verification Code:
br
div#code
br
div.row
div.span5
img#nyan(src='/bootstrap/poptart1red1.gif')
form.well#loginform
fieldset
legend Login to Access Secrets
div.control-group
input#username(placeholder='Username')
input#password(type='password',placeholder='Password')
input#phonenumber(placeholder='+18881234567')
div.form-actions
input#submit.btn.btn-primary(type='submit',value='Log In')
script
$(function () {
$("#verify").hide();
$("#nyan").hide();
var socket = io.connect()
, username = $('#username')
, password = $('#password')
, phonenumber = $('#phonenumber')
, submit = $('#submit')
, loginform = $('#loginform');
socket.on('newcode', function (val) {
$("#verify").show();
$("#verify").removeClass("alert-error");
$("#verify").removeClass("alert-success");
$("#verify").addClass("alert-info");
$("#verify").html('<h1>Verification Code:</h1><br><div id="code"></div><br>');
for(var i =0; i < String(val).length; i++)
{
$("#code").append('<span id="code'+i+'" class="badge" style="font-size:50px;">'+String(val).charAt(i)+'</span>');
}
});
socket.on('wrong code', function (val) {
$("#verify").removeClass("alert-info");
$("#verify").addClass("alert-error");
$("#code" + val).removeClass("badge-info");
$("#code" + val).addClass("badge-error");
$("#verify h1").html('Incorrect Code, Try Again!');
});
socket.on('verified', function (val) {
$("#verify").removeClass("alert-info");
$("#verify").removeClass("alert-error");
$("#verify").addClass("alert-success");
$("#verify").html('You have successfully logged in!');
$("#loginform").hide();
$("#nyan").show();
});
socket.on('correct code', function (val) {
$("#code" + val).removeClass("badge-info");
$("#code" + val).addClass("badge-success");
});
loginform.submit(function (e) {
e.preventDefault();
socket.emit('set username', username.val());
socket.emit('set password', password.val());
socket.emit('init phone', phonenumber.val());
}).focus();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment