patrickpissurno / fastify-esso

The easiest authentication plugin for Fastify, with built-in support for single sign-on (SSO)
https://npm.im/fastify-esso
MIT License
52 stars 6 forks source link

Cannot access generateAuthToken from Controller #9

Closed bnaambo closed 3 years ago

bnaambo commented 3 years ago

Hi I have a issue with Esso in controllers.

this is my setup

server.js

const server = require("fastify")();

const opts = {
    secret: process.env.tokensecret , 
    extra_validation: async function validation (req, reply){ 
        if(moment.unix(req.auth.expires).isBefore(moment()))
            return { expired: true };
    }
};

server.register(require("fastify-esso")(opts));

server.register(require("./routes/login"));

const start = async () =>{
    try {
        await server.listen(process.env.API_PORT, "0.0.0.0");
    } catch (error) {
        server.log.error(error);
    }
};

start();

/routes/login.js

const {doLogin} = require("../../controllers/login");

function loginRoutes(server, options, done){

    server.post("/api/login", {}, doLogin);
    done();
};

module.exports = loginRoutes;

/controllers/login.js

const doLogin = async (req, res) => {

if (!req.body) {
        res.code(406).send({
            code: "406",
            message: "No login information provided!"
        });
    };

const { usr, pass } = req.body;

const wristband = await this.generateAuthToken({ user: user });
res.code(200).send({
        code: "200",
        message: "Successfully logged in!",
        payload: wristband
    });
};

module.exports = {
    doLogin
};

now I get the error "this.generateAuthToken is not a function" what do I do wrong

patrickpissurno commented 3 years ago

So, thanks for trying out fastify-esso.

Basically, the issue is your 'this' object in your controller code seems not to be a fastify instance. The generateAuthToken method is present only in the fastify instance (in your login.js file that's the 'server' parameter). So you need to pass it somehow to your controller code.

However, if I may, I'd recommend you to take a look at the async-await part of the Fastify docs (here) and also at the getting started section (here), as it offers some good insights on a more fastify-like way of doing things (e.g. how to better structure your project, avoid using callbacks and instead replace them with async-await, etc).

But to make things short, instead of creating a separate file for each 'controller' (handler), I'd suggest you to create a separate plugin for each route (so, one route per file) and register them as plugins (with prefixes).

It may sound complicated, but in fact it's quite simple. Starting off your example:

server.js

const moment = require('moment');
const server = require('fastify')();

const opts = {
    secret: process.env.tokensecret , 
    extra_validation: async function validation (req, reply){ 
        if(moment.unix(req.auth.expires).isBefore(moment()))
            return { expired: true };
    }
};

server.register(require('fastify-esso')(opts));

server.register(require('./routes/routes'), { prefix: 'api' });

const start = async () =>{
    try {
        await server.listen(process.env.API_PORT, '0.0.0.0');
    } catch (error) {
        server.log.error(error);
    }
};

start();

/routes/routes.js

/** @param {import('fastify').FastifyInstance} fastify */
module.exports = async function routes(fastify){
    fastify.register(require('./login'), { prefix: 'login' });
}

/routes/login.js

const err = require('http-errors');

/** @param {import('fastify').FastifyInstance} fastify */
module.exports = async function routes(fastify){

    fastify.post('/', async (req, reply) => {
        if(!req.body)
            throw new err.NotAcceptable('No login information provided!');

        const { usr, pass } = req.body;

        // you should validate against your database to make sure the user exists
        // and that their password is correct

        const user = { usr };

        const wristband = await fastify.generateAuthToken({ user: user });
        return {
            code: '200',
            message: 'Successfully logged in!',
            payload: wristband
        };
    });

}

I agree that their docs, although quite good, could use some improvements, and I may write a full step-by-step tutorial on how to get started with fastify + fastify-esso in the future (plus the project structure that I use and recommend).

PS: those magic comments in the code above are not necessary. They're just there to help VSCode (and other IDEs) to offer code completion.

In the mean time, feel free to ask questions, and I'll try my best to help you out in a timely manner.

Cheers.

bnaambo commented 3 years ago

thank u for the fast response. I moved the handler out of the controller and instead handle the function directly at the routes that way I can access it like u shown in ur example. now it works well. thank u so much for your help Keep up the good work