Reputation: 7
I’m fairly new to Fastify, and I’m using it to build an API. I'm having some trouble when using an authentication decorator in a route, which is throwing the following error: "code":"FST_ERR_HOOK_INVALID_HANDLER","name":"FastifyError","statusCode":500},"msg":"preHandler hook should be a function, instead got [object Undefined]".
I've set up a decorator file at @/decorators/authentication.ts
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import fjwt, { FastifyJWT } from "@fastify/jwt";
import fCookie from "@fastify/cookie";
import { env } from "@/config/constants"; // Ensure the correct path
export default async function authentication(app: FastifyInstance) {
// Register JWT plugin
app.register(fjwt, { secret: String(env.JWT_SECRET_KEY) });
// Register cookies plugin
app.register(fCookie, {
secret: env.JWT_SECRET_KEY,
hook: "preHandler",
});
// Decorate app with the authenticate function
app.decorate(
"authenticate",
async (req: FastifyRequest, reply: FastifyReply) => {
try {
const token = req.cookies.access_token;
if (!token) {
return reply.status(401).send({ message: "Authentication required" });
}
// Verify the token and set the user in the request object
const decoded = req.jwt.verify<FastifyJWT['user']>(token)
req.user = decoded;
} catch (err) {
return reply.status(401).send({ message: "Invalid or expired token" });
}
}
);
}
and using it on @/modules/authentication/authentication.routes.ts
import { FastifyInstance } from "fastify";
import AuthenticationController from "./authentication.controller";
import UserService from "@/data-services/user-service";
import { $ref, userSchemas } from "./authentication.schema";
export default async function authRoutes(fastify: FastifyInstance) {
// Decorate fastify with the user service
fastify.decorate("userService", new UserService());
// Register schemas
userSchemas.forEach((schema) => fastify.addSchema(schema));
const authController = new AuthenticationController(fastify.userService);
// Login route
fastify.post(
"/login",
{
schema: {
body: $ref("loginSchema"),
response: {
201: $ref("loginResponseSchema"),
},
},
},
authController.login.bind(authController)
);
// Register route
fastify.post(
"/registro",
{
schema: {
body: $ref("registerUserSchema"),
response: {
201: $ref("registerUserResponseSchema"),
},
},
},
authController.register.bind(authController)
);
// Get current user route with preHandler for authentication
fastify.get(
"/me",
{
preHandler: [fastify.authenticate],
},
authController.getCurrentUser.bind(authController)
);
}
I set up my server.ts file as this
import Fastify from "fastify";
import { env } from "./config/constants";
import "./types";
import authentication from "@/decorators/authentication";
import routes from "./config/routes";
const app = Fastify({
logger: true,
});
const listeners = ["SIGINT", "SIGTERM"];
listeners.forEach((signal) => {
process.on(signal, async () => {
await app.close();
process.exit(0);
});
});
app.register(authentication);
app.register(routes);
app.listen(
{
port: env.PORT,
},
(err, address) => {
if (err) {
app.log.error(err);
process.exit(1);
}
app.log.info(`server listening on ${address}, PORT: ${env.PORT}`);
}
);
I'm logging a temporary handler on the /me route, but fastify.authenticate is returning as undefined. Can anyone explain why this is happening?
packages versions
"@fastify/cookie": "^10.0.1",
"@fastify/jwt": "^9.0.1",
"fastify": "^5.0.0",
"fastify-zod": "^1.4.0",
also already checked these solutions related stack overflow topic Github related issue but didnot found an answer in any of these
I was trying to authenticate the user using the decorator method i created before, but seems to not work, since my routes complains that authenticate is undefined. I have already registered the authentication decorator before the routes and added logs to confirm that the decorators are being registered properly before the routes.
Upvotes: 0
Views: 125
Reputation: 12900
The issue is that fastify.authenticate
returns undefined
This is because the export default async function authentication(app: FastifyInstance)
plugin does not use the fastify-plugin
module, so when it is registered app.register(authentication);
it creates its own context that does not effect the routes
one.
To fix it you need to wrap the function:
import fp from 'fastify-plugin'
export default fp(
async function authentication(app: FastifyInstance) { ... }
)
Useful readings:
Upvotes: 0