Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,13 @@
"compilerArguments",
"annotationProcessors"
]
},
{
"filename": "sdk/spring/CHANGELOG.md",
"words": [
"AADB",
"JPMS"
]
}
],
"allowCompoundWords": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@
"code": "java.method.numberOfParametersChanged",
"new": "method void com.azure.spring.cloud.autoconfigure.aadb2c.configuration.AadB2cOAuth2ClientConfiguration::<init>(com.azure.spring.cloud.autoconfigure.aadb2c.properties.AadB2cProperties, org.springframework.boot.web.client.RestTemplateBuilder)",
"justification": "Bean configuration will not be used by customer in java code."
},
{
"code": "java.method.numberOfParametersChanged",
"new": "method void com.azure.spring.cloud.autoconfigure.aadb2c.AadB2cAutoConfiguration::<init>(org.springframework.boot.web.client.RestTemplateBuilder)",
"justification": "Bean configuration will not be used by customer in java code."
}
]
}
Expand Down
55 changes: 32 additions & 23 deletions sdk/spring/CHANGELOG.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@
import com.azure.spring.cloud.autoconfigure.aad.implementation.oauth2.OAuth2ClientAuthenticationJwkResolver;
import com.azure.spring.cloud.autoconfigure.aad.implementation.webapi.AadJwtBearerGrantRequestEntityConverter;
import com.azure.spring.cloud.autoconfigure.aad.implementation.webapp.AadAzureDelegatedOAuth2AuthorizedClientProvider;
import com.azure.spring.cloud.autoconfigure.aad.implementation.webapp.AadOidcIdTokenDecoderFactory;
import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthenticationProperties;
import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationServerEndpoints;
import com.azure.spring.cloud.autoconfigure.aad.properties.AadProfileProperties;
import com.nimbusds.jose.jwk.JWK;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
Expand All @@ -36,8 +40,12 @@
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.web.client.RestTemplate;

import static com.azure.spring.cloud.autoconfigure.aad.implementation.AadRestTemplateCreator.createOAuth2AccessTokenResponseClientRestTemplate;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.AadRestTemplateCreator.createRestTemplate;

