-
Notifications
You must be signed in to change notification settings - Fork 234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Federation gateway authentication and authorization #343
Comments
I've been doing some research on the topic. Most of this revolves around https://www.openpolicyagent.org/ and https://casbin.org/. If'd like to work on this, I'll be happy to create a new repo in the org... I think this one has way too many features already. I think we might need to define how the auth token is modelled - or maybe you are already assuming that we'd use JWT. In the case, we developed https://github.com/nearform/fast-jwt for a very similar purpose - it's a similar technique to the one used in the AWS API Gateway. |
I like the idea of having different auth strategies that the user can choose. We could start with the most common-auth method to verify the user token and to decorate the request context with the credentials. Export the GraphQL directives to use in the schema definition, and as you suggested using something like open policy or casbin to define and check the RBAC. All of that could live in the new auth repo, but regarding the user RBAC's comparison with the requested operation, I still haven't properly understood how/where to do it!? I assume that we have to do it in this repo somewhere in the gateway resolvers creation or we need to have a hook system in place from where we can extend the gateway "query planner". |
I think you'll need to add some hooks to plugin into some of the internals to achieve this. It's fine and we should definitely add them when they are needed. |
Hi, I'm from Casbin team:) I noticed that Nearform has just released two plugins for Casbin based authorization. Does it help on our GraphQL scenario? |
@mcollina should we start defining the events to emit in mercurius? You mentioned that all the hooks logic, it's really similar to the work already done for Fastify, and that we can reuse a lot from there. @hsluoyz I think this is exactly what we should do here too, adapting it to the GraphQL operation instead that to a route based service. cc @projectjudge @aleccool213 |
That's a good approach! |
Hi! I've been starting to look at this in detail based on @adamovittorio 's suggestion and have come up with an implementation suggestion for it. Let me know what you think! The general approachRegister a plugin after Mercurius is registered with user defined checks and directives in options: const schema = `
type Query {
add(x: Int, y: Int): Int
}
`;
const resolvers = {
Query: {
add: async (_, { x, y }) => x + y,
},
};
app = Fastify();
app.register(mercurius, { schema, resolvers });
app.register(mercuriusAuthPlugin, options); Utilise the new Mercurius hooks to run the authn/authz checks:
This will be provided in the configuration at the same time as the auth strategy and should allow total flexibility. The plugin will use this Directive definition to look for identifiers in the service schema. We can add a config option for Mercurius to load this plugin and define a custom auth strategy with associated SDL. Add Mercurius pluginUsing the fastify plugin framework, register the plugin as normal (but check that mercurius is enabled beforehand). Something along the lines of: fp(async (fastify, options) => {
if (typeof fastify.graphql !== 'function') {
throw new Error('No graphql plugin registered.');
}
// Rest of plugin initialisation
}); Options and their usagesGet auth contextThis will run in Schema directiveThe plugin will require a new “auth” Directive that allows one to define the policies required for a field. This option tells us what schema directive to look for in the destination schema for each service. Note, we will need to define the schema directive but also associated types so they are valid when added to the service schema. Both gateway and services will need to define (at least) the directive. Authz checksThis would happen in Depending on the Directive, we will be able to run the defined auth strategy. When it runs:
Once completed, execute the (adjusted in the case of failures) Query as normal and resolve the response as normal Note, I think there is some complexity to be careful with from both an implementation and performance point of view in the schema comparison with the query document. In addition, for reference types, we will need to be careful here as well. Alternative approaches and open questions
Let me know what you think! As mentioned, I'm very happy to put this together once everyone is agreed and if that works for everyone! :) |
I think we should also support authorizing local resolvers, not just remote servers. Apart from that, go for it. Would you like me to create a new repo in this org? |
No problem, I'll make sure that's included!
Awesome, thanks! :) Yes please, if that's okay with you? |
Hey @mcollina, sorry for the bother - I just wondered if you'd had a chance to create a new repo for this in the org? Cheers! :) |
Thanks very much! :) |
Your solution would be very nice. How I am doing this is a little bit different. I am using custom authChecker middleware in federated services, it parses RBAC headers. Then field level @Authorized() directive calls the authChecker where the RBAC logic is defined. But I am not sure how to pass RBAC headers to downstream services from the gateway. In apollo federation its possible to achieve it with context linking, not sure how to do it with mercurius. |
@valdestron thanks, I'm getting started on it asap :) In Mercurius gateway and depending on how you are generating the RBAC headers, you can set the There is also this issue which may interest you. It proposes to add |
Yes I saw this issue with rewriteHeaders. I am having a problem of generating RBAC headers using rewriteHeaders as I need to access some remote Identity Provider like Auth0 and caching like Redis, before forwarding RBAC headers to downstream. If your solution would be in place, there would be three auth options IMO using mercurius gateway:
Seems that until context issue or your solution is done. We do not have any other way but to reimplement RBAC in each service. |
As of release 7.4.0 (Apr 9th, 2021) gateway: {
services: [
{
name: '...',
url: '...',
rewriteHeaders: (headers, context) => {
return { 'x-custom-header': 'custom-header-value' }
},
},
];
} |
@jonnydgreen Do you have a small example of using OpenTelemetry with gateway and federated services? I am working on this right now and am having issues. |
@dragonfriend0013 I don't have an example I can share unfortunately but I can point you in the right direction for sure and am happy to help get it working for you! :) The Mercurius docs are a good starting point for basic OTEL tracing on a Mercurius server: https://mercurius.dev/#/docs/integrations/open-telemetry Once you have this (or equivalent) up and running, if you wanted to get distributed tracing working in the gateway and federated services, you can use a combination of hooks, OTEL propagation API and rewrite headers. The idea being to:
|
@jonnydgreen I just copied the example from https://mercurius.dev/#/docs/integrations/open-telemetry and had to make a change in the tracer.js (HttpTraceContext undefined, changed to HttpTraceContextPropagator), Now i am getting |
Yeah, I was about to say it sounds like a version issue from the error you're describing - looks like 0.13.0 has been released as of 2 hours ago, let me know if that works for you: https://www.npmjs.com/package/@autotelic/fastify-opentelemetry?activeTab=readme |
If authentication and authorization via SDL becomes a thing with Mercurius, will it be 100% optional? Scott |
This is available as a plugin via |
I am using mercurius-auth on my different services. as per the docs i have added
and have coded my specific code to do the authorization. this works per service. however when i use mercurius gateway to stitch the services together, i get duplicates of the @auth directive and this is causing the parsing to fail.
relevant part of composed gateway schema:
does anyone have any suggestions, or is this a bug in gateway mode? |
I've written a quick test and can confirm that it's an issue in gateway mode: https://github.com/jonnydgreen/mercurius/blob/bugfix/duplicate-directives/test/gateway/custom-directives.js Test result: Schema: From the above, we can see that the service directives are duplicated in the gateway, which correlates with what you are seeing. An initial fix would be to de-duplicate these directives when the schema is constructed; however I'd be concerned about how we might handle directives of the same name but different definition across services. I think there are several options to account for this situation (differing directive definitions with the same name):
As this isn't related to auth at the gateway level and rather, a generic issue for the gateway, I wonder if it's worth us creating a draft PR/issue and working through a solution there? |
I would recommend taking the first directive. I think we might want to supoprt a few things:
It is also ok to error if services could not be composed together. |
Could you open a PR? |
Yeah no problem! I'll start that this evening |
@dragonfriend0013 a fix for this is now shipped in https://github.com/mercurius-js/mercurius/releases/tag/v8.1.3 :) |
I would like to handle authentication and authorization per GraphQL operation at the Gateway level.
This is quite a complex feature, and I don't have a clear solution in mind; I would like to start the discussion for a possible implementation method.
General considerations
Prerequisites
Flow
The text was updated successfully, but these errors were encountered: