I'm new to Spring boot + GraphQL. I need to get the Query/Mutation operation name inside my controller class.
Purpose : Need to grand permission to some users to specific mutation/Query operations. Here the user type will be passed as a request header and will be validated and check whether the user is allowed to access that operation.
@PostMapping
public ResponseEntity<Object> callGraphQLService(@RequestBody String query, @RequestHeader("user") String userName) {
ExecutionResult result = graphService.getGraphQL().execute(ExecutionInput.newExecutionInput()
.query(query)
.context(userName)
.build());
return new ResponseEntity<>(result, HttpStatus.OK);
}
Suggest any efficient mechanism to perform authorization for specific Query/Mutation
I think you're thinking of authorization in REST terms here, and it doesn't map well to GraphQL. Instead of a single decision at the top level based on the operation name (or based on the URL in REST), you need a more granular approach. You need to know who's allowed to see/do what at the field level, as the client is allowed make ad-hoc selections.
There's multiple ways to do this, but since you mentioned Spring, you can simply use Spring Security at the service level. If each protected field is backed by a service method (and it should be), you can protect those methods using Spring Security as usual.
Even better, you should also provide a custom
GraphqlFieldVisibility
implementation, so that unauthorized clients can't even know about the the existence of fields they're not allowed to see in the schema. You can use e.g. Spring'sSpelExpressionParser
to make decisions on what parts of the schema are visible dynamically, for each user, based on Spring Security rules.If Spring Security is not an option, you can implement a custom
Instrumentation
(e.g. by extendingSimpleInstrumentation
). There you can implement the callbacks likebeginExecuteOperation
, that will give you access to the parsed query (enough if you really just want to do REST-style top-level auth only), orbegin(Deferred)Field
(which gives you access to theFieldDefinition
) orbeginFieldFetch/instrumentDataFetcher
(which gives you access to the entireDataFetchingEnvironment
) to perform auth per-field.If you go this way, you can keep the auth information (e.g. the required roles) in the field definition itself as directives. And keep the currently logged in user in the shared context. This way you always have everything you need to do authentication at each level.
In all cases, it's advisable to provide
GraphqlFieldVisibility
to completely hide the existence of the protected fields contextually.Here's an abstract example showing the major points using the
Instrumentation
approach (as you need nothing special for the Spring Security approach, just use Spring Security as usual):You don't have to store the required roles in the directives, it's just a convenient place. You can get the same info from an external source if it's appropriate.
Then register this instrumentation:
And when executing a query, put the current user into the context:
This way you have everything neatly separated (no auth logic in resolvers, no external context needed) and contextual per field, even without using Spring Security.
Let's go further and make a custom
GraphqlFieldVisibility
:As you see, visibility depends on the user, which this time you can not get from the context, so you have to instantiate it per request. This means you need to transform the schema and instantiate GraphQL per request as well. The rest is the same.
With that, you have a full security setup. Unauthorized users won't even know a field exists if they're not allowed to. If they're allowed to see it in general, but they can only fetch it conditionally, the
AuthInstrumentation
covers it.