Thanks to Spring magic, it's quite easy to secure a GraphQL server with OAuth. It's just a matter of adding some Spring beans, and let the Spring Boot autoconfiguration work.
The only issue is to test your server, as graphiql won't be able to generate an OAuth token.
This page won't detail more the OAuth protocol. You'll find more information on the web. And a good idea is to start by OAuth presentation.
This page is based on the graphql-maven-plugin-samples-allGraphQLCases-client sample.
This sample can be executed, when the servers below are started. They are available in the maven plugin project:
Here is an important notice about the HTTP status code that you may encounter here:
As usual, it may happen (hum, ok, it always happens :) ) that some issue prevents your code to work as expected. So here are some useful commands, to check if the whole system is properly configured or not.
All these commands are based on the graphql-maven-plugin-samples-allGraphQLCases resource server, and the graphql-maven-plugin-samples-OAuth-authorization-server authorization server, as provided in the maven GraphQL plugin project.
This commands:
A sample query, to get an OAuth token:
curl -u "clientId:secret" -X POST "http://localhost:8181/oauth/token?grant_type=client_credentials" --noproxy "*" -i
Then, reuse the previous token in the next query:
curl -i -X POST "http://localhost:8180/graphql" --noproxy "*" -H "Authorization: Bearer 8c8e4a5b-d903-4ed6-9738-6f7f364b87ec"
And, to check the token:
curl -i -X GET "http://localhost:8181/profile/me" --noproxy "*" -H "Authorization: Bearer 8c8e4a5b-d903-4ed6-9738-6f7f364b87ec"
Of course, in order to make all this work, you need some code in your classpath.
If you included com.graphql-java-generator:graphql-java-server-dependencies:pom, in your dependencies, everything should be ok. This includes this dependencies:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.4.0</version> </dependency>
There is an issue here: take care at the version for the spring-security-oauth2-autoconfigure dependency. As of 2.4.0, its version is not managed. That is: there is no default value for it. So you must give its version a value. The issue is that this version must the same as the version for the other versions of Spring Boot autoconfiguration.
So, when you update the graphql plugin version, take a look at the effective pom, and check the org.springframework.boot:spring-boot-autoconfigure version. In eclipse, once a pom file is open, you see the effective pom by clicking on Effective POM, in the lower part of the window.
You can also check the pom.xml file in github, but take care that this is the last version, and it may be "in advance", compared to the plugin version you're using.
To configure OAuth2, there are tons of documentations on the web. But you should start from this sample. Below is an extract of the application.yml file of the graphql-maven-plugin-samples-allGraphQLCases-server module. This file must on the root of the resource folder. Here it is:
# Changing the port for the GraphQL server server: port: 8180 # https://docs.spring.io/spring-security-oauth2-boot/docs/current/reference/html5/#oauth2-boot-resource-server-token-info security: oauth2: client: client-id: clientId client-secret: secret resource: introspection-uri: http://localhost:8181/oauth/check_token
The first part is the GraphQL configuration part.
The second part deals with OAuth, and how to access the authorization server:
You'll find tons of docs on the web, on how to configure the Spring for other kind of tokens, including JWT.
To enable OAuth on server side, it's just a matter of activating and configuring Spring Security. To do that, you add a class marked by the @Component annotation, in a subpackage where the code is generated. That is:
Don't forget the @Component annotation!
Here is a sample:
package org.allGraphQLCases.server.oauth2; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.oauth2.resource.FixedAuthoritiesExtractor; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * * @author etienne-sf * @see https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/#servlet-authorization-filtersecurityinterceptor */ @Configuration @EnableWebSecurity public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Value("${security.oauth2.resource.introspection-uri}") String introspectionUri; @Value("${security.oauth2.client.client-id}") String clientId; @Value("${security.oauth2.client.client-secret}") String clientSecret; FixedAuthoritiesExtractor s; @Override protected void configure(HttpSecurity http) throws Exception { http // Disabling CORS and CSRF makes POST on the graphql URL work properly. Double-check that before entering in production .cors().and().csrf().disable() .authorizeRequests(authz -> authz .antMatchers(HttpMethod.GET, "/graphql/subscription").authenticated()// .access("hasRole('ROLE_CLIENT')") .antMatchers(HttpMethod.POST, "/graphql").authenticated()// .access("hasRole('ROLE_CLIENT')") .antMatchers(HttpMethod.GET, "/graphiql").permitAll() // All other URL accesses are prohibited .anyRequest().denyAll()) .oauth2ResourceServer(oauth2 -> oauth2.opaqueToken( // When all lines below are commented, then a custom OpaqueTokenIntrospector is used. See CustomAuthoritiesOpaqueTokenIntrospector token -> token .introspectionUri(this.introspectionUri) .introspectionClientCredentials(this.clientId, this.clientSecret) ) ); } public String getIntrospectionUri() { return introspectionUri; } public void setIntrospectionUri(String introspectionUri) { this.introspectionUri = introspectionUri; } public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public String getClientSecret() { return clientSecret; } public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } }
For more information, don't hesitate to browse the web: there are tons of information on how to manage this, with Spring.
And the GraphQL server is just a standard Spring App!