Skip to content

Commit

Permalink
Fix parameter missing when using OBO process (Azure#30398)
Browse files Browse the repository at this point in the history
  • Loading branch information
moarychan authored Aug 15, 2022
1 parent fa05d3f commit b36afdd
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 7 deletions.
12 changes: 10 additions & 2 deletions sdk/spring/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Release H
Upgrade Spring Boot dependencies version to 2.7.2 and Spring Cloud dependencies version to 2021.0.3istory
# Release History

## 4.4.0-beta.1 (Unreleased)
Upgrade Spring Boot dependencies version to 2.7.2 and Spring Cloud dependencies version to 2021.0.3.

### Spring Cloud Azure Autoconfigure
This section includes changes in `spring-cloud-azure-autoconfigure` module.

#### Bugs Fixed
- Fix parameter `requested_token_use` missing when using On behalf of process [#30359](https://github.com/Azure/azure-sdk-for-java/issues/30359).

## 4.3.0 (2022-06-29)
- This release is compatible with Spring Boot 2.5.0-2.5.14, 2.6.0-2.6.9, 2.7.0-2.7.1. (Note: 2.5.x (x>14), 2.6.y (y>9) and 2.7.z (z>1) should be supported, but they aren't tested with this release.)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,13 @@ OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationReposito
JwtBearerOAuth2AuthorizedClientProvider azureAdJwtBearerProvider(ObjectProvider<OAuth2ClientAuthenticationJwkResolver> resolvers) {
JwtBearerOAuth2AuthorizedClientProvider provider = new JwtBearerOAuth2AuthorizedClientProvider();
OAuth2ClientAuthenticationJwkResolver resolver = resolvers.getIfUnique();
AadJwtBearerGrantRequestEntityConverter jwtBearerConverter = new AadJwtBearerGrantRequestEntityConverter();
if (resolver != null) {
AadJwtBearerGrantRequestEntityConverter jwtBearerConverter = new AadJwtBearerGrantRequestEntityConverter();
jwtBearerConverter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));

DefaultJwtBearerTokenResponseClient responseClient = new DefaultJwtBearerTokenResponseClient();
responseClient.setRequestEntityConverter(jwtBearerConverter);
provider.setAccessTokenResponseClient(responseClient);
}
DefaultJwtBearerTokenResponseClient responseClient = new DefaultJwtBearerTokenResponseClient();
responseClient.setRequestEntityConverter(jwtBearerConverter);
provider.setAccessTokenResponseClient(responseClient);
return provider;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,29 @@

import com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository;
import com.azure.spring.cloud.autoconfigure.aad.configuration.AadOAuth2ClientConfiguration;
import com.azure.spring.cloud.autoconfigure.aad.implementation.TestJwks;
import com.azure.spring.cloud.autoconfigure.aad.implementation.webapi.AadJwtBearerGrantRequestEntityConverter;
import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthenticationProperties;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.util.Base64URL;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.endpoint.DefaultJwtBearerTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.MultiValueMap;

import java.util.Arrays;
import java.util.Set;

import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.oauthClientAndResourceServerRunner;
Expand All @@ -20,6 +36,11 @@
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.webApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

class AadOAuth2ClientConfigurationTests {

Expand Down Expand Up @@ -141,4 +162,89 @@ void testResourceServerWithOboExistCustomAndGraphClient() {
assertThat(customScopes).containsOnly("api://52261059-e515-488e-84fd-a09a3f372814/File.Read");
});
}

@Test
void defaultConverterInJwtBearerOAuth2AuthorizedClientProviderWhenNotUsingPrivateKeyJwtMethod() {
resourceServerWithOboContextRunner()
.withUserConfiguration(AadOAuth2ClientConfiguration.class)
.withPropertyValues(
"spring.cloud.azure.active-directory.enabled=true",
"spring.cloud.azure.active-directory.authorization-clients.graph.scopes=https://graph.microsoft.com/User.Read",
"spring.cloud.azure.active-directory.authorization-clients.graph.authorization-grant-type=on_behalf_of",
"spring.cloud.azure.active-directory.authorization-clients.graph.scopes=api://52261059-e515-488e-84fd-a09a3f372814/File.Read"
)
.run(context -> {
assertThat(context).doesNotHaveBean(OAuth2ClientAuthenticationJwkResolver.class);
final JwtBearerOAuth2AuthorizedClientProvider jwtBearerProvider = context.getBean(
JwtBearerOAuth2AuthorizedClientProvider.class);
final ClientRegistrationRepository clientRepository = context.getBean(
ClientRegistrationRepository.class);
MultiValueMap<String, String> parameters = convertParameters(jwtBearerProvider, clientRepository);
assertThat(parameters).containsEntry("requested_token_use", Arrays.asList("on_behalf_of"));
});
}

@Test
void customConverterInJwtBearerOAuth2AuthorizedClientProviderWhenUsingPrivateKeyJwtMethod() {
RSAKey rsaJwk = spy(TestJwks.DEFAULT_RSA_JWK);
OAuth2ClientAuthenticationJwkResolver jwkResolver = spy(new TestOAuth2ClientAuthenticationJwkResolver(rsaJwk));
given(jwkResolver.resolve(any())).willReturn(rsaJwk);
given(rsaJwk.getX509CertThumbprint()).willReturn(new Base64URL("dGVzdA"));

resourceServerWithOboContextRunner()
.withBean(OAuth2ClientAuthenticationJwkResolver.class, () -> jwkResolver)
.withUserConfiguration(AadOAuth2ClientConfiguration.class)
.withPropertyValues(
"spring.cloud.azure.active-directory.enabled=true",
"spring.cloud.azure.active-directory.credential.client-certificate-path=/test/test.pfx",
"spring.cloud.azure.active-directory.credential.client-certificate-password=test",
"spring.cloud.azure.active-directory.authorization-clients.graph.scopes=https://graph.microsoft.com/User.Read",
"spring.cloud.azure.active-directory.authorization-clients.graph.authorization-grant-type=urn:ietf:params:oauth:grant-type:jwt-bearer",
"spring.cloud.azure.active-directory.authorization-clients.graph.scopes=api://52261059-e515-488e-84fd-a09a3f372814/File.Read",
"spring.cloud.azure.active-directory.authorization-clients.graph.client-authentication-method=private_key_jwt"
)
.run(context -> {
assertThat(context).hasSingleBean(OAuth2ClientAuthenticationJwkResolver.class);
final JwtBearerOAuth2AuthorizedClientProvider jwtBearerProvider = context.getBean(
JwtBearerOAuth2AuthorizedClientProvider.class);
final ClientRegistrationRepository clientRepository = context.getBean(
ClientRegistrationRepository.class);

MultiValueMap<String, String> parameters = convertParameters(jwtBearerProvider, clientRepository);
assertThat(parameters).containsEntry("requested_token_use", Arrays.asList("on_behalf_of"));
assertThat(parameters).containsKey(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE);
verify(jwkResolver).resolve(clientRepository.findByRegistrationId("graph"));
});
}

@SuppressWarnings("unchecked")
private MultiValueMap<String, String> convertParameters(JwtBearerOAuth2AuthorizedClientProvider jwtBearerProvider,
ClientRegistrationRepository clientRepository) {
OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> client =
(OAuth2AccessTokenResponseClient<JwtBearerGrantRequest>) ReflectionTestUtils.getField(jwtBearerProvider, "accessTokenResponseClient");
assertThat(client.getClass().getSimpleName()).isEqualTo(DefaultJwtBearerTokenResponseClient.class.getSimpleName());

JwtBearerGrantRequestEntityConverter requestEntityConverter =
(JwtBearerGrantRequestEntityConverter) ReflectionTestUtils.getField(client, "requestEntityConverter");
assertThat(requestEntityConverter.getClass().getSimpleName()).isEqualTo(AadJwtBearerGrantRequestEntityConverter.class.getSimpleName());

Converter<JwtBearerGrantRequest, MultiValueMap<String, String>> parametersConverter =
(Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>) ReflectionTestUtils.getField(requestEntityConverter, "parametersConverter");
JwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRepository.findByRegistrationId("graph"), mock(Jwt.class));
return parametersConverter.convert(request);
}

class TestOAuth2ClientAuthenticationJwkResolver implements OAuth2ClientAuthenticationJwkResolver {

private final JWK jwk;

TestOAuth2ClientAuthenticationJwkResolver(JWK jwk) {
this.jwk = jwk;
}

@Override
public JWK resolve(ClientRegistration clientRegistration) {
return this.jwk;
}
}
}

0 comments on commit b36afdd

Please sign in to comment.