fastify / fastify-passport

Use passport strategies for authentication within a fastify application
MIT License
256 stars 46 forks source link

Duplicate 'passport' decorator error when instantiating multiple Authenticator instances #1098

Closed thaDude closed 8 months ago

thaDude commented 9 months ago

Prerequisites

Fastify version

4.17.0

Plugin version

2.3.0

Node.js version

16.2

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

14.2.1

Description

Hello,

I followed instructions as per documentation but the Fastify server does not start up with the error quoted below:

{
    "level": 50,
    "time": 1706381991730,
    "pid": 19819,
    "hostname": "<redacted>",
    "err":
    {
        "type": "FastifyError",
        "message": "The decorator 'passport' has already been added!",
        "stack": "FastifyError: The decorator 'passport' has already been added!\n    at decorateConstructor (/Users/<redacted>/Work/<redacted>/node_modules/fastify/lib/decorate.js:41:11)\n    at Object.decorateRequest (/Users/<redacted>/Work/<redacted>/node_modules/fastify/lib/decorate.js:123:3)\n    at /Users/<redacted>/Work/<redacted>/node_modules/@fastify/passport/dist/CreateInitializePlugin.js:10:17\n    at Plugin.exec (/Users/<redacted>/Work/<redacted>/node_modules/avvio/plugin.js:130:19)\n    at Boot.loadPlugin (/Users/<redacted>/Work/<redacted>/node_modules/avvio/plugin.js:272:10)\n    at processTicksAndRejections (node:internal/process/task_queues:83:21)",
        "name": "FastifyError",
        "code": "FST_ERR_DEC_ALREADY_PRESENT",
        "statusCode": 500
    },
    "msg": "The decorator 'passport' has already been added!"
}

Best regards,

David

Steps to Reproduce

'use strict'

const fastify = require('fastify')({
    disableRequestLogging: true,
    logger: true,
    level: 'info'
});

const {Authenticator} = require('@fastify/passport');

const fastifyCookie = require('@fastify/cookie');
const fastifySession = require('@fastify/session');
fastify.register(fastifyCookie);
fastify.register(fastifySession, {
    secret: '6b8de8c5-2440-4903-8157-595adeaed92e',
    cookie: { secure: false },
    expires: 1800000
})

const _username1='user1';
const _password1 = 'password1';

const _username2 = 'user2';
const _password2 = 'password2';

const LocalStrategy = require('passport-local');

let passport1 = new Authenticator({ key: 'userAuth1', userProperty: 'user1' })
passport1.use('local-1', new LocalStrategy({},
    function (username, password, done) {
        if (username === _username1 && password == _password1) {
            return done(null, {username});
        }
        return done('local-1: You got this wrong');
    }));
fastify.register(passport1.initialize());
fastify.register(passport1.secureSession());

let passport2 = new Authenticator({ key: 'userAuth2', userProperty: 'user2' })
passport2.use('local-2', new LocalStrategy({},
    function (username, password, done) {
        if (username === _username2 && password == _password2) {
            return done(null, {username});
        }
        return done('local-2: You got this wrong');
    }));
fastify.register(passport2.initialize());
fastify.register(passport2.secureSession());

passport1.registerUserSerializer(async (user, request) => user);
passport1.registerUserDeserializer(async (serUser, request) => {
    return {...serUser}
});

passport2.registerUserSerializer(async (user, request) => user);
passport2.registerUserDeserializer(async (serUser, request) => {
    return {...serUser}
});

fastify.post("/login-password-1", {
    preValidation: passport1.authenticate('local-1', {
        failureMessage: true,
        failWithError: true,
    })
}, async function (request, reply) {
    console.log(request.user)
    reply.code(200).send({...request.user1})
})

fastify.post("/login-password-2", {
    preValidation: passport2.authenticate('local-2', {
        failureMessage: true,
        failWithError: true,
    })
}, async function (request, reply) {
    console.log(request.user)
    reply.code(200).send({...request.user2})
})

fastify.post("/auth-test", {
}, async function (request, reply) {
    console.log(request.user)
    reply.code(200).send({...request.user})
})

fastify.listen({ port: 3000, host: '0.0.0.0' }, function (err, address) {
    if (err) {
        fastify.log.error(err)
        process.exit(1)
    }
})

Expected Behavior

The fastify server should start up and I should have 2 authentication stacks.

mcollina commented 9 months ago

You'd need to encapsulate the two instances in separate plugins.

thaDude commented 9 months ago

Thank you for your reply Matteo and sorry for the n00b question but how do I do this? Any examples are appreciated. I am using fastify-passport in a commonJS setting.

/David

mcollina commented 8 months ago

Something like:


fastify.register(async (fastify) => {
const LocalStrategy = require('passport-local');

let passport1 = new Authenticator({ key: 'userAuth1', userProperty: 'user1' })
passport1.use('local-1', new LocalStrategy({},
    function (username, password, done) {
        if (username === _username1 && password == _password1) {
            return done(null, {username});
        }
        return done('local-1: You got this wrong');
    }));
fastify.register(passport1.initialize());
fastify.register(passport1.secureSession());

fastify.post("/login-password-1", {
    preValidation: passport1.authenticate('local-1', {
        failureMessage: true,
        failWithError: true,
    })
}, async function (request, reply) {
    console.log(request.user)
    reply.code(200).send({...request.user1})
})

})

Do the same for passport2