File: //var/dev/farhangmoaser/web/routes/api/1.0/auth.js
/**
* Express endpoints for /auth address
* Version: 0.1
* Author: Babak Vandad
*
* Restful api for addresses:
* GET /auth
* GET /auth/local
* GET /auth/local/fail
* GET /auth/app
* GET /auth/app/fail
* GET /auth/google
* GET /auth/google/return
* GET /auth/landing
* GET /auth/google/fail
*/
var path = require('path');
var express = require('express');
var passport = require('passport');
var db = require(path.join(BASEDIR, 'connectors/mysql'));
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var LocalStrategy = require('passport-local').Strategy;
var LocalAPIKeyStrategy = require('passport-localapikey').Strategy;
var authHelper = require(path.join(BASEDIR, 'helpers/auth'));
var router = express.Router();
var UserModel = require(path.join(BASEDIR, 'models/user'));
var AppModel = require(path.join(BASEDIR, 'models/app'));
var config = require(path.join(BASEDIR, 'config.js'));
var consts = require(path.join(BASEDIR, 'consts.js'));
var GOOGLE_CLIENT_ID = "184976576455-riqlc7naqqke24trp0jqu79klo0u1onj.apps.googleusercontent.com";
var GOOGLE_CLIENT_SECRET = "PfptydZsKhv_j3361AFMGt3H";
var SCOPE_GOOGLE_PLUS = 'https://www.googleapis.com/auth/plus.login';
var SCOPE_GOOGLE_USERINFO = 'https://www.googleapis.com/auth/userinfo.email';
/**
* Fail a request by releasing database connection, rollback transaction
* and sending the proper error code and message with 4xx or 5xx status codes.
* @param {object} express response object
* @param {object} error object from costs.js
* @param {integer} override status code (if you want to send 200 instead of 4xx/5xx)
* @return {function} the function to fail the request.
*/
var fail = function(response, error, status){
return function(){
response.status(status ? status : error.status).json({error: error.code, message: error.message});
};
};
/* loading google strategy for passport */
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: config.read('client.hostname')+"/auth/google/return"
}, function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
return done(null, profile);
});
}
));
/* loading local user/pass strategy for passport */
passport.use(new LocalStrategy({usernameField: 'email', passwordField: 'password'},
function(email, password, done) {
db.getConnection(function(err, conn){
if(err) return done(null, false, err);
UserModel.authenticate(conn, email, password).then(
user => done(null, user),
errmsg => done(null, false, {message: errmsg})
);
});
}
));
/* loading local api key strategy for passport */
passport.use(new LocalAPIKeyStrategy({apiKeyField: 'key'},
function(apikey, done) {
db.getConnection(function(err, conn){
if(err) return done(null, false, err);
var app = new AppModel(conn, {key: apikey});
app.load().then(
function () {
return done(null, app);
}, function(errmsg) {
return done(null, false, {message: errmsg});
}
);
});
}
));
passport.serializeUser(authHelper.serialize);
passport.deserializeUser(authHelper.deserialize);
/* login page */
router.get('/', function(req, res){
if(req.isAuthenticated()) {
var target = config.read('client.path');
if(req.query.hasOwnProperty('target'))
target = req.query.target;
res.redirect(target);
}else {
if(req.query.hasOwnProperty('login_target'))
req.session.login_target = req.query.target;
res.redirect(config.read('client.path')+'/auth/google');
}
});
/* login with username/password */
router.get('/local',
passport.authenticate('local', {failureRedirect: config.read('client.path')+'api/1.0/auth/local/fail'}),
function(req, res) {
let userData = req.user.data;
delete userData.id;
delete userData.password;
delete userData.searchable;
delete userData.sortable;
delete userData.activationKey;
let subscriptions = req.user.subscriptions.map(subscription => ({
book: subscription.book,
book_title: subscription.book_title,
dateExpire: subscription.dateExpire,
dateStart: subscription.dateStart,
state: subscription.state
}));
return res.json({token: req.user.token, user: {data: userData, subscriptions: subscriptions, token: req.user.token}});
}
/* failure message */
).get('/local/fail', function(req, res){
fail(res, consts.e.ERR_AUTH)();
});
/* login with api key */
router.get('/app',
passport.authenticate('localapikey', {session: false, failureRedirect: config.read('client.path')+'api/1.0/auth/app/fail'}),
function(req, res) {
authHelper.serialize(req.user, function(err, token){
if(err) return fail(res, consts.e.ERR_SERIALIZATION_ERROR)();
else{
var userData = req.user.data;
delete userData.id;
delete userData.password;
delete userData.searchable;
delete userData.sortable;
delete userData.activationKey;
return res.json({token: token, app: {data: userData, token: req.user.token}});
}
})
}
/* failure message */
).get('/local/fail', function(req, res){
fail(res, consts.e.ERR_AUTH)();
});
/* login with google account */
router.get('/google', passport.authenticate('google', {scope: [SCOPE_GOOGLE_PLUS, SCOPE_GOOGLE_USERINFO]}));
/* callback from google */
router.get('/google/return',
passport.authenticate('google', {failureFlash: true, failureRedirect: config.read('client.path')+'auth/google/fail' }),
function(req, res) {
res.redirect(config.read('client.path')+'/auth/landing');
});
/* after successfull login */
router.get('/landing',
function(req, res, next) {
var target = '/';
if(req.session.hasOwnProperty('login_target')) {
target = req.session.login_target;
delete req.session.login_target;
}
if(req.isAuthenticated())
res.render('auth/landing', {userdata: req.user, target: target});
else
res.send('not authenticated.');
});
/* failure message */
router.get('/google/fail',
function(req, res, next) {
var errmessage = '';
if(req.session.hasOwnProperty('errmessage'))
errmessage = req.session.errmessage;
res.render('auth/google_failure', {message: errmessage});
}
);
module.exports = router;