forked from alexanderscott/backbone-login
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.js
172 lines (136 loc) · 6.07 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
* Node Express app server with API endpoints
* --------------------------------------------
* Creates a SQLite3 schema and users table for inserting, updating, and deleting
* user records. Passwords are salted and a combined salt+hash is stored in the db.
* Also signs and unsigns cookies which it uses to persist the client's authentication.
*
*/
var express = require('express'),
http = require('http'),
config = require("./config"),
bcrypt = require("bcrypt"),
sqlite = require("sqlite3"),
_ = require("underscore"),
app = express(),
port = process.env.PORT || config.port,
server = http.createServer(app).listen(port);
// Initialize sqlite and create our db if it doesnt exist
var sqlite3 = require("sqlite3").verbose();
var db = new sqlite3.Database(__dirname+'/db/bb-login.db');
// Create our users table if it doesn't exist
db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, username TEXT UNIQUE, password TEXT, auth_token TEXT UNIQUE)");
// Allow node to be run with proxy passing
app.enable('trust proxy');
// Logging config
app.configure('local', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
// Compression (gzip)
app.use( express.compress() );
app.use( express.methodOverride() );
app.use( express.urlencoded() ); // Needed to parse POST data sent as JSON payload
app.use( express.json() );
// Cookie config
app.use( express.cookieParser( config.cookieSecret ) ); // populates req.signedCookies
app.use( express.cookieSession( config.sessionSecret ) ); // populates req.session, needed for CSRF
// We need serverside view templating to initially set the CSRF token in the <head> metadata
// Otherwise, the html could just be served statically from the public directory
app.set('view engine', 'html');
app.set('views', __dirname + '/views' );
app.engine('html', require('hbs').__express);
app.use(express.static(__dirname+'/public'));
app.use(express.csrf());
app.use( app.router );
app.get("/", function(req, res){
res.render('index', { csrfToken: req.csrfToken() });
});
// GET /api/auth
// @desc: checks a user's auth status based on cookie
app.get("/api/auth", function(req, res){
db.get("SELECT * FROM users WHERE id = ? AND auth_token = ?", [ req.signedCookies.user_id, req.signedCookies.auth_token ], function(err, user){
if(user){
res.json({ user: _.omit(user, ['password', 'auth_token']) });
} else {
res.json({ error: "Client has no valid login cookies." });
}
});
});
// POST /api/auth/login
// @desc: logs in a user
app.post("/api/auth/login", function(req, res){
db.get("SELECT * FROM users WHERE username = ?", [ req.body.username ], function(err, user){
if(user){
// Compare the POSTed password with the encrypted db password
if( bcrypt.compareSync( req.body.password, user.password)){
res.cookie('user_id', user.id, { signed: true, maxAge: config.cookieMaxAge });
res.cookie('auth_token', user.auth_token, { signed: true, maxAge: config.cookieMaxAge });
// Correct credentials, return the user object
res.json({ user: _.omit(user, ['password', 'auth_token']) });
} else {
// Username did not match password given
res.json({ error: "Invalid username or password." });
}
} else {
// Could not find the username
res.json({ error: "Username does not exist." });
}
});
});
// POST /api/auth/signup
// @desc: creates a user
app.post("/api/auth/signup", function(req, res){
db.serialize(function(){
db.run("INSERT INTO users(username, name, password, auth_token) VALUES (?, ?, ?, ?)",
[ req.body.username, req.body.name, bcrypt.hashSync(req.body.password, 8), bcrypt.genSaltSync(8) ], function(err, rows){
if(err){
res.json({ error: "Username has been taken.", field: "username" });
} else {
// Retrieve the inserted user data
db.get("SELECT * FROM users WHERE username = ?", [ req.body.username ], function(err, user){
if(!user) {
console.log(err, rows);
res.json({ error: "Error while trying to register user." });
} else {
// Set the user cookies and return the cleansed user data
res.cookie('user_id', user.id, { signed: true, maxAge: config.cookieMaxAge });
res.cookie('auth_token', user.auth_token, { signed: true, maxAge: config.cookieMaxAge });
res.json({ user: _.omit(user, ['password', 'auth_token']) });
}
});
}
});
});
});
// POST /api/auth/logout
// @desc: logs out a user, clearing the signed cookies
app.post("/api/auth/logout", function(req, res){
res.clearCookie('user_id');
res.clearCookie('auth_token');
res.json({ success: "User successfully logged out." });
});
// POST /api/auth/remove_account
// @desc: deletes a user
app.post("/api/auth/remove_account", function(req, res){
db.run("DELETE FROM users WHERE id = ? AND auth_token = ?", [ req.signedCookies.user_id, req.signedCookies.auth_token ], function(err, rows){
if(err){
res.json({ error: "Error while trying to delete user." });
} else {
res.clearCookie('user_id');
res.clearCookie('auth_token');
res.json({ success: "User successfully deleted." });
}
});
});
// Close the db connection on process exit
// (should already happen, but to be safe)
process.on("exit", function(){
db.close();
});
console.log("STARTUP:: Express server listening on port::", port, ", environment:: ", app.settings.env);