Use of JWT in httpOnly cookie #4632

Hi everyone.

I’ve been thinking of a new way to apply to solve this in a way that doesn’t require overwriting the core auth controller (auth/local) nor running the cookieSetter middleware on every request/response.

I have come up with the idea of creating a custom POST endpoint (route/controller) that takes the user credentials and use them to perform a new request to Strapi’s core login endpoint: auth/local, capturing the response and if successful (jwt is included) adding the jwt value in a secure cookie.

Example:

Custom controller directories:
/api/custom/
/api/custom/config/
/api/custom/controllers/

Route: /api/custom/config/routes.js
:star2: Now use this route to POST your user credentials (instead of Strapi’s official /auth/local route).

{
  "routes": [
    {
      "method": "POST",
      "path": "/auth/cookielogin",
      "handler": "Custom.index"
    }
  ]
}

Controller: /api/custom/controllers/Custom.js

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 =
      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}`;

    try {
      // Now submit the credentials to Strapi's default login endpoint
      const { data } = await axios.post(`${absoluteURL}/auth/local`, body);

      // Set the secure cookie
      if (data && data.jwt) {
        ctx.cookies.set("jwt", data.jwt, {
          httpOnly: true,
          secure: process.env.NODE_ENV === "production" ? true : false,
          maxAge: 1000 * 60 * 60 * 24 * 14, // 14 Day Age
          domain:
            process.env.NODE_ENV === "development"
              ? "localhost"
              : process.env.PRODUCTION_URL,
        });
      }

      // Respond with the jwt + user data, but now this response also sets the JWT as a secure cookie
      return ctx.send(data);

    } catch (error) {

      return ctx.badRequest(null, error);

    }
  },
};

Now you can use the cookieGetter middleware (detailed above by other users) to make sure your JWT is taken from the cookie and passed into the requests authorization headers, where Strapi is expecting them to be.

Thanks to everyone for the discussion and collaboration. I hope this is useful for some of you.

*Based on what I’ve seen so far, I’m quite excited about what Strapi is doing :clap: