I'm implementing an express server using express-graphql for my React project. I have the following token being passed through validateTokenMiddleware():
{
"http://localhost:8000/roles": [
"admin"
],
"iss": "https://<hidden>.auth0.com/",
"sub": "auth0|<hidden>",
"aud": [
"http://localhost:8000",
"https://<hidden>.auth0.com/userinfo"
],
"iat": 1708694659,
"exp": 1708781059,
"azp": "<hidden>",
"scope": "openid",
"permissions": [
"read:admin"
]
}
I'd like to use the permissions property to check whether a resolver can be executed. Is there a way to add decorators to resolvers or to add middleware to check the permissions of the token passed before executing a resolver? Here is the server app.js:
const express = require("express");
const bodyParser = require("body-parser");
const { graphqlHTTP } = require("express-graphql");
const cors = require("cors");
const graphqlSchema = require("./graphql/schema/index");
const graphqlResolvers = require("./graphql/resolvers/index");
const validateTokenMiddleware = require("./middleware/validate");
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(
"/graphql",
validateTokenMiddleware,
graphqlHTTP({
schema: graphqlSchema,
rootValue: graphqlResolvers,
graphiql: true,
})
);
const validateTokenMiddleware = async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ error: "Unauthorized" });
}
const token = authHeader.split(" ")[1];
const tokenValidationResult = await isTokenValid(token);
if (tokenValidationResult.error) {
return res.status(401).json({ error: "Unauthorized" });
}
// Token is valid, add the decoded token to the request object for later use
req.decodedToken = tokenValidationResult.decoded;
next();
};
The schema is defined using buildSchema() from the graphql package and defined as:
const { buildSchema } = require("graphql");
module.exports = buildSchema(`
input CreateUserInput {
id: String!
firstName: String!
lastName: String!
birthdate: String!
email: String!
bio: String
}
input UpdateUserInput {
id: ID!
firstName: String
lastName: String
birthdate: String
email: String
}
type User {
_id: ID!
firstName: String!
lastName: String!
birthdate: String!
email: String!
createdAt: String
updatedAt: String
}
type Query {
user(id: ID!): User
listUsers: [User]
...
}
type Mutation {
createUser(input: CreateUserInput!): User
updateUser(input: UpdateUserInput!): User
...
}
`);
graphqlResolvers being passed to rootValue in graphqlHTTP() is an object of async resolvers. Here is an example resolver:
user: async ({ id }) => {
try {
const user = await User.findById(new ObjectId(id));
return user;
} catch (err) {
throw err;
}
},