Serve multiple protected static folders using Express

Issue

I’m trying to write a very simple Express app, which is supposed to serve multiple static folders.

I have a root folder "stories" which contains multiple folders (story-1, story2, etc…). Each story folder contains static assets (scripts, CSS stylesheets, subpages…).

My users can unlock each of those stories, so each story folder must be protected. (If anyone tries to access http://backend/stories/story-1, it should give a 401 Forbidden).

My initial thought was to generate a one-time JWT upfront (like a signed url; not a bearer), add it to query params like http://backend/stories/story-1?jwt=the-jwt-token, then do some backend logic to test if the user has access to this content before serving it.

I tried fiddling with a basic express configuration + a custom authorization middleware :

Project structure :

...
-- /src
-- /stories   ⬅️ custom public folder
   -- /story-1 ⬅️ public but protected
      - index.html
      - /subpages
        -page2.html
        -page3.html
      - /styles
      - /scripts
   -- /story-2 ⬅️ public but protected
      - index.html
      - /subpages
        -page2.html
        -page3.html
      - /styles
      - /scripts
   -- /story-3 ⬅️ public but protected
      - index.html
      - /subpages
        -page2.html
        -page3.html
      - /styles
      - /scripts
etc...

index.js :

const express = require("express");

const { authorized } = require("./middlewares/authorized");
const app = express();
const port = 3000;

app.use("/stories/:story", authorized);
app.use("/stories", express.static(__dirname + "/stories"));

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

authorized.js :

exports.authorized = (req, res, next) => {
  const jwt = req.query.jwt;
  if (!jwt) return res.sendStatus(401);
  // todo : custom logic to test if the user has access to this content, if yes we do next(), if no we return a 401.
  return next();
};

This simple example works partially, when I try to go to http://localhost:3000/stories/first-story (without JWT), I get a 401 (that’s ok).

But when I add the jwt :
http://localhost:3000/stories/first-story/?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

enter image description here

The middleware runs for every assets that are linked in the index.html, but those assets urls don’t have the JWT query params, which leads to a 401.

I guess it’s totally normal because that’s how middlewares are intended to work. My guess is that i’m configuring express router wrong :

app.use("/stories/:story", authorized);
app.use("/stories", express.static(__dirname + "/stories"));

I would like to run the middleware only once, when any of the /:story subfolders inside /stories are asked to be served.

Solution

You write:

I would like to run the middleware only once, when any of the /:story subfolders inside /stories are asked to be served.

But if every .html (sub-)page is served by a separate HTTP request, each of these requests must be protected, assuming that the HTML contains material that is worthy of protection. (The styles and scripts may not need this extra protection.)

Therefore it is OK that the authorized middleware runs for each such request. And if the JWT was in a cookie (as suggested by Kaneki21), it would be present automatically in each request.

Answered By – Heiko Theißen

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published