Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth check before call original resolver ? #2

Closed
giautm opened this issue Nov 22, 2017 · 11 comments
Closed

Auth check before call original resolver ? #2

giautm opened this issue Nov 22, 2017 · 11 comments

Comments

@giautm
Copy link

giautm commented Nov 22, 2017

I think we should check Auth before call original resolver? in some case like mutation. or resolver access user info in context object.

@alexisgahon
Copy link

@giautm I am thinking about this actually, having a way to specify easily the resolvers chain order.
Because in some case (like auth / roles) it's more efficient to check before the field / query resolver is resolved.

@chenkie
Copy link
Owner

chenkie commented Nov 29, 2017

@alexisgahon can you elaborate a bit more regarding the chain order?

@alexisgahon
Copy link

@chenkie The way attachDirectives is implemented only allow directives resolver to be executed before the field resolver.

What it does:
[Field Resolver (listArticles)] -> [Directive resolver (isAuthenticated)] -> Next

What I try to achieve
[Directive resolver (_isAuthenticated)] -> [Field Resolver (listArticles] -> Next

Maybe use a prefix on directive name to know how to chain resolvers.
It's just an idea atm.

@orefalo
Copy link

orefalo commented Nov 30, 2017

@alexisgahon I think you have it reversed, it should say:

What it does:
[Directive resolver (_isAuthenticated)] -> [Field Resolver (listArticles] -> Next

What I try to achieve
[Field Resolver (listArticles)] -> [Directive resolver (isAuthenticated)] -> Next

@giautm
Copy link
Author

giautm commented Dec 1, 2017

Hi, i edited attachDirectives. The first arg of directive resolver is a function that call original resolver with original args. In any Field resolver, we can call first or after check login, then return the result. Please checkout:

// Credit: agonbina https://github.com/apollographql/graphql-tools/issues/212
export const attachDirectives = (resolvers, schema: GraphQLSchema) => {
  forEachField(schema, (field: GraphQLField<any, any>) => {
    const directives = field.astNode.directives;
    directives.forEach((directive: DirectiveNode) => {
      const directiveName = directive.name.value;
      const resolver = resolvers[directiveName];

      if (resolver) {
        const originalResolver = field.resolve || defaultFieldResolver;
        const Directive = schema.getDirective(directiveName);
        const directiveArgs = getArgumentValues(Directive, directive);

        field.resolve = (...args) => {
          const [source, _, context, info] = args;
          return resolver(() => {
            const promise = originalResolver.call(field, ...args);
            if (promise instanceof Promise) {
              return promise;
            }
            return Promise.resolve(promise);
          }, source, directiveArgs, context, info);
        };
      }
    });
  });
};

directiveResolvers:

const directiveResolvers = {
  // UPPER CASE a string result
  upperCase(next: () => Promise<any>) {
    return next().then((r) => {
      if (typeof(r) === 'string') {
        return r.toUpperCase();
      }
      return r;
    });
  },
  isAuthenticated(nextResolver, source, args, context) {
    const token = context.headers.authorization;
    if (!token) {
      throw new AuthorizationError({
        message: 'You must supply a JWT for authorization!'
      });
    }
    try {
      const decoded = jwt.verify(
        token.replace('Bearer ', ''),
        process.env.JWT_SECRET
      );
      return nextResolver(); // <---- dont forget it
    } catch (err) {
      throw new AuthorizationError({
        message: 'You are not authorized.'
      });
    }
  },
};

@orefalo
Copy link

orefalo commented Dec 5, 2017

Thank you, great!

now this is typescript and not compatible with the current project. But I too use TS, thank you

@orefalo
Copy link

orefalo commented Dec 5, 2017

What would the graphql declaration for upperCase look like?

directive @isAuthenticated on QUERY | FIELD
directive @hasScope(scope: [String]) on QUERY | FIELD
directive @uppercase??????

@giautm
Copy link
Author

giautm commented Dec 5, 2017

directive @uppercase on FIELD

Current solution is work for directive on field only. Other locations like QUERY | FRAGMENT | INLINE_FRAGMENT,... is not supported.

@giautm
Copy link
Author

giautm commented Dec 5, 2017

I have a PR right here: ardatan/graphql-tools#518 to bring it to graphql-tools, so typescript is not a problem for any project.

@orefalo
Copy link

orefalo commented Dec 5, 2017

yep, thank you

@dan-kwiat
Copy link

Any more thoughts on the schema directives pattern over the last year @chenkie ? I just had a play developing your approach using the updated graphql-tools and adding a customised GraphiQL interface to allow setting scopes.

code: https://github.com/dan-kwiat/graphql-auth
demo: https://graphql-auth.now.sh

Let me know if you'd like to merge.

@giautm giautm closed this as completed Mar 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants