0

i've been struggling a lot with fastify-passport library, mainly because it seems that nobody uses it and there aren't any good examples or issues related to it

anyhow, i have some routes defined like this:

const adminRoutes = [
  {
    handler: (req,res) => {console.log(req.user) },
    url: "/logout",
    method: "GET"
  }
]

this route is then registered by fastify like this (do note that there are more routes, however, this is just a code snippet)

adminRoutes.forEach((route, index) => {
  fastify.route(route)
})

i am using passport local strategy to autenticate, it's configured like this

fastifyPassport.use('login', new passportLocal(async function (username, password, done) {
  try {
    let data = await dbQuery(`SELECT * FROM \`users\` WHERE username="${username}"`)
    if (data[0].length > 0) {
      data = data[0][0]
      if (username === data.username && bCrypt.compareSync(password, data.hashedPassword)) {
          return done(null, username)
        } else return done(null, false)
      }
  } catch (error) {
    console.log(error)
      done(error);
    }
}))

this seems to work, in all my tests, the strategy did what it had to do, when the right user and password is passed all the checks seems to pass and gets all the way down to return done(null, username)

this is my serializer and deserializer

fastifyPassport.registerUserSerializer(async (user, request) => {
  return user
});
fastifyPassport.registerUserDeserializer(async (username, request) => {
  let data = await dbQuery(`SELECT * FROM users WHERE username="${username}"`);
  return data[0][0]
});

i've checked with both a debugger and console logs, they don't seem to ever get called

in fact when i go to /logout my console throws a null

also no session cookie get generated (uncertain of this, sometimes it seems to generate, other times it doesn't)

the complete code is quite long, however, it's probably necessary to see whats the issue

so here is it

this is the server

require('dotenv').config()
const fastify = require('fastify')({ logger: false })
const fastifyPassport = require('fastify-passport')
const fastifySecureSession = require('fastify-secure-session')
const passportLocal = require('passport-local').Strategy
const BannedEverywhere = ["DROP", "CREATE"]
const bCrypt = require('bcryptjs')
const fs = require('fs')
const path = require('path')
const port = process.env.PORT || 3000
const routes = require('./routes')
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: process.env.DB_PASSWD,
  database: 'users',
  port: 3306
});
console.log("Server Connected!")
function dbQuery(dataExpression) {
  if (BannedEverywhere.some(i => dataExpression.includes(i))) {
    return "invalid"
  }
  return pool.query(dataExpression)
}
fastify.register(require('fastify-cors'), {
  origin: (origin, cb) => {
    cb(null, true);
    return
  }
})
fastify.register(fastifySecureSession, { key: fs.readFileSync(path.join(__dirname, 'secret-key'))})
fastify.register(fastifyPassport.initialize())
fastify.register(fastifyPassport.secureSession())
fastifyPassport.registerUserSerializer(async (user, request) => {
  return user
});
fastifyPassport.registerUserDeserializer(async (username, request) => {
  let data = await dbQuery(`SELECT * FROM users WHERE username="${username}"`);
  return data[0][0]
});
fastifyPassport.use('login', new passportLocal(async function (username, password, done) {
  try {
    let data = await dbQuery(`SELECT * FROM \`users\` WHERE username="${username}"`)
    if (data[0].length > 0) {
      data = data[0][0]
      if (username === data.username && bCrypt.compareSync(password, data.hashedPassword)) {
          console.log("got here")
          return done(null, username)
        } else return done(null, false)
      }
  } catch (error) {
    console.log(error)
      done(error);
    }
}))

