GithubHelp home page GithubHelp logo

grand-stack / graphql-auth-directives Goto Github PK

View Code? Open in Web Editor NEW
113.0 4.0 27.0 1.45 MB

Add authorization to your GraphQL API using schema directives.

Home Page: https://www.npmjs.com/package/graphql-auth-directives

License: Other

JavaScript 96.76% Shell 3.24%
graphql schema-directives graphql-api authentication authorization graphql-directive

graphql-auth-directives's People

Contributors

dependabot[bot] avatar dustinsmith1024 avatar francescovenica avatar iamflowz avatar johnymontana avatar lorensr avatar mohamedgamalabugalala avatar phylodome avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

graphql-auth-directives's Issues

2.2.0 verifyAndDecodeToken Bugs

Looking at the code published in 2.2.0, I noticed a few issues.

The previous header retrieval logic took advantage of short-circuiting to avoid issues of attempting to access properties of an undefined object:

  !req ||
  !req.headers ||
  (!req.headers.authorization && !req.headers.Authorization)

At each stage of this predicate, the || ensures we're not attempting to access fields of an undefined object.

But as of 2.2.0:

(!req ||
  !req.headers ||
  (!req.headers.authorization && !req.headers.Authorization)) &&
(!req.cookies && !req.cookies.token))

There are actually 2 bugs in just these 4 lines of code, whereby we may attempt to access the fields of an intermediate undefined object.

First, let's say that the initial !req evaluates to true. Because the initial logic is now adjoined with (!req.cookies && !req.cookies.token)) we will now attempt to evaluate !req.cookies, but req is undefined, and we blow up.

Second, the same logic applies within (!req.cookies && !req.cookies.token) itself, if req.cookies is undefined: !req.cookies evaluates to true, so we attempt to execute !req.cookies.token, and again blow up trying to access a property on an undefined object.

Because these are inside an if statement and not a try/catch, these cases will propagate to the GraphQL error response, which I assume is not the intent.

In the first case, one might argue that a missing req is violating the package's API, and thus no guarantees are provided / blowing up is fine. I'd tend to disagree, favoring the friendlier developer ergonomics of catching such cases and providing a useful error message.

In the second case, it's perfectly reasonable to not have a req.cookies object. In fact this is the default in express, absent a cookie-parsing middleware, which is how I ran into this issue when trying to upgrade.

Req Access Pattern Overrides ApolloServer's Context Config

Cross-post and extension of this issue, as it may be more readily addressed here.

At present, graphql-auth-directives usage effectively co-opts the entire context object via returning only one sub-field of the context object it processes:

const server = new ApolloServer({
  schema,
  context: ({ req }) => { /* Here we pluck the variable `req` from context, via a destructured fn param */
    return req; /* Here we substitute req for the entire context object from which it came */
  }
});

If we inspect graphql-auth-directives' verifyAndDecodeToken function, we find that it takes for granted that the context object is the req object, which would not be the case if one were to configure the context object with both driver and req, as is necessary when instantiating ApolloServer.

Presently, if one instantiates ApolloServer like so:

const driver = neo4j.driver(
  "bolt://localhost:7687",
  neo4j.auth.basic("neo4j", "letmein")
);
const server = new ApolloServer({
  schema,
  context: ({ req }) => {
    return {req, driver};
  }
});

Then the logic within graphql-auth-directives will no longer find the auth header:

