moleculerjs/moleculer-apollo-server

serverOptions context - How do you auth?

itacirgabral opened this issue · 5 comments

when using standard apigateway you can authorizes like this. Graphql has a flexible approach

how do you auth apollo mixin?

I try here, the master branch is without auth, but the auth branch have the issue.

the serverOptions attribute is the options object and contex function should create the context shared across all resolvers in vanilla apollo.

if you create any context function, even ()=>{}, the playground throw "Cannot read property 'call' of undefined"

"use strict";

const ApiGateway 	= require("moleculer-web");
const { ApolloService } = require("moleculer-apollo-server");

module.exports = {
    name: "api",

    mixins: [
        ApiGateway,

        ApolloService({
            typeDefs: ``,
            resolvers: {},
            routeOptions: {
                path: "/graphql",
                cors: true,
                mappingPolicy: "restrict"
            },
            // https://www.apollographql.com/docs/apollo-server/v2/api/apollo-server.html
            serverOptions: {
                tracing: true,
                context: ({ req }) => ({ admin: req.headers.authorization === "admin"})
            }
        })
    ]
};
{
  "errors": [
    {
      "message": "Cannot read property 'call' of undefined",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "hello"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "stacktrace": [
            "TypeError: Cannot read property 'call' of undefined",
            "    at /home/mu/Documents/moleculerapolloauth/node_modules/moleculer-apollo-server/src/service.js:114:33",
            "    at field.resolve (/home/mu/Documents/moleculerapolloauth/node_modules/graphql-extensions/dist/index.js:133:26)",
            "    at resolveFieldValueOrError (/home/mu/Documents/moleculerapolloauth/node_modules/graphql/execution/execute.js:467:18)",
            "    at resolveField (/home/mu/Documents/moleculerapolloauth/node_modules/graphql/execution/execute.js:434:16)",
            "    at executeFields (/home/mu/Documents/moleculerapolloauth/node_modules/graphql/execution/execute.js:275:18)",
            "    at executeOperation (/home/mu/Documents/moleculerapolloauth/node_modules/graphql/execution/execute.js:219:122)",
            "    at executeImpl (/home/mu/Documents/moleculerapolloauth/node_modules/graphql/execution/execute.js:104:14)",
            "    at Object.execute (/home/mu/Documents/moleculerapolloauth/node_modules/graphql/execution/execute.js:64:35)",
            "    at /home/mu/Documents/moleculerapolloauth/node_modules/apollo-server-core/dist/requestPipeline.js:238:46",
            "    at Generator.next (<anonymous>)",
            "    at /home/mu/Documents/moleculerapolloauth/node_modules/apollo-server-core/dist/requestPipeline.js:8:71",
            "    at new Promise (<anonymous>)",
            "    at __awaiter (/home/mu/Documents/moleculerapolloauth/node_modules/apollo-server-core/dist/requestPipeline.js:4:12)",
            "    at execute (/home/mu/Documents/moleculerapolloauth/node_modules/apollo-server-core/dist/requestPipeline.js:217:20)",
            "    at Object.<anonymous> (/home/mu/Documents/moleculerapolloauth/node_modules/apollo-server-core/dist/requestPipeline.js:157:42)",
            "    at Generator.next (<anonymous>)"
          ]
        }
      }
    }
  ],
  "data": {
    "hello": null
  }
}

Is the apollo context accessed as moleculer meta?

// https://github.com/itacirgabral/moleculerapolloauth/blob/auth/services/greeter.service.js
handler(ctx) {
    return `Hello ${ctx.meta.admin ? 'Boss' : 'Dude'}`;
}

I'm guessing that your problem mutating context is that you're not implementing what moleculer-apollo-server expects to exist on context. You can override the context that is automatically generated here but if you don't implement everything it does you'll be likely to cause failures somewhere else in the stack.

With that said, I might recommend a totally different approach.

For authentication the moleculer API gateway should still work fine. That is, implement an authenticate method and do whatever is necessary for your environment for authentication. I would recommend returning something which will automatically mutate the user property of moleculer's ctx.meta. You can also mutate the meta in other ways here -- we mutate it further to add identifying information for the workspace being requested for a multi-tenancy environment.

If your authorization is dead-simple and does not require any information from what GraphQL query or mutation is being executed, then you can likely leave it in the Api gateway. However, most authorization needs will be more complex and you'll want to apply authorization to the actual query, mutation, type, or property of a type. For accomplishing that, I would recommend a custom directive approach as described here and here.

Since moleculer-apollo-server augments apollo's context with the moleculer context, you'll be able to access the moleculer context in the custom directive. If you'll added information to the meta, you'll then be able to access it at context.ctx.meta within the custom directive. This is the approach we have taken, and it allows us to apply authorization rules at the type, query, mutation, or property of a type level.

do whatever is necessary for your environment for authentication
(...)
then you can likely leave it in the Api gateway

You can define the authorize method in apigateway export object. but you still need to set authorization to true in the router definition. The path /graphql have no apigateway router definitions.

That setup don't work:

"settings": {
  "routes":[{
    "path":"/graphql",
    "authorization": true,
  }]
}

how to enable authorization method of apigateway over the graphql path?


moleculer-apollo-server augments apollo's context with the moleculer context

That service handle function receive the context parameter and it have not a ctx attribute. The service code have access to moleculer vanilla ctx. How can the handler access apollo context?

My configuration is:

ApolloService({
...
   routeOptions: {
        path: '/graphql',
        authentication: true,
        cors: true,
        mappingPolicy: 'restrict',
    }
})

And it working well

This is the approach we have taken, and it allows us to apply authorization rules at the type, query, mutation, or property of a type level.

Do you have a small code example how (simple moleculer service)?

No activity on this issue in several months. Closing for now.