Database-based login/logout

This commit is contained in:
Timothy Warren 2014-09-24 17:58:53 -04:00
parent 02083af4a9
commit c5393b050e
8 changed files with 164 additions and 13 deletions

38
app.js
View File

@ -3,6 +3,7 @@
// ------------ Basic Dependencies -------------------------------------------
var express = require('express'),
session = require('express-session'),
csrf = require('csurf'),
path = require('path'),
favicon = require('serve-favicon'),
logger = require('morgan'),
@ -11,7 +12,7 @@ var express = require('express'),
requireDir = require('require-dir'),
connection = require('express-myconnection'),
bcrypt = require('bcrypt-nodejs'),
mysql = require('mysql');
mysql = require('mysql2');
// ----------------------------------------------------------------------------
var app = express();
@ -20,13 +21,13 @@ app.set('trust proxy', true); // Trust X-Forwarded-* headers
// Database connection
app.use(
connection('mysql', {
connection(mysql, {
host: 'localhost',
user: 'node',
password: 'node',
port: 3306,
database: 'node'
}, 'request')
}, 'pool')
);
// view engine setup
@ -42,7 +43,28 @@ app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
resave: true,
saveUninitialized: true,
secret: 'j2uyc0hjh2;clkjang1ddojj'
}));
app.use(express.static(path.join(__dirname, 'public'), {redirect:false}));
app.use(csrf({
ignoreMethods: ['GET', 'HEAD', 'OPTIONS']
}));
//Check session for any pages that require authentication
app.use(function(err, req, res, next) {
if ( ! req.session.uid)
{
console.log("This should redirect to index!")
['/', '/login', '/logout'].forEach(function(item) {
if (req.path.match(item)) return next();
});
res.redirect(303, '/');
}
});
// Route mapping
// Routes are prefixed by the filename,
@ -54,6 +76,7 @@ Object.keys(routes).forEach(function(route) {
var path = (route != 'index')
? '/' + route
: '/';
app.use(path, routes[route]);
});
@ -66,6 +89,15 @@ app.use(function(req, res, next) {
// error handlers
// csrf error handler
app.use(function(err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN') return next(err);
// Bad CSRF Token
res.status(403);
res.send('Session has expired, or has been tampered with.');
});
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {

View File

@ -16,7 +16,6 @@
"dustjs-linkedin":"*",
"dustjs-helpers":"*",
"nodeunit":"*",
"mysql":"*",
"node-memcache":"*"
}
}

View File

@ -1,9 +1,73 @@
var express = require('express');
var router = express.Router();
/* GET home page. */
/* GET Home / Login Form */
router.get('/', function(req, res) {
res.render('index', { title: 'Express'});
var util = require('util');
var request = util.inspect(req, {depth: 2});
// If the user isn't logged in
if ( ! req.session.uid)
{
res.render('login', {
title: 'Node Task Manager',
csrfToken: req.csrfToken()
});
}
else
{
res.render('index', {
title: 'Node Task Manager',
user: req.session.username,
req: request
});
}
});
/* Login action */
router.post('/login', function(req, res) {
var bcrypt = require('bcrypt-nodejs');
var user = req.body.user,
pass = req.body.pass;
req.getConnection(function(err, connection) {
if (err) throw err;
var sql = " SELECT id, username, email, password, timezone, num_format " +
" FROM todo_user " +
" WHERE email = ? OR username = ? ";
// Find the username / email
connection.execute(sql, [user, user], function(err, rows, fields) {
if (err) throw err;
var user = rows[0];
// Verify the password hash
bcrypt.compare(pass, user.password, function(err, passRes) {
if (err) throw err;
// Password is good, set session data and redirect
if (passRes === true)
{
req.session.uid = user.id;
req.session.num_format = user.num_format;
req.session.username = user.username;
res.redirect(303, '/');
}
});
});
})
});
/* Logout action */
router.get('/logout', function(req, res) {
// Destroy the session, and redirect to the index page
req.session.destroy(function(err) {
res.redirect(303, '/');
});
});
module.exports = router;

View File

@ -3,7 +3,23 @@ var router = express.Router();
/* GET list of tasks */
router.get('/list', function(req, res) {
res.json([{}]);
req.getConnection(function(err, connection) {
if (err) throw err;
var uid = req.session.uid;
if ( ! uid)
{
console.log("Redirect because of bad session in list route");
res.redirect('/');
return;
}
connection.execute('SELECT * from todo_task_view WHERE user_id=?', [uid], function(err, rows) {
if (err) throw err;
res.json(rows);
});
});
});

1
views/csrf.dust Normal file
View File

@ -0,0 +1 @@
<input type="hidden" name="_csrf" value="{csrfToken}" />

View File

@ -1,6 +1,6 @@
{>layout/}
{<content}
<h1>{title}</h1>
<p>Welcome to {title}</p>
<p>Hi, {user}. Welcome to {title}</p>
<pre>{req}</pre>
{/content}

View File

@ -2,17 +2,29 @@
<html>
<head>
<title>{title}</title>
<link rel='stylesheet' href='/css/style.css' />
<link rel="stylesheet" href="/css/ink.min.css" />
<link rel="stylesheet" href="/css/ink-flex.min.css" />
<link rel="stylesheet" href="/css/font-awesome.min.css" />
</head>
<body>
<header class="ink-grid">
<h1>{title}</h1>
<nav class="ink-navigation">
<ul class="menu horizontal">
<li><a href="/logout">Logout</a></li>
</ul>
</nav>
</header>
<main class="ink-grid">
{+content}
This is the base content.
{/content}
</main>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="/js/native.history.js"></script>
</body>
<!--
{session}
-->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="/js/native.history.js"></script>
</html>

27
views/login.dust Normal file
View File

@ -0,0 +1,27 @@
{>layout/}
{<content}
<form class="ink-form column-group" action="/login" method="post">
<fieldset>
<legend>Login</legend>
<div class="control-group required">
<label for="user">Username / Email Address</label>
<div class="control">
<input type="text" name="user" id="user" required="required" />
</div>
<p class="tip">This field is required</p>
</div>
<div class="control-group required">
<label for="pass">Password</label>
<div class="control">
<input type="password" name="pass" id="pass" required="required" />
</div>
<p class="tip">This field is required</p>
</div>
<div class="control-group">
<input type="hidden" name="_csrf" value="{csrfToken}" />
<button type="submit">Login</button>
</div>
</fieldset>
</form>
{/content}