maticzav/graphql-shield

fallbackRule context information

Opened this issue · 2 comments

Hi, I have a quick question about the fallbackRule option.

In my code I wrote the following:

const permissions = shield({
	Query: isAllowed,
	Mutation: isAllowed,
},
{
	allowExternalErrors: true, // this allows our regular errors to be returned instead of seeing authentication error
	fallbackRule: isAllowed,
});

I basically checking my queries, mutations resolvers and making sure they are allowed to be called.
I added the same rule in the fallbackRule option so that it could handle every other field.
I have a directive that I pass certain data to my isAllowed rule via context.

But for some reason, my directive values are always undefined when my rule is called from the fallbackRule.
I was wondering if this was normal behavior?

My goal was basically to create a directive that allows me to define permissions in my schema, and using my directive to pass that data via context to the middleware, which works for Queries and Mutations.
But doesn't work for fields unless I declare all of them in the middleware, which I am trying to avoid.
Since this doesn't work:

const permissions = shield({
	"*": isAllowed
})

or this (only works for queries and mutations but not the fields of every other types

const permissions = shield(isAllowed)

I was thinking of using the fallbackRule since I can see it being called for every other field.

UPDATE

Ok so I did another test, and I noticed that if I remove { cache: "contextual" } from my fallbackRule, it sort of works.
I don't know why. My rule needs to check if a person is logged in, has the proper permissions, etc.
So I used that, but with it, it doesn't seem to see the context values of my directive.
If I remove it, I see my directive values, but they show up for all the fields, instead of only the specific field.

Hope anyone can help.

Ok, so after a few tests, it seems to work if I omit the cache options from the fallbackRule.

This works for my use case but not sure if this is the proper way to do it.
But I will close this for now.

I am reopening this because I noticed that the context seems shared and not independent per resolver, if that makes any sense?

For example, when I removed the { cache: "contextual" } from my fallbackRule, I see that my other fields resolve using the isAllowed rule.

But the issue, is that the data that I pass from my directive that is specific to a field via the context, seems to be passed down to the other fields even if they don't have the directive applied.

I did a test like this:

In my directive I have something like this:

fieldConfig.resolve = async function (source, args, context, info) {
	context.directiveValues.uniqueId = "test";

	return resolve(source, args, context, info);
};

return fieldConfig;

And in my rule I look at context.directiveValues, and when I query a field that has the directive applied, every other sibling has the same value in directiveValues and I don't know why.

I assume its by design.

In order to patch/fix it, I had to manually delete the value when its being called so it isn't passed to the others.