How to get 'ctx.state.user' in custom middleware

System Information
  • Strapi Version: 3.2.3
  • Operating System: alpine3.12
  • Database: postgres 13
  • Node Version: node 12.19
  • NPM Version: ?
  • Yarn Version:

Hello,

I am creating a middleware to add owner policy to image upload for a resource.

My problem is that I don’t have access to the ctx.state.user which seems to be set after my middleware.

Here is my middleware in ./middlewares/uploadPostImageOwnerPolicy/index.js

module.exports = strapi => {
  return {
    initialize() {
      strapi.app.use(async (ctx, next) => {
        console.log(ctx.request.headers, ctx.state.user)
      })
    },
  }
};

And here the configuration of the middleware in ./config/middleware.js:

module.exports = {
  load: {
    before: ['responseTime', 'logger', 'cors', 'responses'],
    order: [
      "Define the middlewares' load order by putting their name in this array in the right order",
    ],
    after: ['parser', 'router', 'uploadPostImageOwnerPolicy'],
  },
  settings: {
    uploadPostImageOwnerPolicy: {
      enabled: true,
    },
  },
};

And the result of that is that if I am calling the POST /upload endpoint I got:

{
  host: 'localhost:1337',
  connection: 'keep-alive',
  'content-length': '200',
  accept: 'application/json',
  authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ODMsImlhdCI6MTYwNTkwNDgzMSwiZXhwIjoxNjA4NDk2ODMxfQ.F6klpIRPY5TUQ8umkPl4jI-hpGmoJw2evcXCaXbiIu4',
  'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
  'content-type': 'application/json',
  origin: 'http://localhost:3000',
  'sec-fetch-site': 'same-site',
  'sec-fetch-mode': 'cors',
  'sec-fetch-dest': 'empty',
  referer: 'http://localhost:3000/',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8,fr;q=0.7'
} undefined

So you can see that I have an authorization token (and it’s a valid one because the POST /upload route is configured to be reached only if the user is authenticated).

How to run this middleware after the injection of the user in the state?

I’m a bit confused. So you want to build a middleware or policy? Cause these are different things.
From your message, I understand that you want to limit upload with a reference to another collection type only to creators? If not can you please explain in a bit more detail what you are trying to achieve.

If I am authorizing an user to upload this user can upload image for a resource even if he his not the owner of the resource.

To protect the route I need to add a middleware (and not override the upload route because it’s not like the content api. The route is really complex) to check that the user upload image to a resource that he is owner.

If there is a better solution I will take it.

Oh! I see that I can add policies in strapi, great news I will do that instead of a middleware :slight_smile:

Yes, policies is that you need, if you create a Middleware that is applied to all routes. But policies you can attach to individual routes.

1 Like

(Policies are basically scoped middlewares, they function almost exactly the same but can be ran before/after a handler/controller)

Where possible I recommend policies instead of middlewares, generally you would only use a middleware if you need it ran on every request.

1 Like

What do I need to do to make a policy run after the controller?

The rough break down of how a policy works:

module.exports = async (ctx, next) => {
  // Anything here runs before the controller
  await next();
  // Anything here runs after the controller
};

Simply removing the return part of the return await next() allows you to run them in a beforeX/afterX style. The general use-case for a policy is to restrict access or return errors to the user for advanced validation but they can be used for other purposes (like injecting headers to the response).

A good purpose I’ve seen for this is narrow scoped rate-limiting, where you only really want to rate limit a handful of endpoints.

1 Like