var verifyAndDecodeToken = function verifyAndDecodeToken(_ref) {
  var context = _ref.context; /* <-- Note: I *will not* find the headers on "_ref.context.req" */
  if (
    !context ||
    !context.headers ||
    (!context.headers.authorization && !context.headers.Authorization)
  ) {
    throw new _errors.AuthorizationError({
      message: "No authorization token."
    });
  }

Perhaps something like (in ES6, rather than the transpiled output above):

const verifyAndDecodeToken = function verifyAndDecodeToken({context}) {
  const req = context instanceof http.IncomingMessage ? context : (context.req || context.request);
...

Which would maintain backwards compatibility while allowing for context objects that possess req as a property.

That said, this still has a bit of a code smell, as in the present code what's being called context isn't necessarily the context object in question: it's either the context or a req of type http.IncomingMessage.

Whereas the above suggestion should fix the issue in a backwards-compatible manner, it also unfortunately turns the local context variable into a polymorphic type whose name doesn't closely match its underlying reality (e.g. it's really contextOrReq).

Ideally the API for verifyAndDecodeToken would take the full context as its only argument, then look for a http.IncomingMessage at either of context.req or context.request, afterwards using the proper req semantics in reference to the object in question (req.headers, etc...). In this way the API of this library would make as few assumptions as possible concerning the structure of the context object passed to the ApolloServer constructor, and minimize semantic confusion.

auth directives missing from augmented schema

server:

import { IsAuthenticatedDirective } from "graphql-auth-directives";
import { makeAugmentedSchema } from "neo4j-graphql-js";
import neo4j from "neo4j-driver";
import { typeDefs } from "./graphql-schema";

const schema = makeAugmentedSchema({
  typeDefs,
  schemaDirectives: {
    isAuthenticated: IsAuthenticatedDirective,
    //hasRole: HasRoleDirective,
    //hasScope: HasScopeDirective,
  },
});

const server = new ApolloServer({
  ...
  schema,
})

in my client, apollo.config.js

module.exports = {
  client: {
    service: {
      name: "project",
      // This option uses the resulting schema from makeAugmentedSchema
      url: "http://localhost:4001/graphql",
      skipSSLValidation: true,
    },
}

When I run from my client npx apollo client:download-schema schema.graphql, I cannot find the isAuthenticated directive. Everything else, like neo4j directives, is shown properly.

directive @cypher(statement: String) on FIELD_DEFINITION

directive @relation(name: String, direction: _RelationDirections, from: String, to: String) on FIELD_DEFINITION | OBJECT

directive @additionalLabels(labels: [String]) on OBJECT

directive @MutationMeta(relationship: String, from: String, to: String) on FIELD_DEFINITION

directive @neo4j_ignore on FIELD_DEFINITION

...

It looks like a bug with graphql-auth-directives, but since no other user reported it, I suppose it's my setup that's incorrect, though I cannot figure this out on my own. Any idea?

Support for federated services

I would like to use this library, especially is it is included in neo4j-graphql-js by default, but we use a federated architecture with one gateway handling the authentication and then making subqueries to the individual services. So while it would still be nice to let the individual services decide which fields need auth, it's unnecessary to have each service validate the JWT, since they're only accessible through the gateway. What I'm saying is, it'd be nice to be able to turn JWTs off and just add the required context variables to the request as plain objects, and trust the that the gateway has already verified the authenticity of the claims.

`next is not a function` when @isAuthenticated is used on GraphQL type or field

Hello!

Given I followed all the documentation regarding authentication on your site and added all the prerequisites for using the isAuthenticated directive, when an authenticated request (it happens ONLY when having access token in headers) involving a type or field annotated with @isAuthenticated is made, the following error comes back.

image

Here is the annotation:
image

In order to fix it, I added the check for the next() existence in the src/index.js isAuthenticatedDirective class of your library (closer to the end of it):

export class IsAuthenticatedDirective extends SchemaDirectiveVisitor {
    static getDirectiveDeclaration(directiveName, schema) {
        return new GraphQLDirective({
            name: "isAuthenticated",
            locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT]
        });
    }

    visitObject(obj) {
        const fields = obj.getFields();

        Object.keys(fields).forEach(fieldName => {
            const field = fields[fieldName];
            const next = field.resolve;

            field.resolve = function (result, args, context, info) {
                verifyAndDecodeToken({context}); // will throw error if not valid signed jwt
                if (next) {
                    return next(result, args, context, info);
                }
                return result[field.name];
            };
        });
    }
}

Please fix it in the next possible release.

FYI - using the latest version of your library in the scope of neo4j-graphql-js as of today:
image

Error when using @hasScope directive

Hello again,

When using @hasScope directive, an error occurs when annotated by the directive fields are being resolved and the scope is being checked against expected. The bug lies on line node_modules/graphql-auth-directives/dist/index.js:95 in the HasScopeDirective class where the expectedScopes point to the this.args.roles instead of the this.args.scopes:

image

Please fix it in the next release. FYI: I am using the latest version of your package as of today:

image

Include license file

It's currently not very clear which license applies to this package. While the package.json declares the license as Apache 2.0, the license text is not included anywhere in the repository, and there are no other mentions of any license. A LICENSE.txt would be nice.

Retrieving user info

Hi everyone,

How can I get the user info once I'm authenticated? I expected that it would be inside of context but seems that it is not in anyplace.

I've checked the source code and I see that decoded data is not attached to any place. Is there another way to get it?

Thanks

Custom directive runs after database insertion

Custom directives run after the data has been injected into the database and therefore unfit for the most obvious use-cases. For example, when annotating a field with validation prior to being inserted into the database is not possible. The only alternative to use a custom resolver for almost every field. This poses the problem that all graphql schema declarations would need to be annotated with neo4j_ignore thus rendering the entire makeAugmentedSchema almost obsolete.

Documentation!?

  • How can I use these directives?
  • What data structure they need?
  • How can I customize the logic / fields used?

Would be nice to some short examples. Thanks for the work! :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.