2

Passing a middleware to authenticate user before accessing this route.

When I'm passing tokenController.authUser as a middleware tokenService inside tokenController is undefined. However when I run this method as a function inside the route instead of a middleware it works fine.

server.post('/api/admin/test', { preHandler: [tokenController.authUser] }, async (request: any, reply: any) => { return null }); 

Token Controller :-

import { Users } from "@prisma/client"; import ITokenService from "../../services/tokenService/ITokenService"; import ITokenController from "./ITokenController"; export default class TokenController implements ITokenController { private readonly tokenService: ITokenService; constructor(_tokenService: ITokenService) { this.tokenService = _tokenService; } async authUser(request: any, reply: any): Promise<Users | Error> { const authHeader = request.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (token === null) return reply.code(401); try { const result = await this.tokenService.verifyToken(token); console.log(result); return result; } catch (e) { reply.code(401); return new Error("Error"); } } } 

Token Service :-

import { Users } from "@prisma/client"; import ITokenService from "./ITokenService"; export default class TokenService implements ITokenService { private readonly sign: Function; private readonly verify: Function; private readonly secretKey: string; constructor(sign: Function, verify: Function, _secretKey: string) { this.sign = sign; this.verify = verify; this.secretKey = _secretKey; } public async generateToken(user: Users): Promise<string> { return await this.sign({ user }, this.secretKey); } public async verifyToken(token: string): Promise<Users | Error> { const result = await this.verify(token, this.secretKey); return result; } } 
4
  • HOw do you instantiate the tokenController var? Commented Jan 25, 2022 at 15:05
  • All objects are being instantiated in a container, instantiating isn't a problem because it works fine when not passed as a middleware Commented Jan 26, 2022 at 1:47
  • export default class TokenController requires to run new TokenController() somewhere Commented Jan 26, 2022 at 7:53
  • 1
    Did that in another file called container.js. All objects have been instantiated! Commented Jan 27, 2022 at 4:25

2 Answers 2

5

For some reason making a separate middleware function and calling tokenController.authUser inside that method works fine.

const middleware = (_req, _res, next) => { console.log('middleware'); next() } server.post('/api/admin/test', { preHandler: [middleware] }, async (request: any, reply: any) => { return null }); 
Sign up to request clarification or add additional context in comments.

Comments

1

This is a common problem with methods of classes.

When you pass tokenController.authUser around, you are passing a lone function which is disconnected from the tokenController instance that it came from. As a result, when it is called, the object that this usually points to is missing (or more likely, replaced with globalThis), so this.mostThings will be undefined.

To clarify:

// Works as expected, because when we call a function this way, the // JavaScript engine sets this=tokenController during the call tokenController.authUser(...) const authUser = tokenController.authUser // Does not work, because no "this" can be set, so the function body // cannot "see" tokenController authUser(...) 

The common solution would be to keep the authUser function bound to the tokenController object:

server.post('/api/admin/test', { preHandler: [tokenController.authUser.bind(tokenController)] }, ...) 

Another would be to call tokenController.authUser() directly every time:

function boundAuthUser(...args) { return tokenController.authUser(...args) } server.post('/api/admin/test', { preHandler: [boundAuthUser] }, ...) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.