rewrite: start adding facebook auth. note, this isn't going to work properly due to issues outlined in http://stackoverflow.com/questions/14572600/passport-js-restful-auth . gotta figure something out here...

This commit is contained in:
Tyler Renelle 2013-09-03 23:15:43 -04:00
parent e43740e939
commit ceae8bf768
10 changed files with 352 additions and 254 deletions

View file

@ -50,9 +50,18 @@ habitrpg.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$loca
}); });
}; };
function errorAlert(data, status, headers, config) {
if (status === 0) {
alert("Server not currently reachable, try again later");
} else if (!!data && !!data.err) {
alert(data.err);
} else {
alert("ERROR: " + status);
}
}
$scope.auth = function() { $scope.auth = function() {
var data; var data = {
data = {
username: $scope.loginUsername, username: $scope.loginUsername,
password: $scope.loginPassword password: $scope.loginPassword
}; };
@ -62,18 +71,17 @@ habitrpg.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$loca
$http.post(API_URL + "/api/v1/user/auth/local", data) $http.post(API_URL + "/api/v1/user/auth/local", data)
.success(function(data, status, headers, config) { .success(function(data, status, headers, config) {
runAuth(data.id, data.token); runAuth(data.id, data.token);
}).error(function(data, status, headers, config) { }).error(errorAlert);
if (status === 0) {
alert("Server not currently reachable, try again later");
} else if (!!data && !!data.err) {
alert(data.err);
} else {
alert("ERROR: " + status);
}
});
} }
}; };
$scope.facebookAuth = function(){
$http.get('/auth/facebook')
.success(function(data){
runAuth(data.id, data.token);
}).error(errorAlert);
}
$scope.playButtonClick = function(){ $scope.playButtonClick = function(){
if (User.authenticated()) { if (User.authenticated()) {
window.location.href = '/#/tasks'; window.location.href = '/#/tasks';

View file

@ -7,7 +7,7 @@
"habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#rewrite", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#rewrite",
"derby-auth": "git://github.com/lefnire/derby-auth#master", "derby-auth": "git://github.com/lefnire/derby-auth#master",
"connect-mongo": "*", "connect-mongo": "*",
"passport-facebook": "*", "passport-facebook": "~1.0.0",
"express": "*", "express": "*",
"gzippo": "*", "gzippo": "*",
"guid": "*", "guid": "*",
@ -28,7 +28,8 @@
"connect-assets": "~2.5.2", "connect-assets": "~2.5.2",
"bower": "~1.2.4", "bower": "~1.2.4",
"nib": "~1.0.1", "nib": "~1.0.1",
"jade": "~0.35.0" "jade": "~0.35.0",
"passport": "~0.1.17"
}, },
"private": true, "private": true,
"subdomain": "habitrpg", "subdomain": "habitrpg",

219
src/controllers/auth.js Normal file
View file

@ -0,0 +1,219 @@
var passport = require('passport');
var _ = require('lodash');
var User = require('../models/user').model;
var api = module.exports;
var NO_TOKEN_OR_UID = { err: "You must include a token and uid (user id) in your request"};
var NO_USER_FOUND = {err: "No user found."};
/*
beforeEach auth interceptor
*/
api.auth = function(req, res, next) {
var token, uid;
uid = req.headers['x-api-user'];
token = req.headers['x-api-key'];
if (!(uid && token)) {
return res.json(401, NO_TOKEN_OR_UID);
}
return User.findOne({
_id: uid,
apiToken: token
}, function(err, user) {
if (err) {
return res.json(500, {
err: err
});
}
if (_.isEmpty(user)) {
return res.json(401, NO_USER_FOUND);
}
res.locals.wasModified = +user._v !== +req.query._v;
res.locals.user = user;
req.session.userId = user._id;
return next();
});
};
api.registerUser = function(req, res, next) {
var confirmPassword, e, email, password, username, _ref;
_ref = req.body, email = _ref.email, username = _ref.username, password = _ref.password, confirmPassword = _ref.confirmPassword;
if (!(username && password && email)) {
return res.json(401, {
err: ":username, :email, :password, :confirmPassword required"
});
}
if (password !== confirmPassword) {
return res.json(401, {
err: ":password and :confirmPassword don't match"
});
}
try {
validator.check(email).isEmail();
} catch (_error) {
e = _error;
return res.json(401, {
err: e.message
});
}
return async.waterfall([
function(cb) {
return User.findOne({
'auth.local.email': email
}, cb);
}, function(found, cb) {
if (found) {
return cb("Email already taken");
}
return User.findOne({
'auth.local.username': username
}, cb);
}, function(found, cb) {
var newUser, salt, user;
if (found) {
return cb("Username already taken");
}
newUser = helpers.newUser(true);
salt = utils.makeSalt();
newUser.auth = {
local: {
username: username,
email: email,
salt: salt
}
};
newUser.auth.local.hashed_password = derbyAuthUtil.encryptPassword(password, salt);
user = new User(newUser);
return user.save(cb);
}
], function(err, saved) {
if (err) {
return res.json(401, {
err: err
});
}
return res.json(200, saved);
});
};
/*
Register new user with uname / password
*/
api.loginLocal = function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
async.waterfall([
function(cb) {
if (!(username && password)) return cb('No username or password');
User.findOne({'auth.local.username': username}, cb);
}, function(user, cb) {
if (!user) return cb('Username not found');
// We needed the whole user object first so we can get his salt to encrypt password comparison
User.findOne({
'auth.local.username': username,
'auth.local.hashed_password': utils.encryptPassword(password, user.auth.local.salt)
}, cb);
}
], function(err, user) {
if (!user) err = 'Incorrect password';
if (err) return res.json(401, {err: err});
res.json(200, {
id: user._id,
token: user.apiToken
});
});
};
/*
POST /user/auth/facebook
*/
api.loginFacebook = function(req, res, next) {
var email, facebook_id, name, _ref;
_ref = req.body, facebook_id = _ref.facebook_id, email = _ref.email, name = _ref.name;
if (!facebook_id) {
return res.json(401, {
err: 'No facebook id provided'
});
}
return User.findOne({
'auth.local.facebook.id': facebook_id
}, function(err, user) {
if (err) {
return res.json(401, {
err: err
});
}
if (user) {
return res.json(200, {
id: user.id,
token: user.apiToken
});
} else {
/* FIXME: create a new user instead*/
return res.json(403, {
err: "Please register with Facebook on https://habitrpg.com, then come back here and log in."
});
}
});
};
/*
Registers a new user. Only accepting username/password registrations, no Facebook
*/
api.setupPassport = function(router) {
router.get('/logout', function(req, res) {
req.logout();
delete req.session.userId;
res.redirect('/');
})
// GET /auth/facebook
// Use passport.authenticate() as route middleware to authenticate the
// request. The first step in Facebook authentication will involve
// redirecting the user to facebook.com. After authorization, Facebook will
// redirect the user back to this application at /auth/facebook/callback
router.get('/auth/facebook',
passport.authenticate('facebook'),
function(req, res){
// The request will be redirected to Facebook for authentication, so this
// function will not be called.
});
// GET /auth/facebook/callback
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
router.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
//res.redirect('/');
User.findOne({'auth.facebook.id':req.user.id}, function(err, user){
if (err) return res.json(500, {err:err});
if (!user) return res.json(401, {err: "New Facebook registrations aren't yet supported, only existing Facebook users. Help us code this!"});
res.json({id: user._id, token: user.apiToken});
})
});
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
// function ensureAuthenticated(req, res, next) {
// if (req.isAuthenticated()) { return next(); }
// res.redirect('/login')
// }
};

