Use of JWT in httpOnly cookie #4632

Responses to the discussion on Github - Thread 5


iksent119d ago

Solution for Strapi 3.1.1, that worked for me:

https://talke.dev/strapi-user-permissions-jwt-cookies
+

  1. Change call to “fetch” at extensions/users-permissions/config/policies/permissions.js:
      // fetch authenticated user
      ctx.state.user = await strapi.plugins[
        'users-permissions'
        ].services.user.fetch({id});
  1. Enable CORS at config/middleware.js:
module.exports = {
  settings: {
    cors: {
      origin: ['http://localhost:8000'],
    },
  },
};

  1. Pass withCredentials: true to all requests (both /auth and /articles)

willhxt109d ago

Im having the same issues mentioned above. The cookie does not appear to be setting on the strapi server.

In my browser in the Network tab, it shows the token cookie. On the Application tab, there is no cookie.

   ctx.cookies.set("token", token, {
      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,
    });
    
    ctx.send({
      status: 'Authenticated',
      user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
        model: strapi.query('user', 'users-permissions').model,
      }),
    });

    console.log('TOKEN COOKIE', ctx.cookies.get('token'));

Console.log result is undefined as previous user posted.

My middleware.js is set up properly:

module.exports = () => ({
  settings: {
    cors: {
      enabled: true,
      credentials: true,
      origin: ['http://localhost:8080'],
      headers: [
        "Content-Type",
        "Authorization",
        "X-Frame-Options",
        "access-control-allow-origin"
      ]
    },
  },
});

I also modified the logout function:

"use strict";

module.exports = {
  async logout(ctx) {
    const token = ctx.cookies.get('token');

    if (token != undefined) {
      ctx.cookies.set('token', null);
      ctx.send({
        authorized: true,
        message: "Successfully logged out.",
      });
    }

    else {
      ctx.send({
        authorized: false,
        message: "Unable to logout. You are not logged in.",
      });
    }

  }
};

When POST to /logout I receive “Unable to logout. You are not logged in.” as it is detecting the token as undefined.

Im at a loss. I do not know why the cookie is being sent via network response, but not actually being set in strapi backend. Any ideas?


iksent109d ago

Hello, did you pass withCredentials: true to your request at frontend?


willhxt109d ago

Yes I did. I am using Vue.js

<script>
  import axios from 'axios';
  export default {
    data() {
      return {
        errors: [],
        form: {
          email: '',
          password: '',
        },
        strapi_url: process.env.STRAPI_URL || 'http://localhost:1337'
      }
    },
    methods: {
      login_user: function() {

        // Get jwt token
        axios.post(this.strapi_url + '/auth/local', {
            withCredentials: true,
            identifier: this.form.email,
            password: this.form.password
        }).then(response => {

          // Store token and set store data
          this.$store.commit('set_logged_in', true);

          // Store email
          this.$store.commit('set_user_email', this.form.email);

          // Re-direct home
          this.$router.push('/');

          console.log('User profile', response.data);
        })
        .catch(error => {
          // Handle error.
          this.errors = [];
          if (error.response.status == 400) {
            var i;
            for (i = 0; i < error.response.data.data[0].messages.length; i++) {
              this.errors.push(error.response.data.data[0].messages[i]);
            }
          }
        });
      },
    },
    created() {
      if (this.$store.getters._logged_in == true) {
        this.$router.push("/");
      }
    }
  }
</script>

iksent109d ago

You are passing it as a data param, but it is not correct. Try this:

axios.post(this.strapi_url + '/auth/local', {
            identifier: this.form.email,
            password: this.form.password
}, {withCredentials: true})

willhxt109d ago

That did the trick. Thank you so much. Been beating my head on the laptop for hours.

You’re awesome!


willhxt109d ago

One last question (I’m still new to Vue and JWT)… with this example, what is the best way to confirm if a user is logged in? I know its a bit off topic, but I’m just trying to figure out what I should store inside of my localStorage for Vue.

I assume if I access an API endpoint, and there is no token, it will return a 403 error (I assume).

Should I store only the user characteristics/data in the localStorage? I’m using VuexPersistence localStorage in Vue, so when i close the browser, my localStorage should remain. This could be bad in 14 days when the token expires.

I guess I’m just not sure how to go about checking if I still have a valid cookie/JWT token from Vue and if the user is still logged in.

When the token expires, I obviously need to force the user to logout on the front end. Right now it stores “is_logged_in” as part of the localStorage state using mutations/setters and getters.

I also have ideas of having the user re-authenticate after X amount of minutes/hours of being inactive (I plan to tackle this further down the road). For now, I just need some direction or guidance on how to handle confirmation on whether a user is still logged in.

Thanks again, and feel free to re-direct me to a better forum or thread that can better help me learn.