Skip to content

Commit

Permalink
#2fa authentication example
Browse files Browse the repository at this point in the history
  • Loading branch information
xang555 committed Dec 31, 2018
0 parents commit 99a8f07
Show file tree
Hide file tree
Showing 14 changed files with 2,719 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
48 changes: 48 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const consolidate= require('consolidate')
var nuncjuck=require('nunjucks');
var indexRouter = require('./routes/index');
var login = require('./routes/login');
var cookieSession = require('cookie-session')

var app = express();

// view engine setup
app.engine('html',consolidate.nunjucks);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieSession({
name: 'session',
keys: ['example', 'mysecret'],
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}))

app.use('/', indexRouter);
app.use('/login', login)
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

// render the error page
res.status(err.status || 500);
res.render('error');
});

module.exports = app;
90 changes: 90 additions & 0 deletions bin/www
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env node

/**
* Module dependencies.
*/

var app = require('../app');
var debug = require('debug')('f2a:server');
var http = require('http');

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}

return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}

var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
11 changes: 11 additions & 0 deletions fake-data.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"auth": {
"uname": "xang",
"passwd": "motherfucker"
},
"two_factor_auth": {
"is_enable": false,
"two_factor_auth_secret": null,
"two_factor_auth_secret_temp": null
}
}
1 change: 1 addition & 0 deletions fake-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"auth":{"uname":"xang","passwd":"ilovedocker"},"two_factor_auth":{"is_enable":false,"two_factor_auth_secret":null,"two_factor_auth_secret_temp":"JZZVWVSOIMRW632GOMUHIT2AJZBHAUB7LNDUMIKFIM2EKLSONRFQ"}}
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "f2a",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"consolidate": "^0.15.1",
"cookie-parser": "~1.4.3",
"cookie-session": "^2.0.0-beta.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"http-errors": "~1.6.2",
"jade": "~1.11.0",
"morgan": "~1.9.0",
"nunjucks": "^3.1.6",
"qrcode": "^1.3.2",
"speakeasy": "^2.0.0"
}
}
8 changes: 8 additions & 0 deletions public/stylesheets/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
color: #00B7FF;
}
21 changes: 21 additions & 0 deletions routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
var express = require('express');
var router = express.Router();

const md = function (req, res, next) {
if(req.session.user === null || typeof req.session.user === 'undefined'){
return res.redirect('/login')
}
next()
}

/* GET home page. */
router.get('/', md, function(req, res, next) {
res.render('index', { title: 'Private page', msg: 'this is private page!' });
});

router.get('/logout', function (req, res, next) {
req.session.user = null
res.redirect('/login')
})

module.exports = router;
102 changes: 102 additions & 0 deletions routes/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
var express = require('express');
var router = express.Router();
const fs = require('fs')
const path = require('path')
const speakeasy = require("speakeasy");
const QRCode = require('qrcode')

const fake_data_path = path.join(__dirname,'..', 'fake-data.json')

/* GET home page. */
router.get('/', function (req, res, next) {
res.render('login', { title: 'login', is_one_auth: true });
});

router.post('/', function (req, res, next) {

fs.readFile(fake_data_path, (err, data) => {
if(err){
throw err
}

const json_data = JSON.parse(data)

const {
uname,
passwd,
two_facetor_code,
} = req.body

if(typeof two_facetor_code !== 'undefined') {

const base32secret = json_data['two_factor_auth']['is_enable'] ? json_data['two_factor_auth']['two_factor_auth_secret']:json_data['two_factor_auth']['two_factor_auth_secret_temp'];
const verified = speakeasy.totp.verify({
secret: base32secret,
encoding: 'base32',
window: 1,
token: two_facetor_code });
if(verified){
json_data['two_factor_auth']['two_factor_auth_secret_temp'] = null
json_data['two_factor_auth']['two_factor_auth_secret'] = base32secret
json_data['two_factor_auth']['is_enable'] = true
fs.writeFile(fake_data_path, JSON.stringify(json_data), (err) => {
if(err){
throw err
}

req.session.user = {
name: "xang",
auth: true
}

res.redirect('/')

})
}else {
res.send("Fail! check your token code")
}

}else {
if(json_data['auth']['uname'] === uname && json_data['auth']['passwd'] === passwd) {

if(json_data['two_factor_auth']['is_enable']) {
return res.render('login', { title: 'login', is_two_auth: true, is_enable_2fa: true })
}else {
const secret = speakeasy.generateSecret({
name: "xangnam two-factor"
});
console.log(secret.base32)
json_data['two_factor_auth']['two_factor_auth_secret_temp'] = secret.base32
fs.writeFile(fake_data_path, JSON.stringify(json_data), (err) => {
if(err){
throw err
}
// const otpauth_url = speakeasy.otpauthURL({
// secret: secret.base32,
// label: "vte-camp",
// type: 'hotp',
// counter: 0,
// issuer: 'xangnam',
// encoding: 'base32'
// })
QRCode.toDataURL(secret.otpauth_url, function (err, data_url) {
if(err) {
throw err
}
return res.render('login', { title: 'login', img: data_url, is_two_auth: true, is_enable_2fa: false })
});

})
}

}else {
// auth fail
return res.render('login', { errMsg: "User or Password fail!", title: 'login', is_one_auth: true })
}
}

})

});

module.exports = router;
9 changes: 9 additions & 0 deletions views/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

<html>
<head>
<title>error</title>
</head>
<body>
<h3>{{error.stack}}</h3>
</body>
</html>
10 changes: 10 additions & 0 deletions views/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends "layout.html" %}

{% block header %}
{{title}}
{% endblock %}

{% block body %}
<h1>{{msg}}</h1>
<a href="/logout">logout</a>
{% endblock %}
14 changes: 14 additions & 0 deletions views/layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<html>
<head>
<title>
{% block header %}

{% endblock %}
</title>
</head>
<body>
{% block body %}

{% endblock %}
</body>
</html>
43 changes: 43 additions & 0 deletions views/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{% extends "layout.html" %}

{% block header %}
{{title}}
{% endblock %}

{% block body %}

{% if is_one_auth %}

<div>
<form method="POST">
<input name="uname" type="text" placeholder="Username" />
<br />
<input name="passwd" type="password" placeholder="Password" />
<br />
<input name="submit" type="submit" />
</form>
<div style="color: 'red';">
<h6>{{errMsg}}</h6>
</div>
</div>

{% elif is_two_auth %}

<div>
<form method="POST" action="/login">
{% if is_enable_2fa === false %}
<img src={{img}} alt="qrcode" />
{% endif %}
<br />
<input name="two_facetor_code" type="text" placeholder="Two factor number" />
<br />
<input name="submit" type="submit" title="Verify" />
</form>
<div style="color: 'red';">
<h6>{{errMsg}}</h6>
</div>
</div>

{% endif %}

{% endblock %}
Loading

0 comments on commit 99a8f07

Please sign in to comment.