7
votes

I am currently working on a authentication service for a node.js microservices application using typescript, WebStorm, passport and jwt. While trying to add the route to "/api/login", I am noticing that the intellisense does not seem to pick up the user object of req.user or the authorization object of req.header.authorization. For example, the following method is not working because it can not find the user object:

private generateToken(req: Request, res: Response, next: NextFunction){
req.token = jwt.sign({
  id: req.user.id,
  firstname: req.user.firstname,
  lastname: req.user.lastname,
  roles: req.user.roles
}, process.env.AUTH_KEY, {
  expiresIn: "7d"
});
return next();
}

I am using the Request object from express:

import { NextFunction, Request, Response, Router } from "express";

Would I need to use a different Request object?

Also, if I need to force authentication to certain api routes but lock other routes down, how should this be done using passport-jwt? I know there is an express-unless package that I can use for express-jwt.

1
Are you getting an error when running this code, or is your question about intellisense? Javascript cannot be easily statically analyzed so there are many things that intellisense cannot pick up - Joe Frambach
It is turning red for the intellisense and when I mouseover I am getting "Unresolved variable user". I thought since I am using typescript, it would find the variable. - user1790300
I don't think intellisense is going to be that smart, sorry to disappoint. This is all still new and some parts are still uncharted. Part of working with new technology :) - Joe Frambach

1 Answers

7
votes

Not sure why this question downvoted, maybe because it should be two separate questions.

You can extend type declarations for Express.

Extending the express type definitions

Add a file library-ext.d.ts into your source directory with this.

declare module 'express' {
  export interface Request {
        user?: any;
    }
}

For req.header.authorization try: req.headers['authorization']. Notice the 's'.

Relating to general authentication

It depends whether your registered users can also use the guest routes. If you never need identity on the guest routes then just register the passport authentication middleware only on the authenticated routes, or split the routes into separate routers. That's fairly simple to do, just search stack overflow or look in the docs for it.

The more complicated case is when you need both authenticated and non-authenticated users to access a route - think either a guest or authenticated customer adding something to a cart. Unfortunately passport-jwt rejects with a 401 when a token is not in the authorzation header, so the easiest way I found, rather than forking the project or rolling my own strategy, was to use middleware to add a known value to represent an otherwise anonymous request. Then just make sure that middleware is before the passport authentication in the affected routes. Here's a snippet to get you going:

CoreCtrl

class CoreCtrl {

  simulateAnonymous(req, res, next) {
    if (!req.headers.authorization) {
      req.headers.authorization = 'Bearer guest-token';
    }
    return next();
  }

}

Then somewhere in your Express Setup

setupRouters() {
    // the public and admin routers are bound to the application
    const coreCtrl = new CoreCtrl(this.serverOpts);
    const anonymousCtrl = coreCtrl.simulateAnonymous.bind(coreCtrl);
    this.routers.admin.use(anonymousCtrl);
    this.routers.admin.use(passport.authenticate('UserBearer', { session: false }));
    this.routers.public.use(anonymousCtrl);
    this.routers.public.use(passport.authenticate('CustomerBearer', { session: false }));
  }

Note that I had separate routers for public and admin set up here, that's not necessary but just to illustrate how to do it.

Then in the bearer strategy, you would have some code similar to this.

/**
* Run the strategy
*
* @param token {String} The JWT Token
* @param done {Callback} Callback function
*/
exec(token:string, done):Promise<any> {
  // this is the workaround to support not passing a token for guest users.
  if (token === 'guest-token') {
    return done(null, {
      userId: 'guest',
      roles: ['guest']
    });
  }
  // otherwise decode the token and find the user.
}

Finally, in some later Middleware you can check if the 'guest' role has access to the protected resource. I'd recommend acl module to manage role-based ACL list.