File: /var/dev/nowruzgan/id/app.js
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const jwt = require("jsonwebtoken");
const LocalStrategy = require('passport-local');
const GoogleStrategy = require('passport-google-oauth20');
// const crypto = require('crypto');
const md5 = require('md5');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const fs = require('fs');
const config = JSON.parse(fs.readFileSync(`./config-${process.env.CONFIG || 'prod'}.json`));
const JWT_PRIVATE = fs.readFileSync('./cert/private.pem');
const JWT_PUBLIC = fs.readFileSync('./cert/public.pem');
var app = express();
app.set('config', config);
app.use(cookieParser());
app.set('trust proxy', 1);
app.use(session({
    secret: config.sessionSecret,
    saveUninitialized: true,
    resave: false,
    cookie: {secure: true}
  }));
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
let verifyToken = async token => {
  let conn = app.get('conn');
  if(!token) return false;
  try {
    token = jwt.verify(token, JWT_PUBLIC);
    if(!token.id)
      return false
    let rows = await conn.query(`select * from user where id = ? and state='active' limit 1`, [token.id]);
    if(!rows.length)
      return false;
    if(token.email != rows[0].email)
       return false
  }catch(error) {
    return false;
  }
  return true;
};
var isLogged = async (req, res, next) => {
  let token = req.cookies.token;
  let verified = await verifyToken(token);
  if(verified)
    next();
  else
    res.redirect('/');
};
passport.serializeUser(function(user, done) {
  done(null, JSON.stringify(user));
});
passport.deserializeUser(function(user, done) {
  done(null, JSON.parse(user));
});
passport.use(new LocalStrategy({usernameField: 'email', passReqToCallback: true}, async (req, email, password, cb) => {
  let conn = app.get('conn');
  email = email.toLowerCase();
  let rows = await conn.query(`select * from user where email = ? limit 1`, [email]);
  if(!rows.length) return cb(null, false);
  let user = rows[0];
  if(user.password != md5(password)) return cb(null, false);
  user.token = jwt.sign({
    id: user.id,
    email: user.email,
    firstName: user.first_name,
    lastName: user.last_name
  }, JWT_PRIVATE, {algorithm: 'RS256'});
  req.user = user;
  return cb(null, user);
}));
passport.use(new GoogleStrategy({
    clientID: config.google.clientId,
    clientSecret: config.google.clientSecret,
    callbackURL: 'https://id.nowruzgan.com/r1/g-cb',
    passReqToCallback: true
  }, async (req, accessToken, refreshToken, profile, done) => {
    let conn = app.get('conn');
    let email = profile.emails[0].value.toLowerCase();
    let rows = await conn.query(`select * from user where email = ? and state='active' limit 1;`, [email]);
    let user = {};
    if(!rows.length) {
      let firstName = profile.name.givenName || '';
      let lastName = profile.name.familyName || '';
      let result = await conn.query(`insert into user (email, first_name, last_name, state) values (?, ?, ?, 'active');`, [email, firstName, lastName]);
      user = {
        id: result.insertId,
        email, firstName, lastName
      };
    }else {
      user = {
        id: rows[0].id,
        email: rows[0].email,
        firstName: rows[0].first_name,
        lastName: rows[0].last_name,
      };
    }
  
    user.token = jwt.sign(user, JWT_PRIVATE, {algorithm: 'RS256'});
    if(req.session.lastQuery?.redirect) {
      user.redirect = req.session.lastQuery.redirect;
      req.session.lastQuery = false;
    }
    return done(null, user);
}));
app.get('/', async (req, res, next) => {
  let token = req.cookies.token;
  if(token) {
    let verified = await verifyToken(token);
    if(!verified)
      return res.redirect('/logout');
  }else
    return next();
  if(req.query.redirect){
    if(/\/[^/]*\?/.exec(req.query.redirect))
      res.redirect(req.query.redirect + `&token=${token}`);
    else
      res.redirect(req.query.redirect + `?token=${token}`);
  }else
    res.redirect('/logged.html');
});
app.get('/logged.html', isLogged, (req, res, next) => {
  next();
});
app.get('/r1/login/google',
  (req, res, next) => {
    req.session.lastQuery = req.query;
    return next();
  },
  passport.authenticate('google', {scope: ['email', 'profile']}));
app.get('/r1/g-cb', passport.authenticate('google', {failureRedirect: '/'}), (req, res, next) => {
  res.cookie('token', req.user.token, {maxAge: 1000*30*24*3600});
  if(req.user.redirect){
    if(/\/[^/]*\?/.exec(req.user.redirect))
      res.redirect(req.user.redirect + `&token=${req.user.token}`);
    else
      res.redirect(req.user.redirect + `?token=${req.user.token}`);
  }else
    res.redirect('/logged.html');
});
app.post('/r1/login/local', passport.authenticate('local', { session: false }), (req, res, next) => {
  res.cookie('token', req.user.token, {maxAge: 1000*30*24*3600});
  res.json({token: req.user.token});
});
app.get('/logout', (req, res, next) => {
  res.clearCookie('token');
  if(req.query.redirect)
    res.redirect(req.query.redirect);
  else
    res.redirect('/');
});
app.use(express.static(path.join(__dirname, 'public')));
module.exports = app;