View file

@ -1,19 +1,14 @@
var api, deprecatedMessage, express, icalendar, initDeprecated, router, _; var express = require('express');
var router = new express.Router();
express = require('express'); var _ = require('lodash');
var icalendar = require('icalendar');
router = new express.Router(); var api = require('./user');
var auth = require('./auth');
_ = require('lodash');
icalendar = require('icalendar');
api = require('./user');
/* ---------- Deprecated Paths ------------*/ /* ---------- Deprecated Paths ------------*/
deprecatedMessage = 'This API is no longer supported, see https://github.com/lefnire/habitrpg/wiki/API for new protocol'; var deprecatedMessage = 'This API is no longer supported, see https://github.com/lefnire/habitrpg/wiki/API for new protocol';
router.get('/:uid/up/:score?', function(req, res) { router.get('/:uid/up/:score?', function(req, res) {
return res.send(500, deprecatedMessage); return res.send(500, deprecatedMessage);
@ -30,13 +25,13 @@ router.post('/users/:uid/tasks/:taskId/:direction', function(req, res) {
/* Redirect to new API*/ /* Redirect to new API*/
initDeprecated = function(req, res, next) { var initDeprecated = function(req, res, next) {
req.headers['x-api-user'] = req.params.uid; req.headers['x-api-user'] = req.params.uid;
req.headers['x-api-key'] = req.body.apiToken; req.headers['x-api-key'] = req.body.apiToken;
return next(); return next();
}; };
router.post('/v1/users/:uid/tasks/:taskId/:direction', initDeprecated, api.auth, api.scoreTask); router.post('/v1/users/:uid/tasks/:taskId/:direction', initDeprecated, auth.auth, api.scoreTask);
router.get('/v1/users/:uid/calendar.ics', function(req, res) { router.get('/v1/users/:uid/calendar.ics', function(req, res) {
/*return next() #disable for now*/ /*return next() #disable for now*/

View file

@ -18,44 +18,6 @@ var User = require('./../models/user').model;
var Group = require('./../models/group').model; var Group = require('./../models/group').model;
var api = module.exports; var api = module.exports;
/*
------------------------------------------------------------------------
Misc
------------------------------------------------------------------------
*/
var NO_TOKEN_OR_UID = { err: "You must include a token and uid (user id) in your request"};
var NO_USER_FOUND = {err: "No user found."};
/*
beforeEach auth interceptor
*/
api.auth = function(req, res, next) {
var token, uid;
uid = req.headers['x-api-user'];
token = req.headers['x-api-key'];
if (!(uid && token)) {
return res.json(401, NO_TOKEN_OR_UID);
}
return User.findOne({
_id: uid,
apiToken: token
}, function(err, user) {
if (err) {
return res.json(500, {
err: err
});
}
if (_.isEmpty(user)) {
return res.json(401, NO_USER_FOUND);
}
res.locals.wasModified = +user._v !== +req.query._v;
res.locals.user = user;
req.session.userId = user._id;
return next();
});
};
/* /*
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -430,73 +392,6 @@ api.buy = function(req, res, next) {
------------------------------------------------------------------------ ------------------------------------------------------------------------
*/ */
/*
Registers a new user. Only accepting username/password registrations, no Facebook
*/
api.registerUser = function(req, res, next) {
var confirmPassword, e, email, password, username, _ref;
_ref = req.body, email = _ref.email, username = _ref.username, password = _ref.password, confirmPassword = _ref.confirmPassword;
if (!(username && password && email)) {
return res.json(401, {
err: ":username, :email, :password, :confirmPassword required"
});
}
if (password !== confirmPassword) {
return res.json(401, {
err: ":password and :confirmPassword don't match"
});
}
try {
validator.check(email).isEmail();
} catch (_error) {
e = _error;
return res.json(401, {
err: e.message
});
}
return async.waterfall([
function(cb) {
return User.findOne({
'auth.local.email': email
}, cb);
}, function(found, cb) {
if (found) {
return cb("Email already taken");
}
return User.findOne({
'auth.local.username': username
}, cb);
}, function(found, cb) {
var newUser, salt, user;
if (found) {
return cb("Username already taken");
}
newUser = helpers.newUser(true);
salt = utils.makeSalt();
newUser.auth = {
local: {
username: username,
email: email,
salt: salt
}
};
newUser.auth.local.hashed_password = derbyAuthUtil.encryptPassword(password, salt);
user = new User(newUser);
return user.save(cb);
}
], function(err, saved) {
if (err) {
return res.json(401, {
err: err
});
}
return res.json(200, saved);
});
};
/* /*
Get User Get User
*/ */
@ -515,72 +410,6 @@ api.getUser = function(req, res, next) {
return res.json(200, user); return res.json(200, user);
}; };
/*
Register new user with uname / password
*/
api.loginLocal = function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
async.waterfall([
function(cb) {
if (!(username && password)) return cb('No username or password');
User.findOne({'auth.local.username': username}, cb);
}, function(user, cb) {
if (!user) return cb('Username not found');
// We needed the whole user object first so we can get his salt to encrypt password comparison
User.findOne({
'auth.local.username': username,
'auth.local.hashed_password': utils.encryptPassword(password, user.auth.local.salt)
}, cb);
}
], function(err, user) {
if (!user) err = 'Incorrect password';
if (err) return res.json(401, {err: err});
res.json(200, {
id: user._id,
token: user.apiToken
});
});
};
/*
POST /user/auth/facebook
*/
api.loginFacebook = function(req, res, next) {
var email, facebook_id, name, _ref;
_ref = req.body, facebook_id = _ref.facebook_id, email = _ref.email, name = _ref.name;
if (!facebook_id) {
return res.json(401, {
err: 'No facebook id provided'
});
}
return User.findOne({
'auth.local.facebook.id': facebook_id
}, function(err, user) {
if (err) {
return res.json(401, {
err: err
});
}
if (user) {
return res.json(200, {
id: user.id,
token: user.apiToken
});
} else {
/* FIXME: create a new user instead*/
return res.json(403, {
err: "Please register with Facebook on https://habitrpg.com, then come back here and log in."
});
}
});
};
/* /*
Update user Update user
FIXME add documentation here FIXME add documentation here

View file

@ -2,18 +2,18 @@ var express = require('express');
var router = new express.Router(); var router = new express.Router();
var user = require('../controllers/user'); var user = require('../controllers/user');
var groups = require('../controllers/groups'); var groups = require('../controllers/groups');
var auth = require('../controllers/auth');
/* /*
---------- /api/v1 API ------------ ---------- /api/v1 API ------------
Every url added to router is prefaced by /api/v1 Every url added to router is prefaced by /api/v1
See ./routes/coffee for routes See ./routes/coffee for routes
v1 user. Requires x-api-user (user id) and x-api-key (api key) headers, Test with: v1 user. Requires x-api-user (user id) and x-api-key (api key) headers, Test with:
$ cd node_modules/racer && npm install && cd ../.. $ cd node_modules/racer && npm install && cd ../..
$ mocha test/user.mocha.coffee $ mocha test/user.mocha.coffee
*/ */
var auth = user.auth
var verifyTaskExists = user.verifyTaskExists var verifyTaskExists = user.verifyTaskExists
var cron = user.cron; var cron = user.cron;
@ -23,50 +23,52 @@ router.get('/status', function(req, res) {
}); });
}); });
/* Auth*/ /* auth.auth*/
router.post('/register', user.registerUser); auth.setupPassport(router); //FIXME make this consistent with the others
router.post('/register', auth.registerUser);
router.post('/user/auth/local', auth.loginLocal);
router.post('/user/auth/facebook', auth.loginFacebook);
/* Scoring*/ /* Scoring*/
router.post('/user/task/:id/:direction', auth, cron, user.scoreTask); router.post('/user/task/:id/:direction', auth.auth, cron, user.scoreTask);
router.post('/user/tasks/:id/:direction', auth, cron, user.scoreTask); router.post('/user/tasks/:id/:direction', auth.auth, cron, user.scoreTask);
/* Tasks*/ /* Tasks*/
router.get('/user/tasks', auth, cron, user.getTasks); router.get('/user/tasks', auth.auth, cron, user.getTasks);
router.get('/user/task/:id', auth, cron, user.getTask); router.get('/user/task/:id', auth.auth, cron, user.getTask);
router.put('/user/task/:id', auth, cron, verifyTaskExists, user.updateTask); router.put('/user/task/:id', auth.auth, cron, verifyTaskExists, user.updateTask);
router.post('/user/tasks', auth, cron, user.updateTasks); router.post('/user/tasks', auth.auth, cron, user.updateTasks);
router["delete"]('/user/task/:id', auth, cron, verifyTaskExists, user.deleteTask); router["delete"]('/user/task/:id', auth.auth, cron, verifyTaskExists, user.deleteTask);
router.post('/user/task', auth, cron, user.createTask); router.post('/user/task', auth.auth, cron, user.createTask);
router.put('/user/task/:id/sort', auth, cron, verifyTaskExists, user.sortTask); router.put('/user/task/:id/sort', auth.auth, cron, verifyTaskExists, user.sortTask);
router.post('/user/clear-completed', auth, cron, user.clearCompleted); router.post('/user/clear-completed', auth.auth, cron, user.clearCompleted);
/* Items*/ /* Items*/
router.post('/user/buy/:type', auth, cron, user.buy); router.post('/user/buy/:type', auth.auth, cron, user.buy);
/* User*/ /* User*/
router.get('/user', auth, cron, user.getUser); router.get('/user', auth.auth, cron, user.getUser);
router.post('/user/auth/local', user.loginLocal); router.put('/user', auth.auth, cron, user.updateUser);
router.post('/user/auth/facebook', user.loginFacebook); router.post('/user/revive', auth.auth, cron, user.revive);
router.put('/user', auth, cron, user.updateUser); router.post('/user/batch-update', auth.auth, cron, user.batchUpdate);
router.post('/user/revive', auth, cron, user.revive); router.post('/user/reroll', auth.auth, cron, user.reroll);
router.post('/user/batch-update', auth, cron, user.batchUpdate); router.post('/user/buy-gems', auth.auth, user.buyGems);
router.post('/user/reroll', auth, cron, user.reroll);
router.post('/user/buy-gems', auth, user.buyGems);
/* Groups*/ /* Groups*/
router.get('/groups', auth, groups.getGroups); router.get('/groups', auth.auth, groups.getGroups);
router.post('/groups', auth, groups.createGroup); router.post('/groups', auth.auth, groups.createGroup);
//TODO: //TODO:
//GET /groups/:gid (get group) //GET /groups/:gid (get group)
//PUT /groups/:gid (edit group) //PUT /groups/:gid (edit group)
//DELETE /groups/:gid //DELETE /groups/:gid
router.post('/groups/:gid/join', auth, groups.attachGroup, groups.join); router.post('/groups/:gid/join', auth.auth, groups.attachGroup, groups.join);
router.post('/groups/:gid/leave', auth, groups.attachGroup, groups.leave); router.post('/groups/:gid/leave', auth.auth, groups.attachGroup, groups.leave);
router.post('/groups/:gid/invite', auth, groups.attachGroup, groups.invite); router.post('/groups/:gid/invite', auth.auth, groups.attachGroup, groups.invite);
//GET /groups/:gid/chat //GET /groups/:gid/chat
router.post('/groups/:gid/chat', auth, groups.attachGroup, groups.postChat); router.post('/groups/:gid/chat', auth.auth, groups.attachGroup, groups.postChat);
//PUT /groups/:gid/chat/:messageId //PUT /groups/:gid/chat/:messageId
//DELETE /groups/:gid/chat/:messageId //DELETE /groups/:gid/chat/:messageId

11
src/routes/auth.js Normal file
View file

@ -0,0 +1,11 @@
var auth = require('../controllers/auth');
var express = require('express');
var router = new express.Router();
/* auth.auth*/
auth.setupPassport(router); //FIXME make this consistent with the others
router.post('/register', auth.registerUser);
router.post('/user/auth/local', auth.loginLocal);
router.post('/user/auth/facebook', auth.loginFacebook);
module.exports = router;

View file

@ -19,11 +19,6 @@ router.get('/partials/options', function(req, res) {
res.render('options'); res.render('options');
}); });
router.get('/logout', function(req, res) {
delete req.session.userId;
res.redirect('/');
})
// -------- Marketing -------- // -------- Marketing --------
router.get('/splash.html', function(req, res) { router.get('/splash.html', function(req, res) {

View file

@ -9,11 +9,11 @@ var middleware = require('./middleware');
var server; var server;
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14; var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
// Setup configurations // ------------ Setup configurations ------------
require('./config'); require('./config');
require('./errors'); require('./errors');
// MongoDB Configuration // ------------ MongoDB Configuration ------------
mongoose = require('mongoose'); mongoose = require('mongoose');
require('./models/user'); //load up the user schema - TODO is this necessary? require('./models/user'); //load up the user schema - TODO is this necessary?
require('./models/group'); require('./models/group');
@ -22,12 +22,50 @@ mongoose.connect(nconf.get('NODE_DB_URI'), function(err) {
console.info('Connected with Mongoose'); console.info('Connected with Mongoose');
}); });
/**
Server Configuration // ------------ Passport Configuration ------------
*/ var passport = require('passport')
var util = require('util')
var FacebookStrategy = require('passport-facebook').Strategy;
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing. However, since this example does not
// have a database of user records, the complete Facebook profile is serialized
// and deserialized.
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// all environments // Use the FacebookStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Facebook
// profile), and invoke a callback with a user object.
passport.use(new FacebookStrategy({
clientID: nconf.get("FACEBOOK_KEY"),
clientSecret: nconf.get("FACEBOOK_SECRET"),
callbackURL: nconf.get("BASE_URL") + "/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
//process.nextTick(function () {
// To keep the example simple, the user's Facebook profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Facebook account with a user record in your database,
// and return that user instead.
return done(null, profile);
//});
}
));
// ------------ Server Configuration ------------
app.set("port", nconf.get('PORT')); app.set("port", nconf.get('PORT'));
app.set("views", __dirname + "/../views"); app.set("views", __dirname + "/../views");
app.set("view engine", "jade"); app.set("view engine", "jade");
@ -43,6 +81,12 @@ app.use(express.cookieSession({ secret: nconf.get('SESSION_SECRET'), httpOnly: f
//app.use(express.session()); //app.use(express.session());
app.use(middleware.splash); app.use(middleware.splash);
app.use(middleware.locals); app.use(middleware.locals);
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router); app.use(app.router);
app.use(express['static'](path.join(__dirname, "/../public"))); app.use(express['static'](path.join(__dirname, "/../public")));
@ -53,6 +97,7 @@ if ("development" === app.get("env")) {
// Custom Directives // Custom Directives
app.use(require('./routes/pages').middleware); app.use(require('./routes/pages').middleware);
app.use(require('./routes/auth').middleware);
app.use('/api/v1', require('./routes/api').middleware); app.use('/api/v1', require('./routes/api').middleware);
app.use(require('./controllers/deprecated').middleware); app.use(require('./controllers/deprecated').middleware);
server = http.createServer(app).listen(app.get("port"), function() { server = http.createServer(app).listen(app.get("port"), function() {
@ -68,24 +113,17 @@ module.exports = server;
# #
# #
#expressApp #expressApp
# .use(middleware.allowCrossDomain)
# .use(express.favicon("#{publicPath}/favicon.ico")) # .use(express.favicon("#{publicPath}/favicon.ico"))
# # Gzip static files and serve from memory # # Gzip static files and serve from memory
# .use(gzippo.staticGzip(publicPath, maxAge: ONE_YEAR)) # .use(gzippo.staticGzip(publicPath, maxAge: ONE_YEAR))
# # Gzip dynamically rendered content # # Gzip dynamically rendered content
# .use(express.compress()) # .use(express.compress())
# .use(middleware.translate) # .use(middleware.translate)
# .use(middleware.view)
# .use(auth.middleware(strategies, options)) # .use(auth.middleware(strategies, options))
# # Creates an express middleware from the app's routes
# .use(app.router())
# .use(require('./static').middleware)
# .use(expressApp.router)
# .use(serverError(root)) # .use(serverError(root))
# #
# #
## Errors ## Errors
#expressApp.all '*', (req) -> #expressApp.all '*', (req) ->
# throw "404: #{req.url}" # throw "404: #{req.url}"
*/ */

View file

@ -25,8 +25,8 @@ block content
button.close(type='button', data-dismiss='modal', aria-hidden='true') × button.close(type='button', data-dismiss='modal', aria-hidden='true') ×
h4.modal-title Login / Register h4.modal-title Login / Register
.modal-body .modal-body
//a(href='/auth/facebook') //-a(ng-click='facebookAuth()')
img(src='/img/facebook-login-register.jpeg', alt='Login / Register With Facebook') img(src='/bower_components/habitrpg-shared/img/facebook-login-register.jpeg', alt='Login / Register With Facebook')
//h3 Or //h3 Or
ul.nav.nav-tabs ul.nav.nav-tabs
li.active li.active