/**
* <p>
Expand Down Expand Up @@ -93,8 +101,8 @@ public OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository() {
@Conditional(ClientCertificatePropertiesCondition.class)
OAuth2ClientAuthenticationJwkResolver oAuth2ClientAuthenticationJwkResolver(AadAuthenticationProperties properties) {
return new AadOAuth2ClientAuthenticationJwkResolver(
properties.getCredential().getClientCertificatePath(),
properties.getCredential().getClientCertificatePassword());
properties.getCredential().getClientCertificatePath(),
properties.getCredential().getClientCertificatePassword());
}

/**
Expand All @@ -116,21 +124,21 @@ OAuth2AuthorizedClientManager authorizedClientManager(
JwtBearerOAuth2AuthorizedClientProvider jwtBearerProvider,
ObjectProvider<OAuth2ClientAuthenticationJwkResolver> jwkResolvers) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);
new DefaultOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);

OAuth2ClientAuthenticationJwkResolver jwkResolver = jwkResolvers.getIfUnique();
// @formatter:off
OAuth2AuthorizedClientProvider providers =
OAuth2AuthorizedClientProviderBuilder
.builder()
.authorizationCode()
.clientCredentials(builder ->
clientCredentialsGrantBuilderAccessTokenResponseClientCustomizer(builder, jwkResolver))
.password(builder -> passwordGrantBuilderAccessTokenResponseClientCustomizer(builder, jwkResolver))
.provider(refreshTokenProvider)
.provider(jwtBearerProvider)
.provider(azureDelegatedOAuth2AuthorizedClientProvider(refreshTokenProvider, authorizedClients))
.build();
OAuth2AuthorizedClientProviderBuilder
.builder()
.authorizationCode()
.clientCredentials(builder ->
clientCredentialsGrantBuilderAccessTokenResponseClientCustomizer(builder, jwkResolver))
.password(builder -> passwordGrantBuilderAccessTokenResponseClientCustomizer(builder, jwkResolver))
.provider(refreshTokenProvider)
.provider(jwtBearerProvider)
.provider(azureDelegatedOAuth2AuthorizedClientProvider(refreshTokenProvider, authorizedClients))
.build();
// @formatter:on
manager.setAuthorizedClientProvider(providers);
return manager;
Expand All @@ -142,14 +150,14 @@ JwtBearerOAuth2AuthorizedClientProvider azureAdJwtBearerProvider(
ObjectProvider<OAuth2ClientAuthenticationJwkResolver> resolvers) {
JwtBearerOAuth2AuthorizedClientProvider provider = new JwtBearerOAuth2AuthorizedClientProvider();
OAuth2ClientAuthenticationJwkResolver resolver = resolvers.getIfUnique();
AadJwtBearerGrantRequestEntityConverter jwtBearerConverter = new AadJwtBearerGrantRequestEntityConverter();
DefaultJwtBearerTokenResponseClient client = new DefaultJwtBearerTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
AadJwtBearerGrantRequestEntityConverter converter = new AadJwtBearerGrantRequestEntityConverter();
if (resolver != null) {
jwtBearerConverter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));
}
DefaultJwtBearerTokenResponseClient responseClient = new DefaultJwtBearerTokenResponseClient();
responseClient.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
responseClient.setRequestEntityConverter(jwtBearerConverter);
provider.setAccessTokenResponseClient(responseClient);
client.setRequestEntityConverter(converter);
provider.setAccessTokenResponseClient(client);
return provider;
}

Expand All @@ -159,52 +167,63 @@ RefreshTokenOAuth2AuthorizedClientProvider azureRefreshTokenProvider(
ObjectProvider<OAuth2ClientAuthenticationJwkResolver> resolvers) {
RefreshTokenOAuth2AuthorizedClientProvider provider = new RefreshTokenOAuth2AuthorizedClientProvider();
OAuth2ClientAuthenticationJwkResolver resolver = resolvers.getIfUnique();
DefaultRefreshTokenTokenResponseClient client = new DefaultRefreshTokenTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
if (resolver != null) {
OAuth2RefreshTokenGrantRequestEntityConverter converter = new OAuth2RefreshTokenGrantRequestEntityConverter();
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));

DefaultRefreshTokenTokenResponseClient responseClient = new DefaultRefreshTokenTokenResponseClient();
responseClient.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
responseClient.setRequestEntityConverter(converter);
provider.setAccessTokenResponseClient(responseClient);
client.setRequestEntityConverter(converter);
}
provider.setAccessTokenResponseClient(client);
return provider;
}

/**
* Provide {@link JwtDecoderFactory} used in {@link OAuth2LoginConfigurer#init}. The {@link JwtDecoder} created by
* current {@link JwtDecoderFactory} will use {@link RestTemplate} created by {@link RestTemplateBuilder} bean.
*
* @param properties the AadAuthenticationProperties
* @return JwtDecoderFactory
*/
@Bean
@ConditionalOnMissingBean
JwtDecoderFactory<ClientRegistration> azureAdJwtDecoderFactory(AadAuthenticationProperties properties) {
AadProfileProperties profile = properties.getProfile();
AadAuthorizationServerEndpoints endpoints = new AadAuthorizationServerEndpoints(
profile.getEnvironment().getActiveDirectoryEndpoint(), profile.getTenantId());
return new AadOidcIdTokenDecoderFactory(endpoints.getJwkSetEndpoint(), createRestTemplate(restTemplateBuilder));
}

private void passwordGrantBuilderAccessTokenResponseClientCustomizer(
OAuth2AuthorizedClientProviderBuilder.PasswordGrantBuilder builder,
OAuth2ClientAuthenticationJwkResolver resolver) {
DefaultPasswordTokenResponseClient client = new DefaultPasswordTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
if (resolver != null) {
OAuth2PasswordGrantRequestEntityConverter converter = new OAuth2PasswordGrantRequestEntityConverter();
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));

DefaultPasswordTokenResponseClient client = new DefaultPasswordTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
client.setRequestEntityConverter(converter);

builder.accessTokenResponseClient(client);
}
builder.accessTokenResponseClient(client);
}

private void clientCredentialsGrantBuilderAccessTokenResponseClientCustomizer(
OAuth2AuthorizedClientProviderBuilder.ClientCredentialsGrantBuilder builder,
OAuth2ClientAuthenticationJwkResolver resolver) {
DefaultClientCredentialsTokenResponseClient client = new DefaultClientCredentialsTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
if (resolver != null) {
OAuth2ClientCredentialsGrantRequestEntityConverter converter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
new OAuth2ClientCredentialsGrantRequestEntityConverter();
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));

DefaultClientCredentialsTokenResponseClient client = new DefaultClientCredentialsTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
client.setRequestEntityConverter(converter);

builder.accessTokenResponseClient(client);
}
builder.accessTokenResponseClient(client);
}

