Ankur Verma
Ankur Verma

Reputation: 5933

How to remove authentication for introspection query in Graphql

so may be this is very basic question so please bear with me. Let me explain what I am doing and what I really need.


EXPLANATION


I have created a graphql server by using ApolloGraphql (apollo-server-express npm module).

Here is the code snippet to give you an idea.

api.js

import express from 'express'
import rootSchema from './root-schema'
.... // some extra code
app = express.router()
app.use(jwtaAuthenticator) // --> this code authenticates Authorization header
.... // some more middleware's added
const graphQLServer = new ApolloServer({
  schema: rootSchema, // --> this is root schema object
  context: context => context,
  introspection: true, 
})
graphQLServer.applyMiddleware({ app, path: '/graphql' })

server.js

import http from 'http'
import express from 'express'
import apiRouter from './api' // --> the above file
const app = express()
app.use([some middlewares])
app.use('/', apiRouter)
....
....
export async function init () {

try {
  const httpServer = http.createServer(app)
  httpServer
    .listen(PORT)
    .on('error', (err) => { setTimeout(() => process.exit(1), 5000) })
  } catch (err) {
    setTimeout(() => process.exit(1), 5000)
  }
  console.log('Server started --- ', PORT)
}
export default app

index.js

require('babel-core')
require('babel-polyfill')
require = require('esm')(module/* , options */)
const server = require('./server.js') // --> the above file

server.init()

PROBLEM STATEMENT


I am using node index.js to start the app. So, the app is expecting Authorization header (JWT token) to be present all the times, even for the introspection query. But this is not what I want, I want that introspection query will be resolvable even without the token. So that anyone can see the documentation.

Please shed some light and please guide what is the best approach to do so. Happy coding :)

Upvotes: 9

Views: 7191

Answers (3)

.startsWith('query Introspection') is insecure because any query can be named Introspection.

The better approach is to check the whole query.

First import graphql and prepare introspection query string:

const { parse, print, getIntrospectionQuery } = require('graphql');
// format introspection query same way as apollo tooling do
const introspectionQuery = print(parse(getIntrospectionQuery()));

Then in Apollo Server configuration check query:

context: ({ req }) => {
  // allow introspection query
  if (req.body.query === introspectionQuery) {
    return {};
  }

  // continue
}

Upvotes: 15

Ankur Verma
Ankur Verma

Reputation: 5933

So finally I found the solution and here is what I did.

Let me first tell you that there were 2 middle-wares added on base path. Like this:

app //--> this is express.Router()
  .use(jwtMw) // ---> these are middlewares
  .use(otherMw)

The jwtMw is the one that checks the authentication of the user, and since even introspection query comes under this MW, it used to authenticate that as well. So, after some research I found this solution:

jwtMw.js

function addJWTMeta (req, res, next) {
   // we can check for null OR undefined and all, then check for query Introspection, with better condition like with ignore case
   if (req.body.query.trim().startsWith('query Introspection')) {
      req.isIntrospection = true
      return next()
   }
   ...
   ...
   // ---> extra code to do authentication of the USER based on the Authorization header
}
export default addJWTMeta

otherMw.js

function otherMw (req, res, next) {
  if (req.isIntrospection) return next()
  ...
  ...
  // ---> extra code to do some other context creation
}
export default otherMw

So here in jwtMw.js we are checking that if the query is Introspection just add a variable in req object and move forward, and in next middleware after the jwtMw.js whosoever wants to check for introspection query just check for that variable (isIntrospection, in this case) and if it is present and is true, please move on. We can add this code and scale to every middleware that if req.isIntrospection is there just carry on or do the actual processing otherwise.

Happy coding :)

Upvotes: -3

Daniel Rearden
Daniel Rearden

Reputation: 84657

There's a ton of different ways to handle authorization in GraphQL, as illustrated in the docs:

  • Adding middleware for express (or some other framework like hapi or koa)
  • Checking for authorization inside individual resolvers
  • Checking for authorization inside your data models
  • Utilizing custom directives

Adding express middleware is great for preventing unauthorized access to your entire schema. If you want to allow unauthenticated access to some fields but not others, it's generally recommended you move your authorization logic from the framework layer to the GraphQL or data model layer using one of the methods above.

Upvotes: 1

Related Questions