Apollo-server: Authentication per Resolver with HapiJS 17.5.2 & Apollo Server Hapi 2.0.0-rc.10

Created on 8 Aug 2018  路  12Comments  路  Source: apollographql/apollo-server

Hi team 馃憢,

I'm trying to configure Hapi and Apollo Server here and struggling to figure out the best way to use Hapi Authentication Strategies on resolvers. The documentation is pretty thin for this package and I can't seem to find any pointers anywhere I've looked.

Any help definitely appreciated, I wish I could provide more context.

Most helpful comment

Yes, so you'll need to pass the route options into the ApolloServer constructor. I reckon your code looks somethings like this:

    const server = new ApolloServer({
      // You'll need to pass the request object and optionally the repsponse toolkit.
      context: (request: Request, h: ResponseToolkit) => ({ request, h }),
      // Route options for the /graphql route. 
      route: {
        options: {
          auth: {
            // This option will determine what HapiJS will do with your default strategy.
            // Try means it will tty to authenticate, but on failure it won't block the request.
            // By passing the request object into your context, you can access it inside of your resolvers
            // and check the request.auth object for authentication properties.
            mode: 'try'
          }
        },
      },
      schema: schema,
      introspection: process.env.NODE_ENV === 'development'
    });

    await server.applyMiddleware({ app });
    await server.installSubscriptionHandlers(app.listener);

See https://hapijs.com/tutorials/auth for info on the request.auth object, and what route strategies are available. (try, optional and required)

All 12 comments

What are you trying to do? I've set the authentication strategy on my graphql route as try, so I can determine whether or not a user is authenticated per resolver. (You'll need to pass the request in the context in order to do that.)

Can you explain what you mean by GraphQL route - and perhaps show an example of how you've achieved this with the versions I have mentioned?

The way I had set up the ApolloServer was not as a route but at the top level of the app as per the documentation. It autogenerates the /graphql route.

Yes, so you'll need to pass the route options into the ApolloServer constructor. I reckon your code looks somethings like this:

    const server = new ApolloServer({
      // You'll need to pass the request object and optionally the repsponse toolkit.
      context: (request: Request, h: ResponseToolkit) => ({ request, h }),
      // Route options for the /graphql route. 
      route: {
        options: {
          auth: {
            // This option will determine what HapiJS will do with your default strategy.
            // Try means it will tty to authenticate, but on failure it won't block the request.
            // By passing the request object into your context, you can access it inside of your resolvers
            // and check the request.auth object for authentication properties.
            mode: 'try'
          }
        },
      },
      schema: schema,
      introspection: process.env.NODE_ENV === 'development'
    });

    await server.applyMiddleware({ app });
    await server.installSubscriptionHandlers(app.listener);

See https://hapijs.com/tutorials/auth for info on the request.auth object, and what route strategies are available. (try, optional and required)

I get an error if providing the configuration above that route is not valid for ApolloServer type,

Argument of type '{ context: (request: Request, h: ResponseToolkit) => { request: Request; h: ResponseToolkit; }; o...' is not assignable to parameter of type 'Config'.
  Object literal may only specify known properties, and 'options' does not exist in type 'Config'.

I've seen lots of people with this configuration in their Apollo Server but no idea how they're doing it if it's breaking with TS.

@inthedeepend You're right, the route specific configuration should be with the applyMiddleware function!

I've got it working for me like this:

    const server = new ApolloServer({
      // You'll need to pass the request object and optionally the repsponse toolkit.
      context: (request: Request, h: ResponseToolkit) => ({ request, h })
      schema: schema,
      introspection: process.env.NODE_ENV === 'development'
    });

    await server.applyMiddleware({ app,
      // Route options for the /graphql route. 
      route: {
        auth: {
          // This option will determine what HapiJS will do with your default strategy.
          // Try means it will tty to authenticate, but on failure it won't block the request.
          // By passing the request object into your context, you can access it inside of your resolvers
          // and check the request.auth object for authentication properties.
          mode: 'try'
        }
      }, });
    await server.installSubscriptionHandlers(app.listener);

    await server.start();

@wesselvdv I am still seeing TS errors with the 2nd approach.

Here is the applyMiddlware signature:

(method) ApolloServer.applyMiddleware({ app, cors, path, disableHealthCheck, onHealthCheck, }: ServerRegistration): Promise<void>

Passing a route, throws the TS error. Is this maybe an issue with the TS types?

@apollomusa Which version of apollo-server-hapi are you using?

@wesselvdv "apollo-server-hapi": "^2.0.0-rc.10",

@apollomusa I was using the latest version, I reckon that's why you're having such issues. Consider upgrading to 2.0.4. Then the types should also be corresponding to what I am using.

@wesselvdv suggestion actually worked pretty good for me!
This is my code:

const { ApolloServer } = require('apollo-server-hapi');
const Schemas = require('./schemas');

const AUTH_STRATEGY = 'basicAuth';

async function createServer(app) {
    const apolloServer = new ApolloServer({
        typeDefs: Schemas.typeDefs,
        resolvers: Schemas.resolvers,
        context: ({ request }) => ({ credentials: request.auth.credentials }),
    });

    await apolloServer.applyMiddleware({
        app,
        route: {
            auth: AUTH_STRATEGY,
        },
    });

    await apolloServer.installSubscriptionHandlers(app.listener);
}

module.exports = {
    createServer,
};

This appears to be resolved so I'm going to close this issue! Please feel free to re-open the issue if it's still a problem. Thanks!

Hopefully, https://github.com/apollographql/apollo-server/issues/1499#issuecomment-467281691 helped you.

We won't be directly maintaining integrations with specific frameworks in Apollo Server 3, so closing this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

veeramarni picture veeramarni  路  3Comments

hiucimon picture hiucimon  路  3Comments

nevyn-lookback picture nevyn-lookback  路  3Comments

espoal picture espoal  路  3Comments

dbrrt picture dbrrt  路  3Comments