Apollo Server Federation with graphql-shield

984 views Asked by At

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.

3

There are 3 answers

0
capiono On BEST ANSWER

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)
    ....
  }
})
1
Namyts On

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.

0
flopple f On

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