Skip to content

Commit

Permalink
Configure sample with Mutual-TLS client certificate-bound access tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
jgrandja committed Apr 12, 2024
1 parent f1e6ec1 commit ff4b542
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.web.SecurityFilterChain;
Expand Down Expand Up @@ -179,6 +180,11 @@ public JdbcRegisteredClientRepository registeredClientRepository(JdbcTemplate jd
.jwkSetUrl("http://127.0.0.1:8080/jwks")
.build()
)
.tokenSettings(
TokenSettings.builder()
.x509CertificateBoundAccessTokens(true)
.build()
)
.build();

// Save registered client's in db as if in-memory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,24 @@
import java.util.Arrays;
import java.util.function.Supplier;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.SslProvider;
import sample.authorization.DeviceCodeOAuth2AuthorizedClientProvider;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
Expand Down Expand Up @@ -54,11 +65,15 @@
public class WebClientConfig {

@Bean("default-client-web-client")
public WebClient defaultClientWebClient(OAuth2AuthorizedClientManager authorizedClientManager) {
public WebClient defaultClientWebClient(
OAuth2AuthorizedClientManager authorizedClientManager,
SslBundles sslBundles) throws Exception {

ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
// @formatter:off
return WebClient.builder()
.clientConnector(createClientConnector(sslBundles.getBundle("demo-client")))
.apply(oauth2Client.oauth2Configuration())
.build();
// @formatter:on
Expand All @@ -69,7 +84,8 @@ public WebClient selfSignedDemoClientWebClient(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository,
RestTemplateBuilder restTemplateBuilder,
@Qualifier("self-signed-demo-client-http-request-factory") Supplier<ClientHttpRequestFactory> clientHttpRequestFactory) {
@Qualifier("self-signed-demo-client-http-request-factory") Supplier<ClientHttpRequestFactory> clientHttpRequestFactory,
SslBundles sslBundles) throws Exception {

// @formatter:off
RestTemplate restTemplate = restTemplateBuilder
Expand Down Expand Up @@ -98,6 +114,7 @@ public WebClient selfSignedDemoClientWebClient(
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
// @formatter:off
return WebClient.builder()
.clientConnector(createClientConnector(sslBundles.getBundle("self-signed-demo-client")))
.apply(oauth2Client.oauth2Configuration())
.build();
// @formatter:on
Expand Down Expand Up @@ -143,6 +160,22 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
return authorizedClientManager;
}

private static ClientHttpConnector createClientConnector(SslBundle sslBundle) throws Exception {
KeyManagerFactory keyManagerFactory = sslBundle.getManagers().getKeyManagerFactory();
TrustManagerFactory trustManagerFactory = sslBundle.getManagers().getTrustManagerFactory();

// @formatter:off
SslContext sslContext = SslContextBuilder.forClient()
.keyManager(keyManagerFactory)
.trustManager(trustManagerFactory)
.build();
// @formatter:on

SslProvider sslProvider = SslProvider.builder().sslContext(sslContext).build();
HttpClient httpClient = HttpClient.create().secure(sslProvider);
return new ReactorClientHttpConnector(httpClient);
}

private static OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> createClientCredentialsTokenResponseClient(
RestTemplate restTemplate) {
DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =
Expand Down
2 changes: 1 addition & 1 deletion samples/demo-client/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ spring:
token-uri: https://localhost:9443/oauth2/token

messages:
base-uri: http://127.0.0.1:8090/messages
base-uri: https://127.0.0.1:8443/messages

user-messages:
base-uri: http://127.0.0.1:8091/user/messages
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
@Configuration(proxyBeanMethods = false)
public class ResourceServerConfig {

/*
NOTE:
The `NimbusJwtDecoder` `@Bean` autoconfigured by Spring Boot will contain
an `OAuth2TokenValidator<Jwt>` of type `X509CertificateThumbprintValidator`.
This is the validator responsible for validating the `x5t#S256` claim (if available)
in the `Jwt` against the SHA-256 Thumbprint of the supplied `X509Certificate`.
*/

// @formatter:off
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.config;

import org.apache.catalina.connector.Connector;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* @author Joe Grandja
* @since 1.3
*/
@Configuration(proxyBeanMethods = false)
public class TomcatServerConfig {

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> connectorCustomizer() {
return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createHttpConnector());
}

private Connector createHttpConnector() {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(8090);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}

}
20 changes: 19 additions & 1 deletion samples/messages-resource/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
server:
port: 8090
port: 8443
ssl:
bundle: messages-resource
client-auth: need

logging:
level:
Expand All @@ -10,6 +13,21 @@ logging:
# org.springframework.boot.autoconfigure: DEBUG

spring:
ssl:
bundle:
jks:
messages-resource:
key:
alias: messages-resource-sample
password: password
keystore:
location: classpath:keystore.p12
password: password
type: PKCS12
truststore:
location: classpath:keystore.p12
password: password
type: PKCS12
security:
oauth2:
resourceserver:
Expand Down

0 comments on commit ff4b542

Please sign in to comment.