System Information
-
Strapi Version: v4.1.7
-
Operating System: macOS X 10.15.7
-
Database: mysql
-
Node Version: v14.18.2
-
NPM Version: 6.14.15
-
Yarn Version: 1.22.17
Hey all!
I am trying to write a policy which will pass through authenticated users or limit access to a known API token.
Therefore, I created a API token with the name development in the administration panel.
The policy looks like this:
"use strict";
/**
* `is-dev-or-authenticated` policy.
*/
module.exports = (policyContext, config, { strapi }) => {
strapi.log.info("In is-dev-or-authenticated policy.");
if (policyContext.state.user) {
// if a session is open
// go to next policy or reach the controller's action
return true;
}
const authHeader = policyContext.request.header.authorization;
if (!authHeader) {
return false;
}
const token = authHeader.split(" ")[1];
console.log(token);
console.log(strapi.admin.services.token.decodeJwtToken(token));
return true;
};
As you can see, I am trying to get some information about the used API token in the request.
console.log(token);
really returns the used token.
But console.log(strapi.admin.services.token.decodeJwtToken(token));
says { payload: null, isValid: false }
How can I get the information if the used token is the one with the name development
?
Not sure if you’ve moved beyond this since it’s been over a year since you posted, but I found a way to do this.
You don’t need to decode the token at all, the policyContext has a policyContext.state.auth.credentials.name
property, which is the name of the token provided. So in your case you could probably do something like this.
module.exports = (policyContext, config, { strapi }) => {
strapi.log.info("In is-dev-or-authenticated policy.");
var tokenName = policyContext.state.auth.credentials.name;
if (policyContext.state.user || tokenName == "development")
return true;
return false;
};
You might need to null check the credentials or the auth object, but this kind of solution has worked for me.
1 Like
Yeah and then you’re leaving backdoor to anyone who looks up the name of your token set on frontend & thus can access any route protected by that policy.
This is defeats the whole purpose of using JWT for authorization.
Here is the solution, following how users-permissions plugin (node_modules/@strapi/plugin-users-permissions/server/services/jwt.js) handles bearer token verification:
const jwt = require("jsonwebtoken");
module.exports = (policyContext, config, { strapi }) => {
const authHeader = policyContext.request.header.authorization;
if (!authHeader) {
return false;
}
const token = authHeader.split(" ")[1];
jwt.verify(
token,
strapi.config.get("plugin.users-permissions.jwtSecret"),
{},
(err, tokenPayload = {}) => {
if (err) {
return false;
}
// returns id of a user stored in database
// console.log(tokenPayload);
return true;
}
);
};
example of policy used (in src/extensions/users-permissions/strapi-server.js):
module.exports = (plugin) => {
// Define the setup function in the user controller
plugin.controllers.user.setuptest = async (ctx) => {
// Your custom setup logic here
ctx.body = "Hello World";
};
// Add the setup route directly to the plugin's routes
plugin.routes["content-api"].routes.push({
method: "GET", // or 'PUT', 'POST', etc., as per your requirement
// needs to be /setup/create in order for koa.js to don't interpret new route as api/users/:id (for findOne)
// I guess now it makes sense why there is a prefix by default
path: "/users/setup/create", // The desired path for your setup route
handler: "user.setuptest", // Points to the setup function in user controller
config: {
policies: ["global::is-authenticated"],
prefix: "",
},
});
return plugin;
};