Strapi custom route 403 issue

System Information
  • Strapi Version: 3.6.2
  • Operating System: windows 10
  • Database: sqlite
  • Node Version: 14
  • NPM Version: 6.14.14
  • Yarn Version:

Hello,

I’m struggling for creating a custom for let user update their username, email and password.
So far I’m stuck with a 403 forbidden for my route.

user.js:98 PUT http://localhost:8082/users/updateMe 403 (Forbidden)
changepassword @ user.js:98
user.js:108 {statusCode: 403, error: 'Forbidden', message: 'Forbidden'}

Here my custom route \api\userupdate\config\routes.json"

{
  "method": "PUT",
  "path": "/users/updateMe",
  "handler": "users.updateMe",
  "config": {
	"policies": [],
	"prefix": "",
	"description": "Update the logged in user information",
	"tag": {
	  "plugin": "users-permissions",
	  "name": "User",
	  "actionType": "update"
	}
  }
}

And my controller “\api\userupdate\controllers\users.js”

'use strict';

/**
 * api/userupdate/controllers/users.js
 */

const _ = require('lodash');
const { sanitizeEntity } = require('strapi-utils');

const sanitizeUser = user =>
  sanitizeEntity(user, {
    model: strapi.query('user', 'users-permissions').model,
  });

module.exports = {  /**
   * Update authenticated user.
   * @return {Object|Array}
   */
  async updateMe(ctx) {
    const advancedConfigs = await strapi
      .store({
        environment: '',
        type: 'plugin',
        name: 'users-permissions',
        key: 'advanced',
      })
      .get();

    const user = ctx.state.user;
    const { id, email, username, password } = user;

    if (!user) {
      return ctx.badRequest(null, [{ messages: [{ id: 'No authorization header was found' }] }]);
    }

    if (_.has(ctx.request.body, 'email') && !email) {
      return ctx.badRequest('email.notNull');
    }

    if (_.has(ctx.request.body, 'username') && !username) {
      return ctx.badRequest('username.notNull');
    }

    if (_.has(ctx.request.body, 'password') && !password && user.provider === 'local') {
      return ctx.badRequest('password.notNull');
    }

    if (_.has(ctx.request.body, 'role')) {
      return ctx.badRequest(null, [{ messages: [{ id: 'Cannot update own role' }] }]);
    }

    if (_.has(ctx.request.body, 'confirmed')) {
      return ctx.badRequest(null, [{ messages: [{ id: 'Cannot change own confirmed status' }] }]);
    }

    if (_.has(ctx.request.body, 'provider')) {
      return ctx.badRequest(null, [{ messages: [{ id: 'Cannot change own provider' }] }]);
    }

    if (_.has(ctx.request.body, 'resetPasswordToken')) {
      return ctx.badRequest(null, [{ messages: [{ id: 'Cannot set own password reset token' }] }]);
    }

    if (_.has(ctx.request.body, 'blocked')) {
      return ctx.badRequest(null, [{ messages: [{ id: 'Cannot change own blocked status' }] }]);
    }

    if (_.has(ctx.request.body, 'username')) {
      const userWithSameUsername = await strapi
        .query('user', 'users-permissions')
        .findOne({ username });

      if (userWithSameUsername && userWithSameUsername.id != id) {
        return ctx.badRequest(
          null,
          formatError({
            id: 'Auth.form.error.username.taken',
            message: 'username.alreadyTaken.',
            field: ['username'],
          })
        );
      }
    }

    if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
      const userWithSameEmail = await strapi.query('user', 'users-permissions').findOne({ email });

      if (userWithSameEmail && userWithSameEmail.id != id) {
        return ctx.badRequest(
          null,
          formatError({
            id: 'Auth.form.error.email.taken',
            message: 'Email already taken',
            field: ['email'],
          })
        );
      }
    }

    let updateData = {
      ...ctx.request.body,
    };

    if (_.has(ctx.request.body, 'password') && password === user.password) {
      delete updateData.password;
    }

    const data = await strapi.plugins['users-permissions'].services.user.edit({ id }, updateData);

    const newData = sanitizeUser(data);
    ctx.send(newData);
  },
}

I’ve done many many test to try making a custom udpateMe feature for logged user.
This code is not from me, I’ve was unable to create one myself after many attempts.

In the backend , Role>Authenticated>permissions updateme is enable

also here my code in the frontend :

const url = "http://localhost:8082";
//partie changepassword
const useroptionsForm = document.forms.useroptions;
useroptionsForm.addEventListener('submit',changepassword);

init()

function init() {    
    const resultat = retrieveJWTtoken();
    //console.log(resultat);
    if (resultat.status === "anonymous") {
        window.location.href = './index.html';
    };
    afficheProfile();     
}

function deconnexion() {
    localStorage.removeItem("user");    
    init();
}

function retrieveJWTtoken() {
    //const userFromLocalStorage = localStorage.getItem("user");
    const userFromLocalStorage = getWithExpiry("user");
    if (userFromLocalStorage) { //si user existe dans le storage local
        const user = JSON.parse(userFromLocalStorage);
        if (user.jwt != null) { //et que le jeton existe aussi
            return {status: "authenticated",msg: "connexion réussie", token: user.jwt};
        };        
    };
    return { status: "anonymous",msg: "merci de vous connecter", token: null};
} 
/*{id: 1, username: 'user', email: 'user@user.fr', provider: 'local', confirmed: true, …}
blocked: null
confirmed: false
created_at: "2021-09-02T15:51:22.903Z"
email: "user@user.fr"
id: 1
*/

function afficheProfile() {
    //const token = JSON.parse(localStorage.getItem("user")).jwt; //voir fonction connexion
    const getuser = getWithExpiry("user");
    const token = JSON.parse(getuser).jwt; //voir fonction connexion
    fetch(`${url}/users/me`,{
        headers: {
            "Authorization": `Bearer ${token}`,
        },    
    })
    .then((response) => response.json())
    .then(data => {
        console.log(data);
        const usertext = document.getElementById("usertext");
        if (usertext) {
            usertext.textContent=data.username;
        };
        //console.log(isAdmin);
    })
}

function getWithExpiry(key) {
    const itemStr = localStorage.getItem(key)
    // if the item doesn't exist, return null
    if (!itemStr) {
        return null
    }
    const item = JSON.parse(itemStr)
    const now = new Date()
    // compare the expiry time of the item with the current time
    if (now.getTime() > item.expiry) {
        // If the item is expired, delete the item from storage
        // and return null
        localStorage.removeItem(key)
        return null
    }
    return item.value
}

function changepassword(eventregister) {
    eventregister.preventDefault();
    const getuser = getWithExpiry("user");
    const token = JSON.parse(getuser).jwt; //voir fonction connexion
    const username = useroptionsForm.username.value;
    const email = useroptionsForm.email.value;
    const password = useroptionsForm.password.value;
    console.log(username,email,password);
   
    //http://localhost:8082/users/updateMe
    const payload = { //on envoie ces infos
        username,
        email,
        password
    };

    fetch(`${url}/users/updateMe`,{
        method: 'PUT',
        headers: {
            "Authorization": `Bearer ${token}`,
            "Content-Type": "application/json"
        },
        body: JSON.stringify(payload)
    })
    .then((response) => response.json())
    .then((data) => { 
      console.log(data);//retourne le nouverau jeton JWT
    })
    .catch(err => {
        console.error(err);
    })
}

Why I get this 403 error ? Can you help me please ?