neo4j / graphql

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations.
https://neo4j.com/docs/graphql-manual/current/
Apache License 2.0
496 stars 147 forks source link

Custom directives don't get access to auth context #1159

Open mathix420 opened 2 years ago

mathix420 commented 2 years ago

Hello Neo4J Team!

Describe the bug

The new custom directives, wrap resolvers after neo4j/graphql has already wrapped them with the auth wrapper. That means custom directives don't get access to the auth context. Not sure if it needs to be considered as a bug, but for me, it change the way I was working with this library.

Illustration:

[ custom resolver ] -> [ neo4j auth wrapper ] -> [ original resolver ]

To Reproduce

function creatorsOnlyDirective(directiveName: string): (schema: GraphQLSchema) => GraphQLSchema {
    return (schema) =>
        mapSchema(schema, {
            [MapperKind.OBJECT_FIELD]: (fieldConfig) => {
                const creatorsOnlyDirective = getDirective(schema, fieldConfig, directiveName)?.[0];
                if (creatorsOnlyDirective) {
                    const { resolve = defaultFieldResolver } = fieldConfig;
                    return {
                        ...fieldConfig,
                        resolver: async function (source, args, context, info) {

                            console.log(context.auth);

                            if (!context?.auth?.jwt?.roles?.includes?.("creator"))
                                throw new ForbiddenError("You are not a creator");
                            if (!context?.auth?.jwt?.roles?.includes?.("verified"))
                                throw new ForbiddenError("You account is not verified");
                            return resolve(source, args, context, info);
                        },
                    };
                }
            },
        });
}

With this creatorsOnlyDirective transformer - or any other - you will see that there is no auth context available.

Expected behavior

Illustration:

[ neo4j auth wrapper ] -> [ custom resolver ] -> [ original resolver ]

Maybe a transformers field in the Neo4jGraphQLConstructor to apply transformers before the auth wrapper.

const neoSchema: Neo4jGraphQLConstructor = {
    resolvers,
    typeDefs: [...directivesTypeDefs, typeDefs],
    tramformers: [creatorsOnlyDirective],
    plugins: {
        auth: new Neo4jGraphQLAuthJWTPlugin({
            secret: process.env.PWT_SECRET,
        }),
    },
    driver,
};

System (please complete the following information):

neo4j-team-graphql commented 2 years ago

Many thanks for raising this bug report @mathix420. :bug: We will now attempt to reproduce the bug based on the steps you have provided.

Please ensure that you've provided the necessary information for a minimal reproduction, including but not limited to:

If you have a support agreement with Neo4j, please link this GitHub issue to a new or existing Zendesk ticket.

Thanks again! :pray:

darrellwarde commented 2 years ago

Hey @mathix420, this is interesting, can you give some details about how and where you applying this custom directive?

I'm a bit confused by this issue, because the custom directive "extracts" the resolver out of the field, which should already have the populated because it was "reached" via either a Query or Mutation field which is wrapped here: https://github.com/neo4j/graphql/blob/dev/packages/graphql/src/classes/Neo4jGraphQL.ts#L153

So quite intrigued as to why the extracted resolver doesn't have this custom context.

mathix420 commented 2 years ago

Hello @darrellwarde,

If I'm not missleading, we should implement custom directives like this.

const neoGql = new Neo4jGraphQL({
    resolvers,
    typeDefs: [directiveTypeDef, typeDefs],
    transformers: [transformer],
    plugins: {
        auth: new Neo4jGraphQLAuthJWTPlugin({
            secret: process.env.PWT_SECRET,
        }),
    },
    driver,
});

const schema = directiveTransformer(await neoGql.getSchema())

As you can see with this represntation, in the Custom directive wrapper we can't have access to the auth context as the Auth wrapper (which is responsible for injecting auth in the context) is still not executed.

Group 1

As I understand this (https://github.com/neo4j/graphql/blob/dev/packages/graphql/src/classes/Neo4jGraphQL.ts#L153), it does not wrap the Query field but all the resolvers of the fields inside Query or Mutation field.

Also I wanted to point out that it's been some months that I haven't worked with graphql, so I may be totally misleaded.. I hope it's not the case but at least you're aware of that.