How to pass JWT token from Header Set-Cookie to Headers Authorization: Bearer token

System Information
  • Strapi Version: 4.1**
  • Operating System: Windows 11
  • Database: Postgress
  • Node Version:
  • NPM Version:
  • Yarn Version:

I wanted roles of users so I modified the response by adding extensions/strapi-server.js

// path: src/extensions/users-permissions/strapi-server.js
module.exports = (plugin) => {
  const sanitizeOutput = (user) => {
    const {
      password,
      resetPasswordToken,
      confirmationToken,
      ...sanitizedUser
    } = user; // be careful, you need to omit other private attributes yourself
    return sanitizedUser;
  };

  plugin.controllers.user.me = async (ctx) => {
    if (!ctx.state.user) {
      return ctx.unauthorized();
    }
    const user = await strapi.entityService.findOne(
      "plugin::users-permissions.user",
      ctx.state.user.id,
      {
        populate: {
          role: {
            fields: ["type"],
          },
        },
      }
    );
    ctx.body = sanitizeOutput(user);
  };
  return plugin;
};

So it looks like this, now to add cookie from Server Side i.e. HTTP Only, I added my custom endpoint (" /auth/login") in
src/api/custom

const axios = require("axios");
module.exports = {
  // GET /auth/cookielogin
  async index(ctx) {
    // Capture the request body (identifier and password)
    const { body } = ctx.request;
    // Build Strapi's Absolute Server URL.
    // Copied from https://github.com/strapi/strapi/blob/86e0cf0f55d58e714a67cf4daee2e59e39974dd9/packages/strapi-utils/lib/config.js#L62
    const hostname = "localhost";
    // const hostname =
    //   strapi.config.environment === "development" &&
    //   ["127.0.0.1", "0.0.0.0"].includes(strapi.config.server.host)
    //     ? "localhost"
    //     : strapi.config.server.host;
    const absoluteURL = `http://${hostname}:${strapi.config.server.port}`;
    const sanitizeOutput = (user) => {
      const {
        password,
        resetPasswordToken,
        confirmationToken,
        ...sanitizedUser
      } = user; // be careful, you need to omit other private attributes yourself
      return sanitizedUser;
    };
    try {
      console.log("Tryin to login");
      // Now submit the credentials to Strapi's default login endpoint
      let { data } = await axios.post(`${absoluteURL}/api/auth/local`, body);
      const populatedUser = await strapi.entityService.findOne(
        "plugin::users-permissions.user",
        data.user.id,
        {
          populate: {
            role: {
              fields: ["type"],
            },
          },
        }
      );
      data.user = sanitizeOutput(populatedUser);
      // Set the secure cookie
      if (data && data.jwt) {
        ctx.cookies.set("jwt", data.jwt, {
          httpOnly: true,
          secure: false,
          maxAge: 1000 * 60 * 60 * 24 * 14, // 14 Day Age
          domain: "localhost",
        });
      }
      // Respond with the jwt + user data, but now this response also sets the JWT as a secure cookie
      return ctx.send(data);
    } catch (error) {
      console.log("An error occurred:", error.response);
      return ctx.badRequest(null, error);
    }
  }
};

Now from my Frontend, I am calling /auth/login, which returns just and user and sets the cookie from server i.e. HTTP Only, Now When I am calling /users/me, it says UnAuthorized

I know what could be the problem is,
all protected endpoints in strapi take Authorization: Bearer token Headers but in this, it’s automatically passed with Set-Cookie, thus for strapi endpoints no headers are passed.

the solution could be, somethings from which before the request is hit to the /users/me, Cookie from Headers are taken then placed in Authorization and then hit the API, all this in the backend

Something like this mentioned here,
How to put JWT’s in server-side cookies using the Strapi user-permissions plugin
on the Modify Auth Handler

I tried but it seems this permissions.js and all mentioned in the article is of v3.** as reference links mentioned there are of v3.

So after doing some research I found what to do for this.
So basically, I setup-ed a middleware whose work was to get the jwt token from header cookies and set Authorization: Bearer token

Answer

Create custom middleware

./src/middlewares/TokenPlacer.js
(Filename can be of your choice)

module.exports = () => {
  return async (ctx, next) => {
    const cookies = ctx.request.header.cookie || false;
    if (cookies) {
      let token = cookies
        .split(";")
        .find((c) => c.trim().startsWith("jwt="))
        .split("=")[1];
      if (token) {
        ctx.request.header.authorization = `Bearer ${token}`;
      }
    }
    await next();
  };
};

Here I have saved my JWT token as jwt in the cookie so change it accordingly!

Load the custom middleware

Then locate the middleware file in ./config/middleware.js
If you haven’t used any middleware or customized anything then it should look like this

module.exports = [
  "strapi::errors",
  "strapi::security",
  "strapi::cors",
  "strapi::poweredBy",
  "strapi::logger",
  "strapi::query",
  "strapi::body",
  "strapi::session",
  "strapi::favicon",
  "strapi::public",
];

Now here we need to tell the Strapi to load our custom middleware
So just add "global::__YOUR__MIDDLEWARE_FILENAME__", at the end, so for me it "global::TokenPlacer", , so it would look like this now

module.exports = [
  "strapi::errors",
  "strapi::security",
  "strapi::cors",
  "strapi::poweredBy",
  "strapi::logger",
  "strapi::query",
  "strapi::body",
  "strapi::session",
  "strapi::favicon",
  "strapi::public",
  "global::TokenPlacer",
];

Learn more about Strapi Middleware and customize accordingly - Official Strapi Docs v4

1 Like

Ignore Unnecessary comments in the Code in Questions!!

I’ve tried to implement your solution and it is able to retreive the HTTPOnly cookie, however the GET /users/me request still returns “No authorization header was found” error.
Do you perhaps have an idea if there something else I have to amend/change in order for this to work?