moleculerjs / moleculer-web

:earth_africa: Official API Gateway service for Moleculer framework
http://moleculer.services/docs/moleculer-web.html
MIT License
293 stars 119 forks source link

How to add auth to only some service actions? #227

Closed santo74 closed 3 years ago

santo74 commented 3 years ago

Hi,

I have following requirements:

I have several services where each service has multiple actions. Some actions will be accessible via an API gateway, some not (they are private). Some of the actions accessible via the API getaway should only be accessible after auth, others are public.

My API should have this structure:

/api/<version>/public/<service>/<action>
/api/<version>/protected/<service>/<action>

What works:

The closest I'm getting is with this code:

// api.js
const HTTPServer = require("moleculer-web");

module.exports = {
    name: "api",

    mixins: [HTTPServer],

    settings: {
        port: process.env.PORT || 8080,

        routes: [
            {
                path: "/api/public",
                whitelist: [
                    "v1.public.**"
                ]
            }
            ,{
                path: "/api/protected",
                authentication: true,
                authorization: true,
                whitelist: [
                    "v1.protected.**"
                ]
            }
        ]
    }
};
// public/service.js
module.exports = {
    name: "public.service",
    version: 1,

    actions: {
        async login(ctx) {
            return "public action";
        }
    }
};
// protected/service.js
module.exports = {
    name: "protected.service",
    version: 1,

    actions: {
        async login(ctx) {
            return "protected action";
        }
    }
};

The problem:

This works, but it results in ugly paths for the api's:

/api/public/<version>/public/<service>/<action>
/api/protected/<version>/protected/<service>/<action>

So there are actually 2 problems with this approach, but I don't know how to fix this with moleculer(-web):

  1. I have to split each service in 2 separate services in order to add auth to only some actions of a service at the gateway level
  2. The API paths are ugly, they should be flattened in some way
icebob commented 3 years ago

Check this example how it controls which actions are protected https://github.com/moleculerjs/moleculer-examples/blob/master/conduit/services/api.service.js#L97 https://github.com/moleculerjs/moleculer-examples/blob/master/conduit/services/articles.service.js#L83

santo74 commented 3 years ago

Thanks for the pointers icebob! So if I understand correctly, the suggestion is to enable authorization for the whole API (i.e. the /api route) but then override this behaviour in the authorize implementation based on the auth flag of each action?

This sounds good, but the only thing I'm worried about with this implementation is that each action is public by default, unless an explicit auth: true flag is set. So whenever one forgets to set this flag, the action is exposed via the gateway.

But I suppose I can revert that behaviour by throwing an UnAuthorizedError when auth fails, unless the auth flag of the action is explicitly set to false (auth: false)?

icebob commented 3 years ago

In this case, change the logic, and rename the prop to noAuth: true and swap the condition in the authorization, as well.

santo74 commented 3 years ago

Yes, noAuth: true is better than auth:false

Anyway, I tested this setup and it's working as expected. So I'm closing the issue...

Thanks @icebob