2

I'm using graphql-shield to protect a subgraph.

const isAuthenticated = rule({ cache: 'contextual' })(async (parent, args, ctx, info) => {
  return ctx.isAuthenticated
})

const permissions = shield({
  Query: {
    '*': and(isAuthenticated)
  },
  Mutation: {
    '*': and(isAuthenticated)
  }
})

const server = new ApolloServer({
    schema: applyMiddleware(buildSubgraphSchema([{ typeDefs, resolvers }]), permissions),
    introspection: global.configuration.ENVIRONMENT === 'development'
})

And in my build process, I'm using rover CLI to update thesupergraph schema in Apollo Studio:

rover subgraph introspect http://localhost/graphql | rover subgraph publish super-graph@development --routing-url http://localhost/graphql --schema - --name persons

The rover update fails because the permission shield throws a Not Authorised! error.

How do I protect the subgraph with graphql-shield and also permit SubgraphIntrospectQuery operation?

I understand it is possible to add a bearer token to the rover introspect command:

rover subgraph introspect http://localhost/graphql --header "Authorization: Bearer token"

However, there is no way for me to generate an access token during the build process.

capiono
  • 2,875
  • 10
  • 40
  • 76

3 Answers3

3

You could try allowing the following branches:

export const permissions = shield({
    Query: {
        _service: allow,
    },
    _Service: {
        sdl: allow
    }
},{
    fallbackRule: deny,
    allowExternalErrors: true,
    ...
});

When Apollo performs the introspection, it uses these two branches initially. Also note the "allowExternalErrors" flag. When encountering an error, graphql-shield absorbs them, which can hide issues which you are trying to debug. This might also be a problem, although your code seems fine otherwise.

Apollo also uses "Query._entities", "Query._service", "_Entity.*", "_Service.*", "_Any.*" but I haven't found this to be used by the initial introspection.

You mentioned that you can't generate a token for your build process but it is probably a good idea to protect these endpoints rather than use allow.

Namyts
  • 373
  • 1
  • 9
  • Mods, don't delete this answer unless you can guarentee it is a duplicate problem. The solution for two issues might be similar, which you should know. Plus you could at least let OP see it first, so he actually gets help, since it fulfils the entire purpose of this website – Namyts Mar 03 '22 at 09:28
0

I was able to resolve this issue by changing the scope of my authentication.

Instead of authenticating all "*"

const permissions = shield({
  Query: {
    '*': and(isAuthenticated)
  },
  Mutation: {
    '*': and(isAuthenticated)
  }
})

I change to authenticating individual operations:

const permissions = shield({
  Query: {
    'user': and(isAuthenticated),
    'users': and(isAuthenticated)
    ....
  },
  Mutation: {
    'createUser': and(isAuthenticated),
    'updateUser': and(isAuthenticated)
    ....
  }
})
capiono
  • 2,875
  • 10
  • 40
  • 76
0

There is answer. Good luck:)).

/**
 */
export const permissions = shield(
  {
    Query: {
      "*": deny,
      _service: allow,

      ...,
    },
    Mutation: {
      "*": deny,

      ...,
    },
  },
  {
    fallbackRule: allow,
  },
)

https://github.com/dimatill/graphql-shield/issues/211#issuecomment-450636577