const postData = async (req, reply, err, user, info, status) => {
  if (err !== null) { console.warn("ah habido un error: " + err) }
  else if (user) {
    const params = req.body
    const newData = {
      universidad: params.universidad,
      facultad: params.facultad,
      nombreExamen: params.nombreExamen,
      fechaExamen: params.fechaExamen,
      convocatoriaEspecial: params.convocatoriaEspecial,
      convocatoriaExtraordinaria: params.convocatoriaExtraordinaria,
      curso: params.curso
    }
    const response = await dbQuery('INSERT INTO `examenes` VALUES("'
      + newData.universidad + '","' + newData.facultad + '","'
      + newData.nombreExamen + '","' + newData.fechaExamen + '",'
      + newData.convocatoriaEspecial + ',' + newData.convocatoriaExtraordinaria + ','
      + newData.curso + ")")
    return reply.send({ status: 200, newData, response })
  }
}
const deleteData = async (req, reply, err, user, info, status) => {
  if (err !== null) { console.warn("ah habido un error: " + err) }
  else if (user) {
    const { universidad, facultad, nombreExamen, fechaExamen, curso } = req.body
    const response = await dbQuery('DELETE FROM `examenes` WHERE universidad="' + universidad + '" and facultad="' + facultad + '" and nombreExamen="' + nombreExamen + '" and date(fechaExamen)="' + fechaExamen + '" and curso=' + curso)
    return reply.send({ status: 200, response })
  }
}
const logout = async (req, reply, err, user, info, status) => {
  if (err !== null) { console.warn("ah habido un error: " + err) }
  console.log(req)
  console.log("--------------------------------")
  console.log(user)
  console.log("--------------------------------")
  console.log(info)
  console.log("--------------------------------")
  console.log(status)
}

fastify.get(
  "/login",
  (req, reply) => {
    return reply.sendFile('./login/index.html')
  }
)

fastify.post(
  "/login",
  {preValidation: fastifyPassport.authenticate('login',(req, reply)=>{
    reply.send({redirect: "/"})
  })},
  () => {}
)

const adminRoutes = [
  {
    handler: () => {},
    preValidation: fastifyPassport.authenticate("login", deleteData),
    url: '/api/deleteData',
    method: 'POST'
  },
  {
    handler: () => {},
    preValidation: fastifyPassport.authenticate("login", postData),
    url: '/api/postData',
    method: 'POST'
  },
  {
    handler: () => {},
    preValidation: fastifyPassport.authenticate("login", (req, reply) => { return reply.sendFile('./entry/index.html') }),
    url: '/entry',
    method: 'GET'
  },
  {
    handler: (req,res) => {console.log(req.user) },
    url: "/logout",
    method: "GET"
  }
]

const start = async () => {
  try {
    await fastify.listen(port)
  } catch (err) {
    console.error(err)
    process.exit(1)
  }
}

fastify.register(require('fastify-static'), {
  root: __dirname,
  prefix: '/', // optional: default '/'
})
routes.forEach((route, index) => {
  fastify.route(route)
})
adminRoutes.forEach((route, index) => {
  fastify.route(route)
})

start()

before you comment, i know cors shouldn't be left like that to go in production, don't worry, i know

also logout function was just a test, to try solve this very issue

this is the code that calls the login post request

        const HOST = location.origin;
        const axiosApp = axios.create({
            baseURL: HOST,
        });
        document
            .querySelector("#submit")
            .addEventListener("click", async function () {
                let response = await axiosApp.post(`${HOST}/login`, {
                    username: document.querySelector("#username").value,
                    password: document.querySelector("#password").value,
                });
                console.log(response);
                if(response.status == 200) {
                    document.location.href = response.data.redirect
                }
            });

unrelated, bannedEverywhere is just a rudimentary "security" check, i do plan to improve it, for now it's just a better than nothing, and, i do plan to change all the var + string + var chains with template strings

M1S0
  • 29
  • 7
  • In passport, do not mix callback (done) with async await – Manuel Spigolon Dec 05 '21 at 20:52
  • could you clarify the to what line are you refering to also, why is it not reccomendable to mix async await with the callback(done) could you point me to any documentation about that – M1S0 Dec 05 '21 at 22:21

1 Answers1

0

answering my own question:

when you add a callback like that in the preValidation

{preValidation: fastifyPassport.authenticate('login',(req, reply)=>{
    reply.send({redirect: "/"})
  })}

fastify-passport no longer handles serialization and deserialization on his own, that comes with... a lot more issues, changing the prevalidation to

{preValidation: fastifyPassport.authenticate('login'{successRedirect:"/"})

makes you have to handle 302 codes in the browser, which can be problematic to say the least

M1S0
  • 29
  • 7