Search code examples
node.jsauth0strapi

Add new fields to context variable in strapi backend for user management


I am using strapi as backend and react in the front-end. So the use case is that the user will signup and that signup will be done using auth0. I have defined some roles for the users signing up as shown on auth0 Roles based on plan taken by user

const _ = require("lodash");
const axios = require("axios");
const jwt = require("../jwt");
module.exports = async (ctx, next) => {
let role;
if (ctx.state.user) {
// request is already authenticated in a different way
return next();
}
try {
const tokenInfo = await axios({ method: "post",
url: `${process.env.AUTH0_URL}/userinfo`,
headers: { Authorization: ctx.request.header.authorization,
          },
        });
let user_id = tokenInfo.data.sub;
var config = { method: "get",
url: `${process.env.AUTH0_URL}/api/v2/users/${user_id}/roles`,
headers: {Authorization: `Bearer ${jwt.jwtSecret}`,
          },
        };

axios(config).then(function (response) {
ctx.state.roles = response.data[0].name; // This part does not work in the next policy as ctx.state.role gives undefined in route specific policy
          }).catch(function (error) {
console.log(error);
          });
//   console.log(tokenInfo.data, "tokeninfo");
if (tokenInfo && tokenInfo.data) {
return await next();
        }
      } catch (err) {
console.log(err.message);
return handleErrors(ctx, err, "unauthorized");
      }

Currently these will be managed here only. Now I have a collection which has some research articles which can only be accessed depending upon the plan user has been assigned. In order to protect the route and strapi access I have installed user-permissions plugin in strapi and managing userinfo using a global policy as shown

Project Structure . So here is the code through which I am checking the user info on every route


Now there are two ways in which I tried solving my problem. First I read the tokenInfo data from userInfo route but unfortunately auth0 is not returning roles assigned. It is only returning standard data like

"name": "ansh5@gmail.com",
"nickname": "ansh5",
"picture": "https://s.gravatar.com/avatar/6fdb83f10321dd7712ac2457b11ea34e? 
 s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fan.png",
"updated_at": "2021-07-19T08:03:50.461Z",
"user_id": "auth0|60ec0b3721224b0078ac95f4",

So in order to get user role I used the other API and configured it with my auth0 account.
${process.env.AUTH0_URL}/api/v2/users/${user_id}/roles
I am getting the correct response but when I am doing this assignment.
ctx.state.roles = response.data[0].name;

I am getting undefined in my ctx.state.roles in my route specific policy. Does anybody have idea how we manage strapi and auth0 together.

Solution

  • Yes, it's because the axios calls are asynchronous in nature. So as per your code, axios will try to get the user information over a network call, but strapi will not really wait for the response. Instead it will just move forward to the next policy, hence resulting in an undefined user role. To fix this, you need to await for the api response from axios. Try the code below:

    const axios = require("axios");
    const jwt = require("../jwt");
    module.exports = async (ctx, next) => {
      let role;
      if (ctx.state.user) {
        // request is already authenticated in a different way
        return next();
      }
      try {
        const tokenInfo = await axios({
          method: "post",
          url: `${process.env.AUTH0_URL}/userinfo`,
          headers: {
            Authorization: ctx.request.header.authorization,
          },
        });
        let user_id = tokenInfo.data.sub;
        var config = {
          method: "get",
          url: `${process.env.AUTH0_URL}/api/v2/users/${user_id}/roles`,
          headers: {
            Authorization: `Bearer ${jwt.jwtSecret}`,
          },
        };
    
        const resp = await axios(config);
        ctx.state.roles = response.data[0].name;
        console.log(ctx.state.roles);
       
        //   console.log(tokenInfo.data, "tokeninfo");
        if (tokenInfo && tokenInfo.data) {
          return await next();
        }
      } catch (err) {
        console.log(err.message);
        return handleErrors(ctx, err, "unauthorized");
      }
    }