Skip to content

Commit dbe9aca

Browse files
committed
Add OAuth2 support
1 parent c969b6d commit dbe9aca

13 files changed

Lines changed: 198 additions & 14 deletions

File tree

README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,45 @@
1-
# graphql-spring-webclient
1+
# GraphQL Spring Webclient
2+
3+
Reactive GraphQL client for consuming GraphQL APIs from a Spring Boot application.
4+
Provides OAuth2 authorization through configuration.
5+
6+
## Getting started
7+
8+
Add the starter to your project.
9+
10+
When using Maven:
11+
```xml
12+
<dependency>
13+
<groupId>com.graphql-java-kickstart</groupId>
14+
<artifactId>graphql-webclient-spring-boot-starter</artifactId>
15+
<version>1.0.0-SNAPSHOT</version>
16+
</dependency>
17+
```
18+
19+
When using gradle:
20+
```groovy
21+
implementation "com.graphql-java-kickstart:graphql-webclient-spring-boot-starter:1.0.0-SNAPSHOT"
22+
```
23+
24+
Configure at least the URL of the GraphQL API to consume:
25+
```yaml
26+
graphql:
27+
client:
28+
url: https://graphql.github.com/graphql
29+
```
30+
31+
## Configuration
32+
33+
The following tables list the configurable properties of the GraphQL Spring Webclient and their default values.
34+
These properties are configured with the prefix `graphql.client`, e.g. the property listed in the table as `url`
35+
should be defined as `graphql.client.url` in your Spring Boot configuration files.
36+
37+
| Property | Description |
38+
|----------|-------------|
39+
| url | Full URL of the GraphQL API to connect to, e.g. https://graphql.github.com/graphql |
40+
| oauth2.client-id | OAuth2 client id |
41+
| oauth2.client-secret | OAuth2 client secret |
42+
| oauth2.token-uri | Token URI of the identity provider |
43+
| oauth2.authorization-grant-type | By default the grant type `client_credentials` is used |
44+
45+

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ subprojects {
6666

6767
java {
6868
withSourcesJar()
69-
withJavadocJar()
69+
// withJavadocJar()
7070
}
7171

7272
publishing {
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
dependencies {
2-
implementation(project(':graphql-webclient'))
2+
api(project(':graphql-webclient'))
33

44
implementation "org.springframework.boot:spring-boot-autoconfigure"
55
implementation "org.springframework.boot:spring-boot-starter-webflux"
6+
implementation "org.springframework.security:spring-security-oauth2-client"
67
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package graphql.kickstart.spring.webclient.boot;
2+
3+
import lombok.Data;
4+
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
6+
@Data
7+
@ConfigurationProperties("graphql.client")
8+
class GraphQLClientProperties {
9+
10+
private String url;
11+
12+
}

graphql-webclient-spring-boot-autoconfigure/src/main/java/graphql/kickstart/spring/webclient/boot/GraphQLWebClientAutoConfiguration.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,47 @@
11
package graphql.kickstart.spring.webclient.boot;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import lombok.RequiredArgsConstructor;
45
import lombok.extern.slf4j.Slf4j;
56
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
67
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
78
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
9+
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
10+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
811
import org.springframework.context.annotation.Bean;
912
import org.springframework.context.annotation.ComponentScan;
1013
import org.springframework.context.annotation.Configuration;
14+
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
15+
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
16+
import org.springframework.security.oauth2.client.web.server.UnAuthenticatedServerOAuth2AuthorizedClientRepository;
1117
import org.springframework.web.reactive.function.client.WebClient;
1218

1319
@Slf4j
1420
@Configuration
15-
@AutoConfigureAfter(JacksonAutoConfiguration.class)
21+
@RequiredArgsConstructor
22+
@AutoConfigureAfter({JacksonAutoConfiguration.class, OAuth2ClientAutoConfiguration.class})
23+
@EnableConfigurationProperties(GraphQLClientProperties.class)
1624
@ComponentScan(basePackageClasses = GraphQLWebClientImpl.class)
1725
public class GraphQLWebClientAutoConfiguration {
1826

27+
private final GraphQLClientProperties graphqlClientProperties;
28+
1929
@Bean
2030
@ConditionalOnMissingBean
21-
public WebClient webClient() {
22-
return WebClient.builder().build();
31+
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
32+
WebClient.Builder clientBuilder = WebClient.builder()
33+
.baseUrl(graphqlClientProperties.getUrl());
34+
35+
if (clientRegistrations.findByRegistrationId("graphql").blockOptional().isPresent()) {
36+
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
37+
new ServerOAuth2AuthorizedClientExchangeFilterFunction(
38+
clientRegistrations,
39+
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
40+
oauth.setDefaultClientRegistrationId("graphql");
41+
clientBuilder.filter(oauth);
42+
}
43+
44+
return clientBuilder.build();
2345
}
2446

2547
@Bean
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package graphql.kickstart.spring.webclient.boot;
2+
3+
import static java.util.Optional.ofNullable;
4+
5+
import java.util.Set;
6+
import lombok.Data;
7+
import org.springframework.boot.context.properties.ConfigurationProperties;
8+
import org.springframework.context.annotation.Primary;
9+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
10+
import org.springframework.security.oauth2.core.AuthenticationMethod;
11+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
12+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
13+
14+
@Data
15+
@Primary
16+
@ConfigurationProperties(prefix = "graphql.client.oauth2")
17+
class OAuth2ClientRegistrationProperties {
18+
19+
private String provider;
20+
private String clientId;
21+
private String clientSecret;
22+
private String clientAuthenticationMethod;
23+
private String authorizationGrantType = "client_credentials";
24+
private String redirectUri;
25+
private Set<String> scope;
26+
private String clientName;
27+
28+
private String authorizationUri;
29+
private String tokenUri;
30+
private String userInfoUri;
31+
private String userInfoAuthenticationMethod;
32+
private String userNameAttribute;
33+
private String jwkSetUri;
34+
private String issuerUri;
35+
36+
ClientRegistration getClientRegistration() {
37+
ClientRegistration.Builder builder = ClientRegistration.withRegistrationId("graphql")
38+
.clientId(getClientId())
39+
.clientSecret(getClientSecret())
40+
.redirectUriTemplate(getRedirectUri())
41+
.scope(getScope())
42+
.clientName(getClientName())
43+
.authorizationUri(getAuthorizationUri())
44+
.tokenUri(getTokenUri())
45+
.userInfoUri(getUserInfoUri())
46+
.jwkSetUri(getJwkSetUri());
47+
48+
ofNullable(getClientAuthenticationMethod())
49+
.map(ClientAuthenticationMethod::new)
50+
.ifPresent(builder::clientAuthenticationMethod);
51+
52+
ofNullable(getAuthorizationGrantType())
53+
.map(AuthorizationGrantType::new)
54+
.ifPresent(builder::authorizationGrantType);
55+
56+
ofNullable(getUserInfoAuthenticationMethod())
57+
.map(AuthenticationMethod::new)
58+
.ifPresent(builder::userInfoAuthenticationMethod);
59+
60+
return builder.build();
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package graphql.kickstart.spring.webclient.boot;
2+
3+
import static java.util.Collections.singletonList;
4+
5+
import java.util.List;
6+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
7+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
11+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
12+
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
13+
14+
@Configuration
15+
@EnableConfigurationProperties(OAuth2ClientRegistrationProperties.class)
16+
class OAuth2ClientRegistrationRepositoryConfiguration {
17+
18+
@Bean
19+
@ConditionalOnMissingBean({ClientRegistrationRepository.class})
20+
InMemoryReactiveClientRegistrationRepository clientRegistrationRepository(OAuth2ClientRegistrationProperties properties) {
21+
List<ClientRegistration> registrations = singletonList(properties.getClientRegistration());
22+
return new InMemoryReactiveClientRegistrationRepository(registrations);
23+
}
24+
25+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2-
graphql.kickstart.spring.webclient.boot.GraphQLWebClientAutoConfiguration
2+
graphql.kickstart.spring.webclient.boot.GraphQLWebClientAutoConfiguration,\
3+
graphql.kickstart.spring.webclient.boot.OAuth2ClientRegistrationProperties
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies {
2+
api(project(':graphql-webclient-spring-boot-autoconfigure'))
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package graphql.kickstart.spring.webclient.boot;
2+
3+
public class GraphQLClientException extends RuntimeException {
4+
5+
6+
7+
}

0 commit comments

Comments
 (0)