private AadAzureDelegatedOAuth2AuthorizedClientProvider azureDelegatedOAuth2AuthorizedClientProvider(
RefreshTokenOAuth2AuthorizedClientProvider refreshTokenProvider,
OAuth2AuthorizedClientRepository authorizedClients) {
RefreshTokenOAuth2AuthorizedClientProvider refreshTokenProvider,
OAuth2AuthorizedClientRepository authorizedClients) {
return new AadAzureDelegatedOAuth2AuthorizedClientProvider(refreshTokenProvider, authorizedClients);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ public AadResourceServerConfiguration(RestTemplateBuilder restTemplateBuilder) {
@ConditionalOnMissingBean(JwtDecoder.class)
public JwtDecoder jwtDecoder(AadAuthenticationProperties aadAuthenticationProperties) {
AadAuthorizationServerEndpoints identityEndpoints = new AadAuthorizationServerEndpoints(
aadAuthenticationProperties.getProfile().getEnvironment().getActiveDirectoryEndpoint(), aadAuthenticationProperties.getProfile().getTenantId());
aadAuthenticationProperties.getProfile().getEnvironment().getActiveDirectoryEndpoint(),
aadAuthenticationProperties.getProfile().getTenantId());
NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder
.withJwkSetUri(identityEndpoints.getJwkSetEndpoint())
.withJwkSetUri(identityEndpoints.getJwkSetEndpoint())
.restOperations(createRestTemplate(restTemplateBuilder))
.build();
List<OAuth2TokenValidator<Jwt>> validators = createDefaultValidator(aadAuthenticationProperties);
Expand Down Expand Up @@ -106,7 +107,7 @@ public List<OAuth2TokenValidator<Jwt>> createDefaultValidator(AadAuthenticationP
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnExpression("!'${spring.cloud.azure.active-directory.application-type}'.equalsIgnoreCase('web_application_and_resource_server')")
public static class DefaultAadResourceServerWebSecurityConfigurerAdapter extends
AadResourceServerWebSecurityConfigurerAdapter {
AadResourceServerWebSecurityConfigurerAdapter {

/**
* configure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
* Util class used to create {@link RestTemplate}s for all Azure AD related http requests.
*/
Expand All @@ -27,25 +24,14 @@ public static RestTemplate createRestTemplate(RestTemplateBuilder builder) {
}

public static RestTemplate createOAuth2ErrorResponseHandledRestTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = createRestTemplate(builder);
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
return restTemplate;
builder = builder.errorHandler(new OAuth2ErrorResponseErrorHandler());
return createRestTemplate(builder);
}

public static RestTemplate createOAuth2AccessTokenResponseClientRestTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = createOAuth2ErrorResponseHandledRestTemplate(builder);
List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
if (notContainsElementOfType(converters, FormHttpMessageConverter.class)) {
converters.add(new FormHttpMessageConverter());
}
if (notContainsElementOfType(converters, OAuth2AccessTokenResponseHttpMessageConverter.class)) {
converters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
}
return restTemplate;
}

private static boolean notContainsElementOfType(List<?> list, Class<?> clazz) {
return list.stream().noneMatch(item -> item.getClass().equals(clazz));
builder = builder.messageConverters(
new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter());
return createOAuth2ErrorResponseHandledRestTemplate(builder);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.cloud.autoconfigure.aad.implementation;

import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.net.InetSocketAddress;
import java.net.Proxy;

@Configuration
public class RestTemplateProxyCustomizerConfiguration {

public static final SimpleClientHttpRequestFactory FACTORY = createProxyFactory();

@Bean
public RestTemplateCustomizer proxyRestTemplateCustomizer() {
return (RestTemplate restTemplate) -> restTemplate.setRequestFactory(FACTORY);
}

static SimpleClientHttpRequestFactory createProxyFactory() {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080));
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setProxy(proxy);
return factory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module;

import java.util.HashMap;
import java.util.Collections;
import java.util.Map;

public final class SerializerUtils {
private static final ObjectMapper OBJECT_MAPPER;
private static final TypeReference<Map<String, OAuth2AuthorizedClient>> TYPE_REFERENCE =
new TypeReference<Map<String, OAuth2AuthorizedClient>>() {
};
new TypeReference<Map<String, OAuth2AuthorizedClient>>() {
};

static {
OBJECT_MAPPER = new ObjectMapper();
Expand All @@ -33,6 +33,11 @@ public final class SerializerUtils {
private SerializerUtils() {
}

/**
* Serialize {@link Map} to {@link String}.
* @param authorizedClients the map to be serialized. It will not be modified in this method.
* @return The serialized {@link String}.
*/
public static String serializeOAuth2AuthorizedClientMap(Map<String, OAuth2AuthorizedClient> authorizedClients) {
String result;
try {
Expand All @@ -43,9 +48,14 @@ public static String serializeOAuth2AuthorizedClientMap(Map<String, OAuth2Author
return result;
}

/**
* Deserialize {@link String} to {@link Map}.
* @param authorizedClientsString the String to be deserialized
* @return The deserialized {@link Map}. Return {@link Collections#emptyMap()} if authorizedClientsString is null.
*/
public static Map<String, OAuth2AuthorizedClient> deserializeOAuth2AuthorizedClientMap(String authorizedClientsString) {
if (authorizedClientsString == null) {
return new HashMap<>();
return Collections.emptyMap();
}
Map<String, OAuth2AuthorizedClient> authorizedClients;
try {
Expand Down
Loading