parameters = new LinkedMultiValueMap<>();
+ parameters.set(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, CLIENT_ASSERTION_TYPE_VALUE);
+ parameters.set(OAuth2ParameterNames.CLIENT_ASSERTION, jwt.getTokenValue());
+ return parameters;
+ }
+
+ private static JwsAlgorithm resolveAlgorithm(JWK jwk) {
+ JwsAlgorithm jwsAlgorithm = null;
+
+ if (jwk.getAlgorithm() != null) {
+ jwsAlgorithm = SignatureAlgorithm.from(jwk.getAlgorithm().getName());
+ if (jwsAlgorithm == null) {
+ jwsAlgorithm = MacAlgorithm.from(jwk.getAlgorithm().getName());
+ }
+ }
+ if (jwsAlgorithm == null && KeyType.RSA.equals(jwk.getKeyType())) {
+ jwsAlgorithm = SignatureAlgorithm.RS256;
+ }
+ return jwsAlgorithm;
+ }
+
+ private static final class JwsEncoderHolder {
+
+ private final AadJwtEncoder jwtEncoder;
+
+ private final JWK jwk;
+
+ private JwsEncoderHolder(AadJwtEncoder jwtEncoder, JWK jwk) {
+ this.jwtEncoder = jwtEncoder;
+ this.jwk = jwk;
+ }
+
+ private AadJwtEncoder getJwtEncoder() {
+ return this.jwtEncoder;
+ }
+
+ private JWK getJwk() {
+ return this.jwk;
+ }
+
+ }
+
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtEncoder.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtEncoder.java
new file mode 100644
index 0000000000000..13bbf2626dd70
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtEncoder.java
@@ -0,0 +1,234 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.jwt;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JOSEObjectType;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.crypto.factories.DefaultJWSSignerFactory;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.JWKMatcher;
+import com.nimbusds.jose.jwk.JWKSelector;
+import com.nimbusds.jose.jwk.KeyType;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.proc.SecurityContext;
+import com.nimbusds.jose.produce.JWSSignerFactory;
+import com.nimbusds.jose.util.Base64URL;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtClaimNames;
+import org.springframework.security.oauth2.jwt.JwtException;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A JWT encoder that encodes a JSON Web Token (JWT) using the JSON Web Signature (JWS)
+ * Compact Serialization format. The private/secret key used for signing the JWS is
+ * supplied by the {@code com.nimbusds.jose.jwk.source.JWKSource} provided via the
+ * constructor.
+ *
+ *
+ * NOTE: A specially customized version for Azure AD based on 'NimbusJwsEncoder' or 'JwtEncoder' implementation.
+ * It is compatible with spring-security 5.5.X and 5.6.X versions, it can be replaced by 'NimbusJwsEncoder' when the minimum supported version is 5.6.X.
+ *
+ * @since 4.3.0
+ */
+public final class AadJwtEncoder {
+
+ private static final String ENCODING_ERROR_MESSAGE_TEMPLATE = "An error occurred while attempting to encode the Jwt: %s";
+
+ private static final JWSSignerFactory JWS_SIGNER_FACTORY = new DefaultJWSSignerFactory();
+
+ private final Map jwsSigners = new ConcurrentHashMap<>();
+
+ private final JWKSource jwkSource;
+
+ /**
+ * Constructs a {@code NimbusJwtEncoder} using the provided parameters.
+ *
+ * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}
+ */
+ public AadJwtEncoder(JWKSource jwkSource) {
+ Assert.notNull(jwkSource, "jwkSource cannot be null");
+ this.jwkSource = jwkSource;
+ }
+
+ public Jwt encode(Map jwsHeader, Map jwtClaimsSet) throws JwtException {
+ Assert.notNull(jwsHeader, "jwsHeader cannot be null");
+ Assert.notNull(jwtClaimsSet, "jwtClaimsSet cannot be null");
+ JWK jwk = selectJwk(jwsHeader);
+ String jws = serialize(jwsHeader, jwtClaimsSet, jwk);
+ return new Jwt(jws,
+ (Instant) jwtClaimsSet.get(JwtClaimNames.IAT),
+ (Instant) jwtClaimsSet.get(JwtClaimNames.EXP),
+ jwsHeader, jwtClaimsSet);
+ }
+
+ private JWK selectJwk(Map jwsHeader) {
+ List jwks;
+ try {
+ JWKSelector jwkSelector = new JWKSelector(createJwkMatcher(jwsHeader));
+ jwks = this.jwkSource.get(jwkSelector, null);
+ } catch (Exception ex) {
+ throw new JwtException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,
+ "Failed to select a JWK signing key -> " + ex.getMessage()), ex);
+ }
+
+ if (jwks.size() > 1) {
+ throw new JwtException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,
+ "Found multiple JWK signing keys for algorithm '" + jwsHeader.get("alg") + "'"));
+ }
+
+ if (jwks.isEmpty()) {
+ throw new JwtException(
+ String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, "Failed to select a JWK signing key"));
+ }
+
+ return jwks.get(0);
+ }
+
+ private String serialize(Map headers, Map claims, JWK jwk) {
+ JWSHeader jwsHeader = convertHeader(headers);
+ JWTClaimsSet jwtClaimsSet = convertClaims(claims);
+
+ JWSSigner jwsSigner = this.jwsSigners.computeIfAbsent(jwk, AadJwtEncoder::createSigner);
+
+ SignedJWT signedJwt = new SignedJWT(jwsHeader, jwtClaimsSet);
+ try {
+ signedJwt.sign(jwsSigner);
+ } catch (JOSEException ex) {
+ throw new JwtException(
+ String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, "Failed to sign the JWT -> " + ex.getMessage()), ex);
+ }
+ return signedJwt.serialize();
+ }
+
+ private static JWKMatcher createJwkMatcher(Map jwsHeader) {
+ JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse((String) jwsHeader.get("alg"));
+
+ if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm)) {
+ // @formatter:off
+ return new JWKMatcher.Builder()
+ .keyType(KeyType.forAlgorithm(jwsAlgorithm))
+ .keyUses(KeyUse.SIGNATURE, null)
+ .algorithms(jwsAlgorithm, null)
+ .x509CertSHA256Thumbprint(Base64URL.from((String) jwsHeader.get("x5t#S256")))
+ .build();
+ // @formatter:on
+ }
+ return null;
+ }
+
+ private static JWSSigner createSigner(JWK jwk) {
+ try {
+ return JWS_SIGNER_FACTORY.createJWSSigner(jwk);
+ } catch (JOSEException ex) {
+ throw new JwtException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,
+ "Failed to create a JWS Signer -> " + ex.getMessage()), ex);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static JWSHeader convertHeader(Map headers) {
+ JWSHeader.Builder builder = new JWSHeader.Builder(JWSAlgorithm.parse((String) headers.get("alg")));
+ Map jwk = (Map) headers.get("jwk");
+ if (!CollectionUtils.isEmpty(jwk)) {
+ try {
+ builder.jwk(JWK.parse(jwk));
+ } catch (Exception ex) {
+ throw new JwtException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,
+ "Unable to convert 'jku' JOSE header"), ex);
+ }
+ }
+ String keyId = (String) headers.get("kid");
+ if (StringUtils.hasText(keyId)) {
+ builder.keyID(keyId);
+ }
+
+ String x509SHA1Thumbprint = (String) headers.get("x5t");
+ if (StringUtils.hasText(x509SHA1Thumbprint)) {
+ builder.x509CertThumbprint(new Base64URL(x509SHA1Thumbprint));
+ }
+
+ String type = (String) headers.get("typ");
+ if (StringUtils.hasText(type)) {
+ builder.type(new JOSEObjectType(type));
+ }
+
+ Map customHeaders = new HashMap<>();
+ headers.forEach((name, value) -> {
+ if (!JWSHeader.getRegisteredParameterNames().contains(name)) {
+ customHeaders.put(name, value);
+ }
+ });
+ if (!customHeaders.isEmpty()) {
+ builder.customParams(customHeaders);
+ }
+
+ return builder.build();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static JWTClaimsSet convertClaims(Map claims) {
+ JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
+ Object issuer = claims.get(JwtClaimNames.ISS);
+ if (issuer != null) {
+ builder.issuer(issuer.toString());
+ }
+
+ String subject = (String) claims.get(JwtClaimNames.SUB);
+ if (StringUtils.hasText(subject)) {
+ builder.subject(subject);
+ }
+
+ List audience = (List) claims.get(JwtClaimNames.AUD);
+ if (!CollectionUtils.isEmpty(audience)) {
+ builder.audience(audience);
+ }
+
+ Instant expiresAt = (Instant) claims.get(JwtClaimNames.EXP);
+ if (expiresAt != null) {
+ builder.expirationTime(Date.from(expiresAt));
+ }
+
+ Instant notBefore = (Instant) claims.get(JwtClaimNames.NBF);
+ if (notBefore != null) {
+ builder.notBeforeTime(Date.from(notBefore));
+ }
+
+ Instant issuedAt = (Instant) claims.get(JwtClaimNames.IAT);
+ if (issuedAt != null) {
+ builder.issueTime(Date.from(issuedAt));
+ }
+
+ String jwtId = (String) claims.get(JwtClaimNames.JTI);
+ if (StringUtils.hasText(jwtId)) {
+ builder.jwtID(jwtId);
+ }
+
+ Map customClaims = new HashMap<>();
+ claims.forEach((name, value) -> {
+ if (!JWTClaimsSet.getRegisteredNames().contains(name)) {
+ customClaims.put(name, value);
+ }
+ });
+ if (!customClaims.isEmpty()) {
+ customClaims.forEach(builder::claim);
+ }
+
+ return builder.build();
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2AuthenticatedPrincipal.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2AuthenticatedPrincipal.java
index c679d63d3aeb6..c17dc3284e295 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2AuthenticatedPrincipal.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2AuthenticatedPrincipal.java
@@ -2,10 +2,12 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.autoconfigure.aad.implementation.oauth2;
+import com.azure.spring.cloud.autoconfigure.aad.AadResourceServerWebSecurityConfigurerAdapter;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTClaimsSet.Builder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.util.Assert;
import java.io.Serializable;
@@ -18,7 +20,10 @@
/**
* Entity class of AADOAuth2AuthenticatedPrincipal
+ *
+ * @deprecated use the default converter {@link JwtAuthenticationConverter} instead in {@link AadResourceServerWebSecurityConfigurerAdapter}.
*/
+@Deprecated
public class AadOAuth2AuthenticatedPrincipal implements OAuth2AuthenticatedPrincipal, Serializable {
private static final long serialVersionUID = -3625690847771476854L;
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2ClientAuthenticationJwkResolver.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2ClientAuthenticationJwkResolver.java
new file mode 100644
index 0000000000000..e9dda163d104a
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2ClientAuthenticationJwkResolver.java
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.oauth2;
+
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.util.Base64URL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.util.Assert;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+import java.util.UUID;
+
+/**
+ * An {@link RSAKey} resolver implementation parses the certificate locally.
+ *
+ * @since 4.3.0
+ * @see Certificate credentials
+ */
+public class AadOAuth2ClientAuthenticationJwkResolver implements OAuth2ClientAuthenticationJwkResolver {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AadOAuth2ClientAuthenticationJwkResolver.class);
+
+ private final String clientCertificatePath;
+ private final String clientCertificatePassword;
+
+ /**
+ * Creates a new instance of {@link AadOAuth2ClientAuthenticationJwkResolver}
+ * @param clientCertificatePath the client certificate path
+ * @param clientCertificatePassword the client certificate password
+ */
+ public AadOAuth2ClientAuthenticationJwkResolver(String clientCertificatePath,
+ String clientCertificatePassword) {
+ Assert.notNull(clientCertificatePath, "clientCertificatePath cannot be null");
+ Assert.notNull(clientCertificatePassword, "clientCertificatePassword cannot be null");
+
+ String fileExtension = clientCertificatePath.substring(clientCertificatePath.lastIndexOf(".") + 1);
+ Assert.isTrue("pfx".equals(fileExtension) || "p12".equals(fileExtension),
+ "Only files with the '.pfx' or '.p12' extension are supported.");
+
+ this.clientCertificatePath = clientCertificatePath;
+ this.clientCertificatePassword = clientCertificatePassword;
+ }
+
+ @Override
+ public JWK resolve(ClientRegistration clientRegistration) {
+ if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.equals(clientRegistration.getClientAuthenticationMethod())) {
+ try (FileInputStream inputStream = new FileInputStream(clientCertificatePath)) {
+ KeyStore keyStore = KeyStore.getInstance("PKCS12");
+ char[] password = clientCertificatePassword.toCharArray();
+ keyStore.load(inputStream, password);
+ String alias = keyStore.aliases().nextElement();
+ PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password);
+ X509Certificate x509Certificate = (X509Certificate) keyStore.getCertificate(alias);
+ PublicKey publicKey = x509Certificate.getPublicKey();
+ return new RSAKey.Builder((RSAPublicKey) publicKey)
+ .privateKey(privateKey)
+ .x509CertThumbprint(Base64URL.encode(getX5t(x509Certificate)))
+ .keyID(UUID.randomUUID().toString())
+ .build();
+ } catch (KeyStoreException | IOException | NoSuchAlgorithmException
+ | CertificateException | UnrecoverableKeyException e) {
+ LOGGER.error("Resolve RSAKey exception.", e);
+ }
+ }
+ return null;
+ }
+
+ private byte[] getX5t(X509Certificate cert)
+ throws NoSuchAlgorithmException, CertificateEncodingException {
+ return getSHA1Byte(cert.getEncoded());
+ }
+
+ private byte[] getSHA1Byte(byte[] data) throws NoSuchAlgorithmException {
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.update(data);
+ return digest.digest();
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/OAuth2ClientAuthenticationJwkResolver.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/OAuth2ClientAuthenticationJwkResolver.java
new file mode 100644
index 0000000000000..43011e3ba858e
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/OAuth2ClientAuthenticationJwkResolver.java
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.oauth2;
+
+import com.nimbusds.jose.jwk.JWK;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+
+/**
+ * Resolver interface to resolve a {@link JWK} implementation through a {@link ClientRegistration}.
+ * @since 4.3.0
+ */
+@FunctionalInterface
+public interface OAuth2ClientAuthenticationJwkResolver {
+
+ /**
+ * @param clientRegistration the client registration.
+ * @return a a {@link JWK}.
+ */
+ JWK resolve(ClientRegistration clientRegistration);
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadJwtBearerGrantRequestEntityConverter.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadJwtBearerGrantRequestEntityConverter.java
new file mode 100644
index 0000000000000..fffaf601ceb80
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadJwtBearerGrantRequestEntityConverter.java
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.webapi;
+
+import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
+import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequestEntityConverter;
+import org.springframework.util.MultiValueMap;
+
+/**
+ * This is a special JWT Bearer flow implementation for Microsoft identify platform.
+ *
+ * @since 4.3.0
+ * @see OAuth 2.0 On-Behalf-Of
+ */
+public class AadJwtBearerGrantRequestEntityConverter extends JwtBearerGrantRequestEntityConverter {
+
+ @Override
+ protected MultiValueMap createParameters(JwtBearerGrantRequest jwtBearerGrantRequest) {
+ MultiValueMap parameters = super.createParameters(jwtBearerGrantRequest);
+ parameters.add("requested_token_use", "on_behalf_of");
+ return parameters;
+ }
+}
+
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadOboOAuth2AuthorizedClientProvider.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadOboOAuth2AuthorizedClientProvider.java
index 3f81fbe5ac2d7..9a28e5cf27acf 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadOboOAuth2AuthorizedClientProvider.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadOboOAuth2AuthorizedClientProvider.java
@@ -3,7 +3,6 @@
package com.azure.spring.cloud.autoconfigure.aad.implementation.webapi;
import com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
import com.microsoft.aad.msal4j.ClientCredentialFactory;
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.aad.msal4j.IClientSecret;
@@ -19,11 +18,13 @@
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
@@ -45,18 +46,21 @@
import java.util.Optional;
import java.util.concurrent.ExecutionException;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants.ON_BEHALF_OF;
+
/**
* A strategy for authorizing (or re-authorizing) an OAuth 2.0 Client. This implementations implement {@link
- * AadAuthorizationGrantType "on_behalf_of" authorization grant type}.
+ * Constants#ON_BEHALF_OF "on_behalf_of" authorization grant type}.
*
- * @see AadAuthorizationGrantType
+ * @see AuthorizationGrantType
* @see OAuth2AuthorizedClientProvider
+ * @deprecated use {@link JwtBearerOAuth2AuthorizedClientProvider} instead.
*/
+@Deprecated
public class AadOboOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(AadOboOAuth2AuthorizedClientProvider.class);
-
private final Clock clock = Clock.systemUTC();
private final Duration clockSkew = Duration.ofSeconds(60);
@@ -78,8 +82,7 @@ public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
Assert.notNull(context, "context cannot be null");
ClientRegistration clientRegistration = context.getClientRegistration();
- if (!AadAuthorizationGrantType.ON_BEHALF_OF
- .isSameGrantType(clientRegistration.getAuthorizationGrantType())) {
+ if (!ON_BEHALF_OF.equals(clientRegistration.getAuthorizationGrantType())) {
return null;
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProvider.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProvider.java
index 1dc095426ae96..b952a61169c2c 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProvider.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProvider.java
@@ -3,7 +3,7 @@
package com.azure.spring.cloud.autoconfigure.aad.implementation.webapp;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
+import com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
@@ -12,6 +12,7 @@
import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.util.Assert;
@@ -26,14 +27,15 @@
import java.util.Optional;
import static com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository.AZURE_CLIENT_REGISTRATION_ID;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants.AZURE_DELEGATED;
/**
* A strategy for authorizing (or re-authorizing) an OAuth 2.0 Client. This implementation implement {@link
- * AadAuthorizationGrantType "azure_delegated" authorization grant type}.
+ * Constants#AZURE_DELEGATED "azure_delegated" authorization grant type}.
*
* @see OAuth2AuthorizedClient
* @see OAuth2AuthorizationContext
- * @see AadAuthorizationGrantType
+ * @see AuthorizationGrantType
* @see Section 1.3 Authorization Grant
* @since 3.8.0
*/
@@ -74,8 +76,7 @@ public AadAzureDelegatedOAuth2AuthorizedClientProvider(
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
Assert.notNull(context, "context cannot be null");
ClientRegistration clientRegistration = context.getClientRegistration();
- if (!AadAuthorizationGrantType.AZURE_DELEGATED.isSameGrantType(
- clientRegistration.getAuthorizationGrantType())) {
+ if (!AZURE_DELEGATED.equals(clientRegistration.getAuthorizationGrantType())) {
return null;
}
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationProperties.java
index 48cfbbdd1bafb..e598b49e5daf2 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationProperties.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationProperties.java
@@ -8,6 +8,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.util.StringUtils;
import java.time.Duration;
@@ -23,13 +24,15 @@
import java.util.stream.Stream;
import static com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository.AZURE_CLIENT_REGISTRATION_ID;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants.AZURE_DELEGATED;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants.ON_BEHALF_OF;
import static com.azure.spring.cloud.autoconfigure.aad.properties.AadApplicationType.RESOURCE_SERVER;
import static com.azure.spring.cloud.autoconfigure.aad.properties.AadApplicationType.RESOURCE_SERVER_WITH_OBO;
import static com.azure.spring.cloud.autoconfigure.aad.properties.AadApplicationType.WEB_APPLICATION;
import static com.azure.spring.cloud.autoconfigure.aad.properties.AadApplicationType.inferApplicationTypeByDependencies;
-import static com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType.AUTHORIZATION_CODE;
-import static com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType.AZURE_DELEGATED;
-import static com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType.ON_BEHALF_OF;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.JWT_BEARER;
+import static org.springframework.security.oauth2.core.ClientAuthenticationMethod.CLIENT_SECRET_JWT;
/**
* Configuration properties for Azure Active Directory Authentication.
@@ -134,15 +137,15 @@ public class AadAuthenticationProperties implements InitializingBean {
*/
private AadApplicationType applicationType;
- private static final Map> NON_COMPATIBLE_APPLICATION_TYPE_AND_GRANT_TYPES = initCompatibleApplicationTypeAndGrantTypes();
+ private static final Map> NON_COMPATIBLE_APPLICATION_TYPE_AND_GRANT_TYPES = initCompatibleApplicationTypeAndGrantTypes();
- private static Map> initCompatibleApplicationTypeAndGrantTypes() {
- Map> nonCompatibleApplicationTypeAndGrantTypes =
+ private static Map> initCompatibleApplicationTypeAndGrantTypes() {
+ Map> nonCompatibleApplicationTypeAndGrantTypes =
new HashMap<>();
nonCompatibleApplicationTypeAndGrantTypes.put(WEB_APPLICATION,
- Stream.of(ON_BEHALF_OF).collect(Collectors.toSet()));
+ Stream.of(ON_BEHALF_OF, JWT_BEARER).collect(Collectors.toSet()));
nonCompatibleApplicationTypeAndGrantTypes.put(RESOURCE_SERVER,
- Stream.of(AUTHORIZATION_CODE, ON_BEHALF_OF).collect(Collectors.toSet()));
+ Stream.of(AUTHORIZATION_CODE, ON_BEHALF_OF, JWT_BEARER).collect(Collectors.toSet()));
nonCompatibleApplicationTypeAndGrantTypes.put(RESOURCE_SERVER_WITH_OBO,
Stream.of(AUTHORIZATION_CODE).collect(Collectors.toSet()));
@@ -590,22 +593,25 @@ private boolean isValidApplicationType(AadApplicationType configured, AadApplica
private void validateAuthorizationClientProperties(String registrationId,
AuthorizationClientProperties properties) {
- AadAuthorizationGrantType grantType = Optional.of(properties)
- .map(AuthorizationClientProperties::getAuthorizationGrantType)
- .orElse(null);
+ if (CLIENT_SECRET_JWT.equals(properties.getClientAuthenticationMethod())) {
+ throw new IllegalStateException("The client authentication method of '"
+ + registrationId + "' is not supported.");
+ }
+
+ AuthorizationGrantType grantType = properties.getAuthorizationGrantType();
if (grantType != null) {
validateAuthorizationGrantType(registrationId, grantType);
} else {
grantType = decideDefaultGrantTypeFromApplicationType(registrationId, applicationType);
properties.setAuthorizationGrantType(grantType);
- LOGGER.debug("The client '{}' sets the default value of AADAuthorizationGrantType to '{}'.", grantType,
+ LOGGER.debug("The client '{}' sets the default value of AuthorizationGrantType to '{}'.", grantType,
registrationId);
}
// Extract validated scopes from properties
List scopes = extractValidatedScopes(registrationId, properties);
- addNecessaryScopesForAuhtorizationCodeClients(properties, scopes);
+ addNecessaryScopesForAuthorizationCodeClients(registrationId, properties, scopes);
}
/**
@@ -617,11 +623,17 @@ private void validateAuthorizationClientProperties(String registrationId,
* "openid" : allows to request an ID token.
* "profile" : allows returning additional claims in the ID token.
* "offline_access" : allows to request a refresh token.
+ * @param registrationId client ID
* @param properties AuthorizationClientProperties
* @param scopes scopes for authorization_code clients.
*/
- private void addNecessaryScopesForAuhtorizationCodeClients(AuthorizationClientProperties properties,
+ private void addNecessaryScopesForAuthorizationCodeClients(String registrationId,
+ AuthorizationClientProperties properties,
List scopes) {
+ if (AZURE_CLIENT_REGISTRATION_ID.equals(registrationId) && (scopes == null || scopes.isEmpty())) {
+ return;
+ }
+
if (properties.getAuthorizationGrantType().equals(AUTHORIZATION_CODE)) {
String[] scopesNeeded = new String[] { "openid", "profile", "offline_access" };
for (String scope : scopesNeeded) {
@@ -634,7 +646,7 @@ private void addNecessaryScopesForAuhtorizationCodeClients(AuthorizationClientPr
private List extractValidatedScopes(String registrationId, AuthorizationClientProperties properties) {
List scopes = properties.getScopes();
- if (scopes == null || scopes.isEmpty()) {
+ if (!AZURE_CLIENT_REGISTRATION_ID.equals(registrationId) && (scopes == null || scopes.isEmpty())) {
throw new IllegalStateException(
"'spring.cloud.azure.active-directory.authorization-clients." + registrationId + ".scopes' must be "
+ "configured");
@@ -642,11 +654,11 @@ private List extractValidatedScopes(String registrationId, Authorization
return scopes;
}
- private void validateAuthorizationGrantType(String registrationId, AadAuthorizationGrantType grantType) {
+ private void validateAuthorizationGrantType(String registrationId, AuthorizationGrantType grantType) {
if (NON_COMPATIBLE_APPLICATION_TYPE_AND_GRANT_TYPES.containsKey(applicationType)) {
if (NON_COMPATIBLE_APPLICATION_TYPE_AND_GRANT_TYPES.get(applicationType).contains(grantType)) {
throw new IllegalStateException(String.format(UNMATCHING_OAUTH_GRANT_TYPE_FROMAT,
- applicationType.getValue(), registrationId, grantType));
+ applicationType.getValue(), registrationId, grantType.getValue()));
}
LOGGER.debug("'spring.cloud.azure.active-directory.authorization-clients.{}.authorization-grant-type'"
+ " is valid.", registrationId);
@@ -666,9 +678,9 @@ private void validateAuthorizationGrantType(String registrationId, AadAuthorizat
* @param appType AadApplicationType
* @return default grant type
*/
- private AadAuthorizationGrantType decideDefaultGrantTypeFromApplicationType(String registrationId,
- AadApplicationType appType) {
- AadAuthorizationGrantType grantType;
+ private AuthorizationGrantType decideDefaultGrantTypeFromApplicationType(String registrationId,
+ AadApplicationType appType) {
+ AuthorizationGrantType grantType;
switch (appType) {
case WEB_APPLICATION:
if (registrationId.equals(AZURE_CLIENT_REGISTRATION_ID)) {
@@ -679,7 +691,7 @@ private AadAuthorizationGrantType decideDefaultGrantTypeFromApplicationType(Stri
break;
case RESOURCE_SERVER:
case RESOURCE_SERVER_WITH_OBO:
- grantType = AadAuthorizationGrantType.ON_BEHALF_OF;
+ grantType = JWT_BEARER;
break;
case WEB_APPLICATION_AND_RESOURCE_SERVER:
throw new IllegalStateException("spring.cloud.azure.active-directory.authorization-clients." + registrationId
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthorizationGrantType.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthorizationGrantType.java
index 13beece11bdfa..196e11626da28 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthorizationGrantType.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthorizationGrantType.java
@@ -6,7 +6,10 @@
/**
* Defines grant types: client_credentials, authorization_code, on_behalf_of, azure_delegated.
+ *
+ * @deprecated use {@link AuthorizationGrantType} instead.
*/
+@Deprecated
public enum AadAuthorizationGrantType {
/**
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadCredentialProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadCredentialProperties.java
index 01fe2ac00abb6..8d03b0abc457a 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadCredentialProperties.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadCredentialProperties.java
@@ -18,6 +18,16 @@ public class AadCredentialProperties {
*/
private String clientSecret;
+ /**
+ * Path of a PFX or P12 certificate file to use when performing service principal authentication with Azure.
+ */
+ private String clientCertificatePath;
+
+ /**
+ * Password of the certificate file.
+ */
+ private String clientCertificatePassword;
+
/**
*
* @return The client ID.
@@ -49,4 +59,32 @@ public String getClientSecret() {
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
+
+ /**
+ * @return The client certificate path.
+ */
+ public String getClientCertificatePath() {
+ return clientCertificatePath;
+ }
+
+ /**
+ * @param clientCertificatePath The client certificate path.
+ */
+ public void setClientCertificatePath(String clientCertificatePath) {
+ this.clientCertificatePath = clientCertificatePath;
+ }
+
+ /**
+ * @return The client certificate password.
+ */
+ public String getClientCertificatePassword() {
+ return clientCertificatePassword;
+ }
+
+ /**
+ * @param clientCertificatePassword The client certificate password.
+ */
+ public void setClientCertificatePassword(String clientCertificatePassword) {
+ this.clientCertificatePassword = clientCertificatePassword;
+ }
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AuthorizationClientProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AuthorizationClientProperties.java
index 6586b407be80d..607cbb77c6c06 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AuthorizationClientProperties.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aad/properties/AuthorizationClientProperties.java
@@ -3,6 +3,9 @@
package com.azure.spring.cloud.autoconfigure.aad.properties;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+
import java.util.List;
/**
@@ -12,14 +15,16 @@ public class AuthorizationClientProperties {
private List scopes;
- private AadAuthorizationGrantType authorizationGrantType;
+ private AuthorizationGrantType authorizationGrantType;
+
+ private ClientAuthenticationMethod clientAuthenticationMethod;
/**
* Gets the authorization grant type.
*
* @return the authorization grant type
*/
- public AadAuthorizationGrantType getAuthorizationGrantType() {
+ public AuthorizationGrantType getAuthorizationGrantType() {
return authorizationGrantType;
}
@@ -28,7 +33,7 @@ public AadAuthorizationGrantType getAuthorizationGrantType() {
*
* @param authorizationGrantType the authorization grant type
*/
- public void setAuthorizationGrantType(AadAuthorizationGrantType authorizationGrantType) {
+ public void setAuthorizationGrantType(AuthorizationGrantType authorizationGrantType) {
this.authorizationGrantType = authorizationGrantType;
}
@@ -49,4 +54,22 @@ public void setScopes(List scopes) {
public List getScopes() {
return scopes;
}
+
+ /**
+ * Gets the client authentication method.
+ *
+ * @return the client authentication method
+ */
+ public ClientAuthenticationMethod getClientAuthenticationMethod() {
+ return clientAuthenticationMethod;
+ }
+
+ /**
+ * Sets the client authentication method.
+ *
+ * @param clientAuthenticationMethod the client authentication method
+ */
+ public void setClientAuthenticationMethod(ClientAuthenticationMethod clientAuthenticationMethod) {
+ this.clientAuthenticationMethod = clientAuthenticationMethod;
+ }
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/configuration/AadB2cOAuth2ClientConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/configuration/AadB2cOAuth2ClientConfiguration.java
index c6ad6b4a67e24..f2378e2e70c8c 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/configuration/AadB2cOAuth2ClientConfiguration.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/configuration/AadB2cOAuth2ClientConfiguration.java
@@ -2,7 +2,6 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.autoconfigure.aadb2c.configuration;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
import com.azure.spring.cloud.autoconfigure.aadb2c.implementation.AadB2cClientRegistrationRepository;
import com.azure.spring.cloud.autoconfigure.aadb2c.implementation.AadB2cConditions;
import com.azure.spring.cloud.autoconfigure.aadb2c.implementation.AadB2cUrl;
@@ -29,7 +28,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -103,10 +101,7 @@ private ClientRegistration buildUserFlowClientRegistration(Map.Entry client) {
- AuthorizationGrantType authGrantType = Optional.ofNullable(client.getValue().getAuthorizationGrantType())
- .map(AadAuthorizationGrantType::getValue)
- .map(AuthorizationGrantType::new)
- .orElse(null);
+ AuthorizationGrantType authGrantType = client.getValue().getAuthorizationGrantType();
if (!AuthorizationGrantType.CLIENT_CREDENTIALS.equals(authGrantType)) {
LOGGER.warn("The authorization type of the {} client registration is not supported.", client.getKey());
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AadB2cProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AadB2cProperties.java
index d37fb6d226cf2..9fb33aa99e41b 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AadB2cProperties.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AadB2cProperties.java
@@ -15,7 +15,7 @@
import java.util.Map;
import java.util.Optional;
-import static com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType.CLIENT_CREDENTIALS;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.CLIENT_CREDENTIALS;
/**
* Configuration properties for Azure Active Directory B2C.
@@ -143,7 +143,7 @@ private void validateCommonProperties() {
long credentialCount = authorizationClients.values()
.stream()
.map(AuthorizationClientProperties::getAuthorizationGrantType)
- .filter(client -> CLIENT_CREDENTIALS == client)
+ .filter(client -> CLIENT_CREDENTIALS.equals(client))
.count();
if (credentialCount > 0 && !StringUtils.hasText(profile.getTenantId())) {
throw new AadB2cConfigurationException("'tenant-id' must be configured "
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AuthorizationClientProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AuthorizationClientProperties.java
index e081899ec1fb9..8b100b71c2c14 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AuthorizationClientProperties.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/aadb2c/properties/AuthorizationClientProperties.java
@@ -3,7 +3,7 @@
package com.azure.spring.cloud.autoconfigure.aadb2c.properties;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import java.util.List;
@@ -14,14 +14,14 @@ public class AuthorizationClientProperties {
private List scopes;
- private AadAuthorizationGrantType authorizationGrantType;
+ private AuthorizationGrantType authorizationGrantType;
/**
* Gets the authorization grant type.
*
* @return the authorization grant type
*/
- public AadAuthorizationGrantType getAuthorizationGrantType() {
+ public AuthorizationGrantType getAuthorizationGrantType() {
return authorizationGrantType;
}
@@ -30,7 +30,7 @@ public AadAuthorizationGrantType getAuthorizationGrantType() {
*
* @param authorizationGrantType the authorization grant type
*/
- public void setAuthorizationGrantType(AadAuthorizationGrantType authorizationGrantType) {
+ public void setAuthorizationGrantType(AuthorizationGrantType authorizationGrantType) {
this.authorizationGrantType = authorizationGrantType;
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/AadClientRegistrationRepositoryTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/AadClientRegistrationRepositoryTests.java
index 6f452719df933..24cb381666388 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/AadClientRegistrationRepositoryTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/AadClientRegistrationRepositoryTests.java
@@ -3,9 +3,11 @@
package com.azure.spring.cloud.autoconfigure.aad;
+import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthenticationProperties;
import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationServerEndpoints;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
@@ -19,16 +21,21 @@
import java.util.List;
import java.util.Set;
-import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.oauthClientRunner;
-import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.webApplicationContextRunner;
import static com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository.AZURE_CLIENT_REGISTRATION_ID;
import static com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository.resourceServerCount;
-import static com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType.AUTHORIZATION_CODE;
-import static com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType.AZURE_DELEGATED;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.oauthClientRunner;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.resourceServerWithOboContextRunner;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.webApplicationAndResourceServerContextRunner;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.webApplicationContextRunner;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants.AZURE_DELEGATED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.JWT_BEARER;
+import static org.springframework.security.oauth2.core.ClientAuthenticationMethod.PRIVATE_KEY_JWT;
class AadClientRegistrationRepositoryTests {
@@ -42,7 +49,7 @@ void noClientsConfiguredTest() {
repository.getAzureClientAccessTokenScopes());
ClientRegistration azure = repository.findByRegistrationId(AZURE_CLIENT_REGISTRATION_ID);
- assertEquals(AUTHORIZATION_CODE.getValue(), azure.getAuthorizationGrantType().getValue());
+ assertEquals(AUTHORIZATION_CODE, azure.getAuthorizationGrantType());
assertEquals(new HashSet<>(Arrays.asList("openid", "profile", "offline_access")), azure.getScopes());
List clients = collectClients(repository);
@@ -55,16 +62,17 @@ void noClientsConfiguredTest() {
void azureClientConfiguredTest() {
webApplicationContextRunner()
.withPropertyValues(
- "spring.cloud.azure.active-directory.authorization-clients.azure.scopes = Azure.Scope"
+ "spring.cloud.azure.active-directory.authorization-clients.azure.scopes = Azure.Scope",
+ "spring.cloud.azure.active-directory.authorization-clients.azure.client-authentication-method = private_key_jwt"
)
.run(context -> {
AadClientRegistrationRepository repository =
(AadClientRegistrationRepository) context.getBean(ClientRegistrationRepository.class);
assertEquals(new HashSet<>(Arrays.asList("Azure.Scope", "openid", "profile", "offline_access")),
repository.getAzureClientAccessTokenScopes());
-
ClientRegistration azure = repository.findByRegistrationId(AZURE_CLIENT_REGISTRATION_ID);
- assertEquals(AUTHORIZATION_CODE.getValue(), azure.getAuthorizationGrantType().getValue());
+ assertEquals(PRIVATE_KEY_JWT, azure.getClientAuthenticationMethod());
+ assertEquals(AUTHORIZATION_CODE, azure.getAuthorizationGrantType());
assertEquals(new HashSet<>(Arrays.asList("Azure.Scope", "openid", "profile", "offline_access")),
azure.getScopes());
@@ -73,6 +81,28 @@ void azureClientConfiguredTest() {
});
}
+ @Test
+ void azureClientInvalidedConfiguredTest() {
+ webApplicationContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.azure.client-authentication-method = client_secret_jwt"
+ )
+ .run(context ->
+ assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class))
+ );
+ }
+
+ @Test
+ void otherClientInvalidedConfiguredTest() {
+ webApplicationContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.other.client-authentication-method = client_secret_jwt"
+ )
+ .run(context ->
+ assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class))
+ );
+ }
+
@Test
void graphClientConfiguredTest() {
webApplicationContextRunner()
@@ -86,12 +116,12 @@ void graphClientConfiguredTest() {
repository.getAzureClientAccessTokenScopes());
ClientRegistration azure = repository.findByRegistrationId(AZURE_CLIENT_REGISTRATION_ID);
- assertEquals(AUTHORIZATION_CODE.getValue(), azure.getAuthorizationGrantType().getValue());
+ assertEquals(AUTHORIZATION_CODE, azure.getAuthorizationGrantType());
assertEquals(new HashSet<>(Arrays.asList("Graph.Scope", "openid", "profile", "offline_access")),
azure.getScopes());
ClientRegistration graph = repository.findByRegistrationId("graph");
- assertEquals(AZURE_DELEGATED.getValue(), graph.getAuthorizationGrantType().getValue());
+ assertEquals(AZURE_DELEGATED, graph.getAuthorizationGrantType());
assertEquals(new HashSet<>(Collections.singletonList("Graph.Scope")), graph.getScopes());
List clients = collectClients(repository);
@@ -113,12 +143,12 @@ void authorizationCodeGraphClientConfiguredTest() {
repository.getAzureClientAccessTokenScopes());
ClientRegistration azure = repository.findByRegistrationId(AZURE_CLIENT_REGISTRATION_ID);
- assertEquals(AUTHORIZATION_CODE.getValue(), azure.getAuthorizationGrantType().getValue());
+ assertEquals(AUTHORIZATION_CODE, azure.getAuthorizationGrantType());
assertEquals(new HashSet<>(Arrays.asList("openid", "profile", "offline_access")),
azure.getScopes());
ClientRegistration graph = repository.findByRegistrationId("graph");
- assertEquals(AUTHORIZATION_CODE.getValue(), graph.getAuthorizationGrantType().getValue());
+ assertEquals(AUTHORIZATION_CODE, graph.getAuthorizationGrantType());
assertEquals(new HashSet<>(Arrays.asList("Graph.Scope", "openid", "profile", "offline_access")),
graph.getScopes());
@@ -127,6 +157,25 @@ void authorizationCodeGraphClientConfiguredTest() {
});
}
+ @Test
+ void clientWithJwtBearerAuthorizationGrantType() {
+ resourceServerWithOboContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.webapi1.authorization-grant-type = on_behalf_of",
+ "spring.cloud.azure.active-directory.authorization-clients.webapi1.scopes = Test",
+ "spring.cloud.azure.active-directory.authorization-clients.webapi2.authorization-grant-type = urn:ietf:params:oauth:grant-type:jwt-bearer",
+ "spring.cloud.azure.active-directory.authorization-clients.webapi2.scopes = Test"
+ )
+ .run(context -> {
+ ClientRegistrationRepository repository = context.getBean(ClientRegistrationRepository.class);
+ ClientRegistration webapi1 = repository.findByRegistrationId("webapi1");
+ assertEquals(JWT_BEARER, webapi1.getAuthorizationGrantType());
+
+ ClientRegistration webapi2 = repository.findByRegistrationId("webapi2");
+ assertEquals(JWT_BEARER, webapi2.getAuthorizationGrantType());
+ });
+ }
+
@Test
void clientWithClientCredentialsPermissions() {
webApplicationContextRunner()
@@ -137,7 +186,7 @@ void clientWithClientCredentialsPermissions() {
.run(context -> {
ClientRegistrationRepository repository = context.getBean(ClientRegistrationRepository.class);
assertEquals(repository.findByRegistrationId(AZURE_CLIENT_REGISTRATION_ID).getAuthorizationGrantType(),
- AuthorizationGrantType.AUTHORIZATION_CODE);
+ AUTHORIZATION_CODE);
assertEquals(repository.findByRegistrationId("graph").getAuthorizationGrantType(),
AuthorizationGrantType.CLIENT_CREDENTIALS);
});
@@ -257,6 +306,27 @@ void haveResourceServerScopeInAccessTokenWhenThereAreMultiResourceServerScopesIn
});
}
+ @Test
+ void rewriteAuthorizationGrantTypeWhenIsOnBehalfOf() {
+ rewriteAuthorizationGrantTypeWhenIsOnBehalfOfByRunner(resourceServerWithOboContextRunner());
+ rewriteAuthorizationGrantTypeWhenIsOnBehalfOfByRunner(webApplicationAndResourceServerContextRunner());
+ }
+
+ private void rewriteAuthorizationGrantTypeWhenIsOnBehalfOfByRunner(WebApplicationContextRunner runner) {
+ runner
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.graph.scopes = fakeValue:/.default",
+ "spring.cloud.azure.active-directory.authorization-clients.graph.authorizationGrantType = on_behalf_of"
+ )
+ .run(context -> {
+ AadClientRegistrationRepository repository = context.getBean(AadClientRegistrationRepository.class);
+ assertNotNull(repository);
+ ClientRegistration graph = repository.findByRegistrationId("graph");
+ assertNotNull(graph);
+ assertTrue(JWT_BEARER.equals(graph.getAuthorizationGrantType()));
+ });
+ }
+
// TODO (moary) Enable this test.
// Related issue: https://github.com/Azure/azure-sdk-for-java/issues/23154
@Disabled
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/AadAuthenticationFilterPropertiesTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/AadAuthenticationFilterPropertiesTests.java
index 9d551c94213cb..16e2288800a9f 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/AadAuthenticationFilterPropertiesTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/AadAuthenticationFilterPropertiesTests.java
@@ -40,6 +40,8 @@ public void canSetProperties() {
assertThat(properties.getCredential().getClientId()).isEqualTo(TestConstants.CLIENT_ID);
assertThat(properties.getCredential().getClientSecret()).isEqualTo(TestConstants.CLIENT_SECRET);
+ assertThat(properties.getCredential().getClientCertificatePath()).isEqualTo(TestConstants.CLIENT_CERTIFICATE_PATH);
+ assertThat(properties.getCredential().getClientCertificatePassword()).isEqualTo(TestConstants.CLIENT_CERTIFICATE_PASSWORD);
assertThat(properties.getUserGroup().getAllowedGroupNames()
.toString()).isEqualTo(TestConstants.TARGETED_GROUPS.toString());
}
@@ -51,6 +53,8 @@ private void configureAllRequiredProperties(AnnotationConfigApplicationContext c
AAD_PROPERTY_PREFIX + "profile.tenant-id=demo-tenant-id",
AAD_PROPERTY_PREFIX + "credential.client-id=" + TestConstants.CLIENT_ID,
AAD_PROPERTY_PREFIX + "credential.client-secret=" + TestConstants.CLIENT_SECRET,
+ AAD_PROPERTY_PREFIX + "credential.client-certificate-path=" + TestConstants.CLIENT_CERTIFICATE_PATH,
+ AAD_PROPERTY_PREFIX + "credential.client-certificate-password=" + TestConstants.CLIENT_CERTIFICATE_PASSWORD,
AAD_PROPERTY_PREFIX + "user-group.allowed-group-names="
+ TestConstants.TARGETED_GROUPS.toString().replace("[", "").replace("]", "")
);
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/TestConstants.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/TestConstants.java
index 2bb24631d8559..cf26ca27b1d25 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/TestConstants.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/filter/TestConstants.java
@@ -10,6 +10,8 @@ public class TestConstants {
static final String CLIENT_ID = "real_client_id";
static final String CLIENT_SECRET = "real_client_secret";
+ static final String CLIENT_CERTIFICATE_PATH = "client_certificate_path";
+ static final String CLIENT_CERTIFICATE_PASSWORD = "client_certificate_password";
static final List TARGETED_GROUPS = Arrays.asList("group1", "group2", "group3");
static final String TOKEN_HEADER = "Authorization";
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestClientRegistrations.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestClientRegistrations.java
new file mode 100644
index 0000000000000..d6f512bacf2a4
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestClientRegistrations.java
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation;
+
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+
+public class TestClientRegistrations {
+
+ public static ClientRegistration.Builder clientRegistration(AuthorizationGrantType grantType,
+ ClientAuthenticationMethod method) {
+ return ClientRegistration
+ .withRegistrationId("test")
+ .clientId("test")
+ .clientSecret("test-secret")
+ .clientAuthenticationMethod(method)
+ .authorizationGrantType(grantType)
+ .tokenUri("http://localhost/token");
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestJwks.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestJwks.java
new file mode 100644
index 0000000000000..5dca4d976bd8e
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestJwks.java
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation;
+
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.util.Base64URL;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+
+public final class TestJwks {
+
+ // @formatter:off
+ public static final RSAKey DEFAULT_RSA_JWK =
+ jwk(
+ TestKeys.DEFAULT_PUBLIC_KEY,
+ TestKeys.DEFAULT_PRIVATE_KEY,
+ TestKeys.DEFAULT_CERTIFICATE
+ ).build();
+ // @formatter:on
+
+ private TestJwks() {
+ }
+
+ public static RSAKey.Builder jwk(RSAPublicKey publicKey, RSAPrivateKey privateKey, X509Certificate cert) {
+ // @formatter:off
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.update(cert.getEncoded());
+ byte[] bytes = digest.digest();
+ return new RSAKey.Builder(publicKey)
+ .privateKey(privateKey)
+ .x509CertThumbprint(Base64URL.encode(bytes))
+ .keyID("rsa-jwk-kid");
+ } catch (CertificateEncodingException | NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return null;
+ // @formatter:on
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestJwtClaimsSets.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestJwtClaimsSets.java
new file mode 100644
index 0000000000000..e48a36d9372df
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestJwtClaimsSets.java
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation;
+
+import org.springframework.security.oauth2.jwt.JwtClaimNames;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public final class TestJwtClaimsSets {
+
+ private TestJwtClaimsSets() {
+
+ }
+
+ public static Map jwtClaimsSet() {
+ Map jwtClaimsSet = new HashMap<>();
+ Instant issuedAt = Instant.now();
+ Instant expiresAt = issuedAt.plus(Duration.ofSeconds(60L));
+ jwtClaimsSet.put(JwtClaimNames.ISS, "https://localhost");
+ jwtClaimsSet.put(JwtClaimNames.SUB, "test");
+ jwtClaimsSet.put(JwtClaimNames.AUD, Collections.singletonList("client-1"));
+ jwtClaimsSet.put(JwtClaimNames.JTI, UUID.randomUUID().toString());
+ jwtClaimsSet.put(JwtClaimNames.IAT, issuedAt);
+ jwtClaimsSet.put(JwtClaimNames.EXP, expiresAt);
+ return jwtClaimsSet;
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestKeys.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestKeys.java
new file mode 100644
index 0000000000000..53b344c8ef98f
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/TestKeys.java
@@ -0,0 +1,132 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+
+public final class TestKeys {
+
+ public static final KeyFactory KF;
+ static {
+ try {
+ KF = KeyFactory.getInstance("RSA");
+ } catch (NoSuchAlgorithmException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ // @formatter:off
+ public static final String DEFAULT_RSA_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd"
+ + "7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv"
+ + "c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6"
+ + "iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2"
+ + "kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o"
+ + "RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj"
+ + "KwIDAQAB";
+ // @formatter:on
+
+ public static final RSAPublicKey DEFAULT_PUBLIC_KEY;
+ static {
+ X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PUBLIC_KEY));
+ try {
+ DEFAULT_PUBLIC_KEY = (RSAPublicKey) KF.generatePublic(spec);
+ } catch (InvalidKeySpecException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ // @formatter:off
+ public static final String DEFAULT_RSA_PRIVATE_KEY = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA"
+ + "iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM"
+ + "g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK"
+ + "LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF"
+ + "oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc"
+ + "3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn"
+ + "+jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE"
+ + "E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek"
+ + "lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG"
+ + "mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7"
+ + "62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0"
+ + "bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA"
+ + "+Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH"
+ + "Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA"
+ + "8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd"
+ + "I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY"
+ + "QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d"
+ + "rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk"
+ + "HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA"
+ + "Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN"
+ + "HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a"
+ + "FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF"
+ + "snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H"
+ + "c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM"
+ + "TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR"
+ + "47jndeyIaMTNETEmOnms+as17g==";
+ // @formatter:on
+
+ public static final RSAPrivateKey DEFAULT_PRIVATE_KEY;
+ static {
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PRIVATE_KEY));
+ try {
+ DEFAULT_PRIVATE_KEY = (RSAPrivateKey) KF.generatePrivate(spec);
+ } catch (InvalidKeySpecException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ // @formatter:off
+ public static final String DEFAULT_CERTIFICATE_KEY = "-----BEGIN CERTIFICATE-----\n"
+ + "MIIDzzCCAregAwIBAgIUEc1Q6X2u3x6iGQ7RjVeDJIjQ1XMwDQYJKoZIhvcNAQEL\n"
+ + "BQAwdzELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEPMA0GA1UEBwwGTG9u\n"
+ + "ZG9uMRgwFgYDVQQKDA9HbG9iYWwgU2VjdXJpdHkxFjAUBgNVBAsMDUlUIERlcGFy\n"
+ + "dG1lbnQxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIyMDUzMDA5NDE1N1oXDTIz\n"
+ + "MDUzMDA5NDE1N1owdzELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEPMA0G\n"
+ + "A1UEBwwGTG9uZG9uMRgwFgYDVQQKDA9HbG9iYWwgU2VjdXJpdHkxFjAUBgNVBAsM\n"
+ + "DUlUIERlcGFydG1lbnQxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG\n"
+ + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3OG2pj0NhQhpZhajw4i4Viq4ys8rjqMSn8uF\n"
+ + "eMlpYflRCSBrWi3/+ll+th/vdQRoE/N2hq6oMIoZ1oAXsXEeCIcw2vJrU+2Fd0N8\n"
+ + "D9CFQIHatFUQsJmIhih7bv7DzFTpHOVU7tzzs4WVDnqJfMsMHCGh+oAx1nBEN5LS\n"
+ + "1nsQoDBw32fW6mcQnmD+aWByeb9rHpSO2+E1XotRYnSzsMJY2tuLyIqH8647fXq+\n"
+ + "9K/olDgtGsha+TDJ1CX6UGS28zQ3BCaj7h54+y7LqOtFdw8ICn6Hoj39mNEtVlxC\n"
+ + "4hUFLLvDphMFC7/+WNuYCq9iIr7xJCj6c+1mTLNduZ30UuEk1QIDAQABo1MwUTAd\n"
+ + "BgNVHQ4EFgQUjFN264nAPC1KW4zs408p8gizoQ0wHwYDVR0jBBgwFoAUjFN264nA\n"
+ + "PC1KW4zs408p8gizoQ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n"
+ + "AQEAAX3VT+cMzihRo/2n0z1SHvY3Ozv+l7C4S709EmvdZBc4KaOzm8kJ+MTohmg9\n"
+ + "8TmJMtnBLcOiiEyd2qXZkD64vIfuWNsSjGDy/YBEqNNuwX+8LDbKbqJyeb07E+Hb\n"
+ + "63c+nogfCGgGXxuuRODMmh1rFprx60owNaNXYAE/K0DljOg8onYqSidVb1gIdmKN\n"
+ + "8EUJPDpryEo4Zt95ruAPrnoWcLvjUlKDjrnvjnAOy1wkAtywLcQbmYWXrYQojNuJ\n"
+ + "0DGXu1zSGh8dLo96tlvWYRVE4MzPQvRjNDhuShlw3c9+gTEandAODQ5Kj0wL1q0i\n"
+ + "F051Y7HdrnfZ/mK58D4VPm+EbQ==\n"
+ + "-----END CERTIFICATE-----";
+ // @formatter:on
+
+ public static final X509Certificate DEFAULT_CERTIFICATE;
+ static {
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ InputStream cert = new ByteArrayInputStream(DEFAULT_CERTIFICATE_KEY.getBytes(StandardCharsets.UTF_8));
+ DEFAULT_CERTIFICATE = (X509Certificate) certFactory.generateCertificate(cert);
+ } catch (CertificateException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ private TestKeys() {
+ }
+
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/WebApplicationContextRunnerUtils.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/WebApplicationContextRunnerUtils.java
index 594391601406c..a9136bcff64be 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/WebApplicationContextRunnerUtils.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/WebApplicationContextRunnerUtils.java
@@ -51,6 +51,13 @@ public static WebApplicationContextRunner resourceServerWithOboContextRunner() {
.withPropertyValues(withResourceServerPropertyValues());
}
+ public static WebApplicationContextRunner webApplicationAndResourceServerContextRunner() {
+ return oauthClientAndResourceServerRunner()
+ .withPropertyValues(withWebApplicationOrResourceServerWithOboPropertyValues())
+ .withPropertyValues(withResourceServerPropertyValues())
+ .withPropertyValues(withPropertyValueWebApplicationAndResourceServer());
+ }
+
@SuppressWarnings("unchecked")
public static MultiValueMap toMultiValueMap(RequestEntity> entity) {
return (MultiValueMap) Optional.ofNullable(entity)
@@ -68,7 +75,14 @@ public static String[] withWebApplicationOrResourceServerWithOboPropertyValues()
public static String[] withResourceServerPropertyValues() {
return new String[] {
+ "spring.cloud.azure.active-directory.enabled = true",
"spring.cloud.azure.active-directory.profile.tenant-id=fake-tenant-id",
"spring.cloud.azure.active-directory.app-id-uri=fake-app-id-uri"};
}
+
+ public static String[] withPropertyValueWebApplicationAndResourceServer() {
+ return new String[] {
+ "spring.cloud.azure.active-directory.application-type = web_application_and_resource_server"
+ };
+ }
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/ClientCertificatePropertiesConditionTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/ClientCertificatePropertiesConditionTests.java
new file mode 100644
index 0000000000000..54c1f956247b0
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/ClientCertificatePropertiesConditionTests.java
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.conditions;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Configuration;
+
+public class ClientCertificatePropertiesConditionTests extends AbstractCondition {
+
+ @Test
+ void match() {
+ this.contextRunner
+ .withPropertyValues(
+ "spring.cloud.azure.credential.client-certificate-path = client-certificate-path",
+ "spring.cloud.azure.credential.client-certificate-password = client-certificate-password"
+ )
+ .withUserConfiguration(ClientCertificateProperties.class)
+ .run(assertConditionMatch(true));
+ }
+
+ @Test
+ void noMatch() {
+ this.contextRunner
+ .withUserConfiguration(ClientCertificateProperties.class)
+ .run(assertConditionMatch(false));
+ }
+
+ @Configuration
+ @Conditional(ClientCertificatePropertiesCondition.class)
+ static class ClientCertificateProperties extends Config { }
+
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/ResourceServerWithOboConditionTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/ResourceServerWithOboConditionTests.java
deleted file mode 100644
index 84a06908487bc..0000000000000
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/ResourceServerWithOboConditionTests.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.spring.cloud.autoconfigure.aad.implementation.conditions;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.FilteredClassLoader;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
-
-class ResourceServerWithOboConditionTests extends AbstractCondition {
-
- @Test
- void testConditionWhenApplicationTypeInferenceIsWebApplication() {
- this.contextRunner
- .withPropertyValues("spring.cloud.azure.active-directory.credential.client-id = fake-client-id")
- .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class))
- .withUserConfiguration(ResourceServerWithOBOConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsWebApplication() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=web_application")
- .withUserConfiguration(ResourceServerWithOBOConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsResourceServer() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=resource_server")
- .withUserConfiguration(ResourceServerWithOBOConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsResourceServerWithOBO() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=resource_server_with_obo")
- .withUserConfiguration(ResourceServerWithOBOConditionConfig.class)
- .run(assertConditionMatch(true));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsWebApplicationAndResourceServer() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=web_application_and_resource_server")
- .withUserConfiguration(ResourceServerWithOBOConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Configuration
- @Conditional(ResourceServerWithOBOCondition.class)
- static class ResourceServerWithOBOConditionConfig extends Config { }
-}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/WebApplicationAndResourceServerConditionTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/WebApplicationAndResourceServerConditionTests.java
deleted file mode 100644
index fcda5b5a8c7d1..0000000000000
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/WebApplicationAndResourceServerConditionTests.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.spring.cloud.autoconfigure.aad.implementation.conditions;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.FilteredClassLoader;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
-
-class WebApplicationAndResourceServerConditionTests extends AbstractCondition {
-
- @Test
- void testConditionWhenApplicationTypeInferenceIsWebApplication() {
- this.contextRunner
- .withPropertyValues("spring.cloud.azure.active-directory.credential.client-id = fake-client-id")
- .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class))
- .withUserConfiguration(WebApplicationAndResourceServerConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsWebApplication() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=web_application")
- .withUserConfiguration(WebApplicationAndResourceServerConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsResourceServer() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=resource_server")
- .withUserConfiguration(WebApplicationAndResourceServerConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsResourceServerWithOBO() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=resource_server_with_obo")
- .withUserConfiguration(WebApplicationAndResourceServerConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsWebApplicationAndResourceServer() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=web_application_and_resource_server")
- .withUserConfiguration(WebApplicationAndResourceServerConditionConfig.class)
- .run(assertConditionMatch(true));
- }
-
- @Configuration
- @Conditional(WebApplicationAndResourceServerCondition.class)
- static class WebApplicationAndResourceServerConditionConfig extends Config { }
-}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/WebApplicationWithoutResourceServerConditionTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/WebApplicationWithoutResourceServerConditionTests.java
deleted file mode 100644
index 37219d009abe3..0000000000000
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/conditions/WebApplicationWithoutResourceServerConditionTests.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.spring.cloud.autoconfigure.aad.implementation.conditions;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.FilteredClassLoader;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
-
-class WebApplicationWithoutResourceServerConditionTests extends AbstractCondition {
-
- @Test
- void testConditionWhenApplicationTypeInferenceIsWebApplication() {
- this.contextRunner
- .withPropertyValues("spring.cloud.azure.active-directory.credential.client-id = fake-client-id")
- .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class))
- .withUserConfiguration(WebApplicationWithoutResourceServerConditionConfig.class)
- .run(assertConditionMatch(true));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsWebApplication() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=web_application")
- .withUserConfiguration(WebApplicationWithoutResourceServerConditionConfig.class)
- .run(assertConditionMatch(true));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsResourceServer() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=resource_server")
- .withUserConfiguration(WebApplicationWithoutResourceServerConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsResourceServerWithOBO() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=resource_server_with_obo")
- .withUserConfiguration(WebApplicationWithoutResourceServerConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Test
- void testConditionWhenApplicationTypeIsWebApplicationAndResourceServer() {
- this.contextRunner
- .withPropertyValues(
- "spring.cloud.azure.active-directory.credential.client-id = fake-client-id",
- "spring.cloud.azure.active-directory.application-type=web_application_and_resource_server")
- .withUserConfiguration(WebApplicationWithoutResourceServerConditionConfig.class)
- .run(assertConditionMatch(false));
- }
-
- @Configuration
- @Conditional(WebApplicationWithoutResourceServerCondition.class)
- static class WebApplicationWithoutResourceServerConditionConfig extends Config { }
-}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtClientAuthenticationParametersConverterTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtClientAuthenticationParametersConverterTests.java
new file mode 100644
index 0000000000000..495c61556e6ec
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtClientAuthenticationParametersConverterTests.java
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.jwt;
+
+import com.azure.spring.cloud.autoconfigure.aad.implementation.TestJwks;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.util.Base64URL;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTParser;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
+import org.springframework.util.MultiValueMap;
+
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.function.Function;
+
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.TestClientRegistrations.clientRegistration;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.jupiter.api.Assertions.assertNull;
+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.springframework.security.oauth2.core.AuthorizationGrantType.CLIENT_CREDENTIALS;
+import static org.springframework.security.oauth2.core.ClientAuthenticationMethod.CLIENT_SECRET_JWT;
+import static org.springframework.security.oauth2.core.ClientAuthenticationMethod.PRIVATE_KEY_JWT;
+
+public class AadJwtClientAuthenticationParametersConverterTests {
+
+ private Function jwkResolver;
+
+ private AadJwtClientAuthenticationParametersConverter converter;
+
+ @BeforeEach
+ @SuppressWarnings("unchecked")
+ public void setup() {
+ this.jwkResolver = mock(Function.class);
+ this.converter = new AadJwtClientAuthenticationParametersConverter<>(this.jwkResolver);
+ }
+
+ @Test
+ void convertNull() {
+ OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest =
+ new OAuth2ClientCredentialsGrantRequest(clientRegistration(CLIENT_CREDENTIALS, CLIENT_SECRET_JWT).build());
+ assertNull(this.converter.convert(clientCredentialsGrantRequest));
+ }
+
+ @Test
+ void resolveNullJwkThenThrowOAuth2AuthorizationException() {
+ OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest =
+ new OAuth2ClientCredentialsGrantRequest(clientRegistration(CLIENT_CREDENTIALS, PRIVATE_KEY_JWT).build());
+ assertThatExceptionOfType(OAuth2AuthorizationException.class)
+ .isThrownBy(() -> this.converter.convert(clientCredentialsGrantRequest))
+ .withMessage("[invalid_key] Failed to resolve JWK signing key for client registration 'test'.");
+ }
+
+ @Test
+ void testAssertion() throws ParseException {
+ RSAKey rsaJwk = spy(TestJwks.DEFAULT_RSA_JWK);
+ given(this.jwkResolver.apply(any())).willReturn(rsaJwk);
+ given(rsaJwk.getX509CertThumbprint()).willReturn(new Base64URL("dGVzdA"));
+ ClientRegistration registration = clientRegistration(CLIENT_CREDENTIALS, PRIVATE_KEY_JWT).build();
+ OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest =
+ new OAuth2ClientCredentialsGrantRequest(registration);
+ MultiValueMap parameters = this.converter.convert(clientCredentialsGrantRequest);
+
+ assertThat(parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE))
+ .isEqualTo("urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
+ String encodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);
+ assertThat(encodedJws).isNotNull();
+
+ JWT jwt = JWTParser.parse(encodedJws);
+
+ JWSHeader header = (JWSHeader) jwt.getHeader();
+ assertThat(header.getAlgorithm().getName()).isEqualTo(SignatureAlgorithm.RS256.getName());
+ assertThat(jwt.getJWTClaimsSet().getIssuer().equals(registration.getClientId()));
+ assertThat(jwt.getJWTClaimsSet().getSubject()).isEqualTo(registration.getClientId());
+ assertThat(jwt.getJWTClaimsSet().getAudience())
+ .isEqualTo(Collections.singletonList(registration.getProviderDetails().getTokenUri()));
+ assertThat(jwt.getJWTClaimsSet().getJWTID()).isNotNull();
+ assertThat(jwt.getJWTClaimsSet().getIssueTime()).isNotNull();
+ assertThat(jwt.getJWTClaimsSet().getExpirationTime()).isNotNull();
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtEncoderTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtEncoderTests.java
new file mode 100644
index 0000000000000..0855de81ecf8d
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/jwt/AadJwtEncoderTests.java
@@ -0,0 +1,79 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.jwt;
+
+import com.azure.spring.cloud.autoconfigure.aad.implementation.TestJwtClaimsSets;
+import com.nimbusds.jose.KeySourceException;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.proc.SecurityContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
+import org.springframework.security.oauth2.jwt.JwtException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+public class AadJwtEncoderTests {
+
+ private List jwkList;
+
+ private JWKSource jwkSource;
+
+ private AadJwtEncoder jwtEncoder;
+
+ @BeforeEach
+ public void setUp() {
+ this.jwkList = new ArrayList<>();
+ this.jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(new JWKSet(this.jwkList));
+ this.jwtEncoder = new AadJwtEncoder(this.jwkSource);
+ }
+
+ @Test
+ public void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() {
+ assertThatIllegalArgumentException().isThrownBy(() -> new AadJwtEncoder(null))
+ .withMessage("jwkSource cannot be null");
+ }
+
+ @Test
+ public void encodeWhenHeadersNullThenThrowIllegalArgumentException() {
+ Map jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet();
+ assertThatIllegalArgumentException().isThrownBy(() -> this.jwtEncoder.encode(null, jwtClaimsSet))
+ .withMessage("jwsHeader cannot be null");
+ }
+
+ @Test
+ public void encodeWhenClaimsNullThenThrowIllegalArgumentException() {
+ Map jwsHeader = new HashMap<>();
+ jwsHeader.put("alg", SignatureAlgorithm.RS256.getName());
+ assertThatIllegalArgumentException().isThrownBy(() -> this.jwtEncoder.encode(jwsHeader, null))
+ .withMessage("jwtClaimsSet cannot be null");
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void encodeWhenJwkSelectFailedThenThrowJwtEncodingException() throws Exception {
+ this.jwkSource = mock(JWKSource.class);
+ this.jwtEncoder = new AadJwtEncoder(this.jwkSource);
+ given(this.jwkSource.get(any(), any())).willThrow(new KeySourceException("key source error"));
+
+ Map jwsHeader = new HashMap<>();
+ jwsHeader.put("alg", SignatureAlgorithm.RS256.getName());
+ Map jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet();
+
+ assertThatExceptionOfType(JwtException.class)
+ .isThrownBy(() -> this.jwtEncoder.encode(jwsHeader, jwtClaimsSet))
+ .withMessageContaining("Failed to select a JWK signing key -> key source error");
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2ClientAuthenticationJwkResolverTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2ClientAuthenticationJwkResolverTests.java
new file mode 100644
index 0000000000000..10b1f001c2814
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/oauth2/AadOAuth2ClientAuthenticationJwkResolverTests.java
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.oauth2;
+
+import com.azure.spring.cloud.autoconfigure.aad.implementation.TestJwks;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.KeyType;
+import com.nimbusds.jose.jwk.RSAKey;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+
+import javax.xml.bind.DatatypeConverter;
+import java.util.function.Function;
+
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.TestClientRegistrations.clientRegistration;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.JWT_BEARER;
+import static org.springframework.security.oauth2.core.ClientAuthenticationMethod.CLIENT_SECRET_JWT;
+import static org.springframework.security.oauth2.core.ClientAuthenticationMethod.NONE;
+
+public class AadOAuth2ClientAuthenticationJwkResolverTests {
+
+ private Function jwkFunction;
+
+ @BeforeEach
+ @SuppressWarnings("unchecked")
+ public void setup() {
+ this.jwkFunction = mock(Function.class);
+ }
+
+ @Test
+ void resolveJwkFunction() {
+ AadOAuth2ClientAuthenticationJwkResolver jwkResolver =
+ new AadOAuth2ClientAuthenticationJwkResolver("D:\\test\\test.pfx", "test");
+ assertNull(jwkResolver.resolve(clientRegistration(JWT_BEARER, CLIENT_SECRET_JWT).build()));
+ assertNull(jwkResolver.resolve(clientRegistration(JWT_BEARER, NONE).build()));
+ }
+
+ @Test
+ void jwkValue() {
+ RSAKey rsaJwk = Mockito.spy(TestJwks.DEFAULT_RSA_JWK);
+ given(this.jwkFunction.apply(any())).willReturn(rsaJwk);
+
+ JWK jwk = jwkFunction.apply(clientRegistration(JWT_BEARER, CLIENT_SECRET_JWT).build());
+ assertEquals("F6A8558E721545972D3B8EE60BA22F913A322601",
+ DatatypeConverter.printHexBinary(jwk.getX509CertThumbprint().decode()));
+ assertEquals(KeyType.RSA, jwk.getKeyType());
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadJwtBearerGrantRequestEntityConverterTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadJwtBearerGrantRequestEntityConverterTests.java
new file mode 100644
index 0000000000000..cea314080a091
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadJwtBearerGrantRequestEntityConverterTests.java
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.aad.implementation.webapi;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.http.RequestEntity;
+import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.util.MultiValueMap;
+
+import java.time.Instant;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AadJwtBearerGrantRequestEntityConverterTests {
+
+ @SuppressWarnings("unchecked")
+ @Test
+ void requestedTokenUseParameter() {
+ ClientRegistration clientRegistration = ClientRegistration.withRegistrationId("test")
+ .clientId("test")
+ .clientSecret("test-secret")
+ .authorizationGrantType(AuthorizationGrantType.JWT_BEARER)
+ .tokenUri("http://localhost/token")
+ .build();
+ Jwt jwt = Jwt.withTokenValue("jwt-token-value")
+ .header("alg", JwsAlgorithms.RS256)
+ .claim("sub", "test")
+ .issuedAt(Instant.ofEpochMilli(Instant.now().toEpochMilli()))
+ .expiresAt(Instant.ofEpochMilli(Instant.now().plusSeconds(60).toEpochMilli()))
+ .build();
+ JwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, jwt);
+ AadJwtBearerGrantRequestEntityConverter converter =
+ new AadJwtBearerGrantRequestEntityConverter();
+ RequestEntity> entity =
+ (RequestEntity>) converter.convert(request);
+ MultiValueMap parameters = entity.getBody();
+ assertTrue(parameters.containsKey("requested_token_use"));
+ assertEquals("on_behalf_of", parameters.getFirst("requested_token_use"));
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadResourceServerConfigurationTests.java
index 341c4f6db83d4..d92496cd5e59d 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadResourceServerConfigurationTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapi/AadResourceServerConfigurationTests.java
@@ -6,7 +6,6 @@
import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthenticationProperties;
import com.nimbusds.jwt.proc.JWTClaimsSetAwareJWSKeySelector;
import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
@@ -27,7 +26,7 @@ class AadResourceServerConfigurationTests {
void testNotExistBearerTokenAuthenticationToken() {
resourceServerContextRunner()
.withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class))
- .run(context -> assertThrows(NoSuchBeanDefinitionException.class,
+ .run(context -> assertThrows(IllegalStateException.class,
() -> context.getBean(JWTClaimsSetAwareJWSKeySelector.class)));
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProviderTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProviderTests.java
index 11c5f655fae84..9e316927fe0cc 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProviderTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/AadAzureDelegatedOAuth2AuthorizedClientProviderTests.java
@@ -3,7 +3,6 @@
package com.azure.spring.cloud.autoconfigure.aad.implementation.webapp;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
import org.junit.jupiter.api.Test;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
@@ -12,36 +11,38 @@
import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
-import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import java.time.Instant;
import static com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository.AZURE_CLIENT_REGISTRATION_ID;
+import static com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants.AZURE_DELEGATED;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.CLIENT_CREDENTIALS;
class AadAzureDelegatedOAuth2AuthorizedClientProviderTests {
private static final ClientRegistration AZURE_CLIENT_REGISTRATION =
toClientRegistrationBuilder(AZURE_CLIENT_REGISTRATION_ID)
- .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+ .authorizationGrantType(AUTHORIZATION_CODE)
.build();
private static final ClientRegistration DELEGATED_CLIENT_REGISTRATION =
toClientRegistrationBuilder("delegated")
- .authorizationGrantType(new AuthorizationGrantType(AadAuthorizationGrantType.AZURE_DELEGATED.getValue()))
+ .authorizationGrantType(AZURE_DELEGATED)
.scope("testScope")
.build();
private static final ClientRegistration CLIENT_CREDENTIALS_CLIENT_REGISTRATION =
toClientRegistrationBuilder("clientCredentials")
- .authorizationGrantType(new AuthorizationGrantType(AadAuthorizationGrantType.CLIENT_CREDENTIALS.getValue()))
+ .authorizationGrantType(CLIENT_CREDENTIALS)
.build();
private static ClientRegistration.Builder toClientRegistrationBuilder(String registrationId) {
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/SerializerUtilsTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/SerializerUtilsTests.java
index f862c5d372ab8..5e4fa1fc0ef33 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/SerializerUtilsTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/implementation/webapp/SerializerUtilsTests.java
@@ -2,7 +2,7 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.autoconfigure.aad.implementation.webapp;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
+import com.azure.spring.cloud.autoconfigure.aad.implementation.constants.Constants;
import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -18,6 +18,9 @@
import static com.azure.spring.cloud.autoconfigure.aad.implementation.jackson.SerializerUtils.serializeOAuth2AuthorizedClientMap;
import static com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository.AZURE_CLIENT_REGISTRATION_ID;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.CLIENT_CREDENTIALS;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.JWT_BEARER;
class SerializerUtilsTests {
@@ -27,17 +30,14 @@ class SerializerUtilsTests {
void serializeAndDeserializeTest() {
Map authorizedClients = new HashMap<>();
authorizedClients.put(AZURE_CLIENT_REGISTRATION_ID,
- createOAuth2AuthorizedClient(AZURE_CLIENT_REGISTRATION_ID,
- AadAuthorizationGrantType.AUTHORIZATION_CODE.getValue()));
+ createOAuth2AuthorizedClient(AZURE_CLIENT_REGISTRATION_ID, AUTHORIZATION_CODE.getValue()));
authorizedClients.put("graph",
createOAuth2AuthorizedClient("graph",
- AadAuthorizationGrantType.AZURE_DELEGATED.getValue()));
+ Constants.AZURE_DELEGATED.getValue()));
authorizedClients.put("arm",
- createOAuth2AuthorizedClient("arm",
- AadAuthorizationGrantType.CLIENT_CREDENTIALS.getValue()));
+ createOAuth2AuthorizedClient("arm", CLIENT_CREDENTIALS.getValue()));
authorizedClients.put("office",
- createOAuth2AuthorizedClient("office",
- AadAuthorizationGrantType.ON_BEHALF_OF.getValue()));
+ createOAuth2AuthorizedClient("office", JWT_BEARER.getValue()));
String serializedOAuth2AuthorizedClients = serializeOAuth2AuthorizedClientMap(authorizedClients);
LOGGER.info(serializedOAuth2AuthorizedClients);
Map deserializedOAuth2AuthorizedClients =
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationPropertiesTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationPropertiesTests.java
index 6eb4fe54a00dd..3720d997518fc 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationPropertiesTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aad/properties/AadAuthenticationPropertiesTests.java
@@ -3,18 +3,25 @@
package com.azure.spring.cloud.autoconfigure.aad.properties;
+import com.azure.spring.cloud.autoconfigure.aad.implementation.jwt.AadJwtEncoder;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import java.util.Arrays;
import java.util.Map;
+import static com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository.AZURE_CLIENT_REGISTRATION_ID;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.oauthClientRunner;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.resourceServerContextRunner;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.resourceServerWithOboContextRunner;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.webApplicationContextRunner;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
+import static org.springframework.security.oauth2.core.ClientAuthenticationMethod.PRIVATE_KEY_JWT;
class AadAuthenticationPropertiesTests {
@@ -32,7 +39,7 @@ void mapPropertiesSetting() {
Map authorizationClients = properties.getAuthorizationClients();
assertTrue(authorizationClients.containsKey("test"));
assertTrue(authorizationClients.get("test").getScopes().containsAll(Arrays.asList("test1", "test2")));
- assertEquals(authorizationClients.get("test").getAuthorizationGrantType(), AadAuthorizationGrantType.AUTHORIZATION_CODE);
+ assertEquals(authorizationClients.get("test").getAuthorizationGrantType(), AUTHORIZATION_CODE);
Map authenticateAdditionalParameters = properties.getAuthenticateAdditionalParameters();
assertEquals(authenticateAdditionalParameters.size(), 1);
@@ -50,6 +57,98 @@ void webAppWithOboWithExceptionTest() {
assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class)));
}
+ @Test
+ void webAppWithJwtBearerWithExceptionTest() {
+ webApplicationContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.graph.authorizationGrantType = urn:ietf:params:oauth:grant-type:jwt-bearer")
+ .run(context ->
+ assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class)));
+ }
+
+ @Test
+ void resourceServerOboWithExceptionTest() {
+ resourceServerContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.graph.authorizationGrantType = on_behalf_of")
+ .run(context ->
+ assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class)));
+ }
+
+ @Test
+ void resourceServerJwtBearerWithExceptionTest() {
+ resourceServerContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.graph.authorizationGrantType = urn:ietf:params:oauth:grant-type:jwt-bearer")
+ .run(context ->
+ assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class)));
+ }
+
+ @Test
+ void supportedClientAuthenticationMethod() {
+ supportedClientAuthenticationMethodsByRunnerApplicationType(webApplicationContextRunner());
+ supportedClientAuthenticationMethodsByRunnerApplicationType(resourceServerContextRunner());
+ supportedClientAuthenticationMethodsByRunnerApplicationType(resourceServerWithOboContextRunner());
+ }
+
+ private void supportedClientAuthenticationMethodsByRunnerApplicationType(WebApplicationContextRunner runner) {
+ runner
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.one.client-authentication-method = basic",
+ "spring.cloud.azure.active-directory.authorization-clients.one.scopes = test",
+ "spring.cloud.azure.active-directory.authorization-clients.two.client-authentication-method = client_secret_basic",
+ "spring.cloud.azure.active-directory.authorization-clients.two.scopes = test",
+ "spring.cloud.azure.active-directory.authorization-clients.three.client-authentication-method = post",
+ "spring.cloud.azure.active-directory.authorization-clients.three.scopes = test",
+ "spring.cloud.azure.active-directory.authorization-clients.four.client-authentication-method = client_secret_post",
+ "spring.cloud.azure.active-directory.authorization-clients.four.scopes = test",
+ "spring.cloud.azure.active-directory.authorization-clients.five.client-authentication-method = private_key_jwt",
+ "spring.cloud.azure.active-directory.authorization-clients.five.scopes = test"
+ )
+ .run(context ->
+ assertThat(context.getBean(AadAuthenticationProperties.class)).isNotNull());
+ }
+
+ @Test
+ void unsupportedClientAuthenticationMethods() {
+ unsupportedClientAuthenticationMethodsByRunnerApplicationType(webApplicationContextRunner());
+ unsupportedClientAuthenticationMethodsByRunnerApplicationType(resourceServerContextRunner());
+ unsupportedClientAuthenticationMethodsByRunnerApplicationType(resourceServerWithOboContextRunner());
+ }
+
+ private void unsupportedClientAuthenticationMethodsByRunnerApplicationType(WebApplicationContextRunner runner) {
+ runner
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.one.client-authentication-method = client_secret_jwt",
+ "spring.cloud.azure.active-directory.authorization-clients.one.scopes = test"
+ )
+ .run(context ->
+ assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class)));
+ }
+
+ @Test
+ void overrideClientAuthenticationMethodForClientAzure() {
+ webApplicationContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.azure.client-authentication-method = private_key_jwt")
+ .run(context -> {
+ AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class);
+ assertThat(properties).isNotNull();
+ Map authorizationClients = properties.getAuthorizationClients();
+ assertEquals(PRIVATE_KEY_JWT,
+ authorizationClients.get(AZURE_CLIENT_REGISTRATION_ID).getClientAuthenticationMethod());
+ });
+ }
+
+ @Test
+ void configureScopesNullWithExceptionTest() {
+ webApplicationContextRunner()
+ .withPropertyValues(
+ "spring.cloud.azure.active-directory.authorization-clients.test.client-authentication-method = private_key_jwt")
+ .run(context ->
+ assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class)));
+ }
+
@Test
void graphUriConfigurationTest() {
webApplicationContextRunner()
@@ -226,7 +325,6 @@ void applicationTypeOfWebApplication() {
@Test
void applicationTypeWithResourceServer() {
resourceServerContextRunner()
- .withPropertyValues("spring.cloud.azure.active-directory.enabled=true")
.run(context -> {
AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class);
assertEquals(properties.getApplicationType(), AadApplicationType.RESOURCE_SERVER);
@@ -234,7 +332,6 @@ void applicationTypeWithResourceServer() {
resourceServerContextRunner()
.withPropertyValues(
- "spring.cloud.azure.active-directory.enabled=true",
"spring.cloud.azure.active-directory.application-type=resource_server"
)
.run(context -> {
@@ -284,7 +381,6 @@ void applicationTypeWithWebApplicationAndResourceServer() {
void testInvalidApplicationType() {
resourceServerContextRunner()
.withPropertyValues(
- "spring.cloud.azure.active-directory.enabled=true",
"spring.cloud.azure.active-directory.application-type=web_application"
)
.run(context -> assertThrows(IllegalStateException.class, () -> context.getBean(AadAuthenticationProperties.class)));
@@ -304,12 +400,16 @@ void setDefaultValueFromAzureGlobalPropertiesTest() {
"spring.cloud.azure.profile.tenant-id = global-tenant-id",
"spring.cloud.azure.active-directory.credential.client-id = aad-client-id",
"spring.cloud.azure.active-directory.credential.client-secret = aad-client-secret",
+ "spring.cloud.azure.active-directory.credential.client-certificate-path = certificate.pfx",
+ "spring.cloud.azure.active-directory.credential.client-certificate-password = aad-client-certificate-password",
"spring.cloud.azure.active-directory.profile.tenant-id = aad-tenant-id"
)
.run(context -> {
AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class);
assertEquals("aad-client-id", properties.getCredential().getClientId());
assertEquals("aad-client-secret", properties.getCredential().getClientSecret());
+ assertEquals("certificate.pfx", properties.getCredential().getClientCertificatePath());
+ assertEquals("aad-client-certificate-password", properties.getCredential().getClientCertificatePassword());
assertEquals("aad-tenant-id", properties.getProfile().getTenantId());
});
oauthClientRunner()
@@ -317,13 +417,23 @@ void setDefaultValueFromAzureGlobalPropertiesTest() {
"spring.cloud.azure.active-directory.enabled = true",
"spring.cloud.azure.credential.client-id = global-client-id",
"spring.cloud.azure.credential.client-secret = global-client-secret",
+ "spring.cloud.azure.credential.client-certificate-path = certificate.p12",
+ "spring.cloud.azure.credential.client-certificate-password = aad-client-certificate-password",
"spring.cloud.azure.profile.tenant-id = global-tenant-id"
)
.run(context -> {
AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class);
assertEquals("global-client-id", properties.getCredential().getClientId());
assertEquals("global-client-secret", properties.getCredential().getClientSecret());
+ assertEquals("certificate.p12", properties.getCredential().getClientCertificatePath());
+ assertEquals("aad-client-certificate-password", properties.getCredential().getClientCertificatePassword());
assertEquals("global-tenant-id", properties.getProfile().getTenantId());
});
}
+
+ @Test
+ public void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() {
+ assertThatIllegalArgumentException().isThrownBy(() -> new AadJwtEncoder(null))
+ .withMessage("jwkSource cannot be null");
+ }
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AadB2cAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AadB2cAutoConfigurationTests.java
index 862c09bda9f2c..bf362c8e230ab 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AadB2cAutoConfigurationTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AadB2cAutoConfigurationTests.java
@@ -2,12 +2,11 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.autoconfigure.aadb2c.implementation;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
import com.azure.spring.cloud.autoconfigure.aadb2c.AadB2cAuthorizationRequestResolver;
-import com.azure.spring.cloud.autoconfigure.aadb2c.AadB2cLogoutSuccessHandler;
import com.azure.spring.cloud.autoconfigure.aadb2c.AadB2cAutoConfiguration;
-import com.azure.spring.cloud.autoconfigure.aadb2c.properties.AadB2cProperties;
+import com.azure.spring.cloud.autoconfigure.aadb2c.AadB2cLogoutSuccessHandler;
import com.azure.spring.cloud.autoconfigure.aadb2c.AadB2cResourceServerAutoConfiguration;
+import com.azure.spring.cloud.autoconfigure.aadb2c.properties.AadB2cProperties;
import com.azure.spring.cloud.autoconfigure.aadb2c.properties.AuthorizationClientProperties;
import com.azure.spring.cloud.autoconfigure.context.AzureGlobalPropertiesAutoConfiguration;
import org.junit.jupiter.api.Assertions;
@@ -18,6 +17,7 @@
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import java.util.Arrays;
@@ -54,7 +54,7 @@ void mapPropertiesSetting() {
Map authorizationClients = properties.getAuthorizationClients();
assertTrue(authorizationClients.containsKey("test"));
assertTrue(authorizationClients.get("test").getScopes().containsAll(Arrays.asList("test1", "test2")));
- assertEquals(authorizationClients.get("test").getAuthorizationGrantType(), AadAuthorizationGrantType.CLIENT_CREDENTIALS);
+ assertEquals(authorizationClients.get("test").getAuthorizationGrantType(), AuthorizationGrantType.CLIENT_CREDENTIALS);
Map authenticateAdditionalParameters = properties.getAuthenticateAdditionalParameters();
assertEquals(authenticateAdditionalParameters.size(), 2);
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AbstractAadB2cOAuth2ClientTestConfigurations.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AbstractAadB2cOAuth2ClientTestConfigurations.java
index 8fdae8b5f024d..4171db1f2ca61 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AbstractAadB2cOAuth2ClientTestConfigurations.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/aadb2c/implementation/AbstractAadB2cOAuth2ClientTestConfigurations.java
@@ -5,7 +5,6 @@
import com.azure.spring.cloud.autoconfigure.aadb2c.configuration.AadB2cOAuth2ClientConfiguration;
import com.azure.spring.cloud.autoconfigure.aadb2c.properties.AadB2cProperties;
import com.azure.spring.cloud.autoconfigure.aadb2c.properties.AuthorizationClientProperties;
-import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthorizationGrantType;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
@@ -16,6 +15,7 @@
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import java.util.Map;
@@ -58,7 +58,7 @@ void testClientCredentialProperties() {
Assertions.assertEquals(authorizationClients.get(clientName).getScopes().get(0),
AadB2cConstants.TEST_CLIENT_CREDENTIAL_SCOPES);
Assertions.assertEquals(authorizationClients.get(clientName).getAuthorizationGrantType(),
- AadAuthorizationGrantType.CLIENT_CREDENTIALS);
+ AuthorizationGrantType.CLIENT_CREDENTIALS);
}
});
}