Inversifyjs: [inversify-express-utils]: Auth provider cannot rely on server-level middleware

Created on 9 Apr 2019  路  2Comments  路  Source: inversify/InversifyJS

Using the inversify-express-utils you can create an express application and configure:

  • an auth(orization) provider
  • server level middleware

When building the server application the http context is created before the server level middleware is registered.

Here's an excerpt of the build method.

this._app.all("*", (
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) => {
  (async () => {
      const httpContext = await _self._createHttpContext(req, res, next);
      ...
      next();
  })();
});

// register server-level middleware before anything else
if (this._configFn) {
  this._configFn.apply(undefined, [this._app]);
}

For each request the http context will be created, which in turn creates the principal. After that the server level middleware configured via the setConfig is executed.

 private async _createHttpContext(
   req: express.Request,
   res: express.Response,
   next: express.NextFunction
) {
  const principal = await this._getCurrentUser(req, res, next);
  ...
}

Let's assume a use case where you have to read data from the body in the authorization provider. If you configure the body-parser via the setConfig it's too late. This is called after the principal is created. When trying to compose a principal (getCurrentUser) you cannot ready the body as it is undefined at the time. The same probably applies for cookies as this requires the cookie-parser middleware to be setup.

Most helpful comment

I just ran into this issue trying to grab the access token from the headers of a CORS request in my
AuthProvider when I have CORS middleware set in the config.

To clarify, I have the following in my AuthProvider:

@injectable()
export default class PlayerAuthProvider implements interfaces.AuthProvider {
    async getUser(req: Request, res: Response, next: NextFunction): Promise<interfaces.Principal> {
        let accessTokenFromClient = req.headers["x-auth-token"];

which is missing the header, therefore returns 401 for a CORS request, given the following setup that uses setConfig:

const server = new InversifyExpressServer(container, null, null, null, PlayerAuthProvider);
server.setConfig((app) => {
    app.use(cors({origin: true}));
    app.options('*', cors());
    app.use(bodyParser.json());
});

However, it goes on to successfully validate the token and populate the Principal, using the following setup that configures an express app and passes it as an argument:

const app = express();
app.use(cors({origin: true}));
app.options('*', cors());
app.use(bodyParser.json());
const server = new InversifyExpressServer(container, null, null, app, PlayerAuthProvider);

I have found that re-arranging the code in the build (as mentioned by @geersch) to setup the config first solves the issue, as shown below:

// register server-level middleware before anything else
if (this._configFn) {
  this._configFn.apply(undefined, [this._app]);
}

this._app.all("*", (
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) => {
  (async () => {
      const httpContext = await _self._createHttpContext(req, res, next);
      ...
      next();
  })();
});

I was thinking about opening a PR with this fix, but wanted to search it in the issues first, at which point, I ended up here.

I'd still be happy to submit a PR, but I wanted to at least post on here first and see if there's any reply. I'm especially curious whether this is a valid solution, or rather if there is a legitimate reason that the http context is created before the server middleware is registered. Don't wanna break anything; this library rocks! 馃槃鉂わ笍

All 2 comments

yep, I've faced with the same issue

I just ran into this issue trying to grab the access token from the headers of a CORS request in my
AuthProvider when I have CORS middleware set in the config.

To clarify, I have the following in my AuthProvider:

@injectable()
export default class PlayerAuthProvider implements interfaces.AuthProvider {
    async getUser(req: Request, res: Response, next: NextFunction): Promise<interfaces.Principal> {
        let accessTokenFromClient = req.headers["x-auth-token"];

which is missing the header, therefore returns 401 for a CORS request, given the following setup that uses setConfig:

const server = new InversifyExpressServer(container, null, null, null, PlayerAuthProvider);
server.setConfig((app) => {
    app.use(cors({origin: true}));
    app.options('*', cors());
    app.use(bodyParser.json());
});

However, it goes on to successfully validate the token and populate the Principal, using the following setup that configures an express app and passes it as an argument:

const app = express();
app.use(cors({origin: true}));
app.options('*', cors());
app.use(bodyParser.json());
const server = new InversifyExpressServer(container, null, null, app, PlayerAuthProvider);

I have found that re-arranging the code in the build (as mentioned by @geersch) to setup the config first solves the issue, as shown below:

// register server-level middleware before anything else
if (this._configFn) {
  this._configFn.apply(undefined, [this._app]);
}

this._app.all("*", (
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) => {
  (async () => {
      const httpContext = await _self._createHttpContext(req, res, next);
      ...
      next();
  })();
});

I was thinking about opening a PR with this fix, but wanted to search it in the issues first, at which point, I ended up here.

I'd still be happy to submit a PR, but I wanted to at least post on here first and see if there's any reply. I'm especially curious whether this is a valid solution, or rather if there is a legitimate reason that the http context is created before the server middleware is registered. Don't wanna break anything; this library rocks! 馃槃鉂わ笍

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AriaFallah picture AriaFallah  路  4Comments

asykes74 picture asykes74  路  4Comments

Deviad picture Deviad  路  3Comments

AlexanderKiriluyk picture AlexanderKiriluyk  路  4Comments

stjepangolemac picture stjepangolemac  路  5Comments