From cd0feeb94324c32a585ca42c521e43cf622fe2b0 Mon Sep 17 00:00:00 2001 From: Avery-Dunn <62066438+Avery-Dunn@users.noreply.github.com> Date: Fri, 25 Mar 2022 17:37:39 -0700 Subject: [PATCH] Release 1.11.3 (#486) * fix: Correct scope for test dependency in POM The `azure-security-keyvault-secrets` test dependency was include twice and without the correct `test` scope. * Updated keyvault secrets version * Allow client assertion to be a callback and a per-request parameter (#482) * Allow creating a client assertion from a callback * Allow client credentials as a per-request parameter * Minor changes to fix build issues * Bump version numbers for 1.11.3 release (#484) Co-authored-by: B. Schellhaas Co-authored-by: Siddhi --- README.md | 6 +- changelog.txt | 5 ++ pom.xml | 8 +-- .../ClientCredentialsIT.java | 57 ++++++++++++++++--- .../aad/msal4j/ClientCredentialFactory.java | 29 ++++++++-- .../msal4j/ClientCredentialParameters.java | 5 ++ .../msal4j/ConfidentialClientApplication.java | 2 +- .../aad/msal4j/TokenRequestExecutor.java | 10 +++- src/samples/msal-b2c-web-sample/pom.xml | 2 +- src/samples/msal-obo-sample/pom.xml | 2 +- src/samples/msal-web-sample/pom.xml | 2 +- 11 files changed, 102 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index fed2b2d2..66461ee4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.11.2 +Current version - 1.11.3 You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt). @@ -28,12 +28,12 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.11.2 + 1.11.3 ``` ### Gradle -compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.11.2' +compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.11.3' ## Usage diff --git a/changelog.txt b/changelog.txt index 0eb2e9bd..e4dc6434 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +Version 1.11.3 +============= +- Allow client assertions as callbacks and as per-request parameters +- Adjust scope for azure-security-keyvault-secrets dependency + Version 1.11.2 ============= - Updated oauth2-oidc-sdk version to address security vulnerability diff --git a/pom.xml b/pom.xml index f83e6519..6268ba82 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.microsoft.azure msal4j - 1.11.2 + 1.11.3 jar msal4j @@ -102,11 +102,7 @@ com.azure azure-security-keyvault-secrets 4.3.5 - - - com.azure - azure-security-keyvault-secrets - 4.3.5 + test org.seleniumhq.selenium diff --git a/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java b/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java index 62cd0c25..08dbc1fc 100644 --- a/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java +++ b/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java @@ -16,6 +16,7 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collections; +import java.util.concurrent.Callable; import static com.microsoft.aad.msal4j.TestConstants.KEYVAULT_DEFAULT_SCOPE; @@ -48,16 +49,35 @@ public void acquireTokenClientCredentials_ClientSecret() throws Exception { public void acquireTokenClientCredentials_ClientAssertion() throws Exception { String clientId = "2afb0add-2f32-4946-ac90-81a02aa4550e"; - ClientAssertion clientAssertion = JwtHelper.buildJwt( - clientId, - (ClientCertificate) certificate, - "https://login.microsoftonline.com/common/oauth2/v2.0/token", - true); + ClientAssertion clientAssertion = getClientAssertion(clientId); + + IClientCredential credential = ClientCredentialFactory.createFromClientAssertion(clientAssertion.assertion()); + + assertAcquireTokenCommon(clientId, credential); + } + + @Test + public void acquireTokenClientCredentials_Callback() throws Exception { + String clientId = "2afb0add-2f32-4946-ac90-81a02aa4550e"; + + // Creates a valid client assertion using a callback, and uses it to build the client app and make a request + Callable callable = () -> { + ClientAssertion clientAssertion = getClientAssertion(clientId); - IClientCredential credential = ClientCredentialFactory.createFromClientAssertion( - clientAssertion.assertion()); + return clientAssertion.assertion(); + }; + + IClientCredential credential = ClientCredentialFactory.createFromCallback(callable); assertAcquireTokenCommon(clientId, credential); + + // Creates an invalid client assertion to build the application, but overrides it with a valid client assertion + // in the request parameters in order to make a successful token request + ClientAssertion invalidClientAssertion = getClientAssertion("abc"); + + IClientCredential invalidCredentials = ClientCredentialFactory.createFromClientAssertion(invalidClientAssertion.assertion()); + + assertAcquireTokenCommon_withParameters(clientId, invalidCredentials, credential); } @Test @@ -98,6 +118,13 @@ public void acquireTokenClientCredentials_DefaultCacheLookup() throws Exception Assert.assertNotEquals(result2.accessToken(), result3.accessToken()); } + private ClientAssertion getClientAssertion(String clientId) { + return JwtHelper.buildJwt( + clientId, + (ClientCertificate) certificate, + "https://login.microsoftonline.com/common/oauth2/v2.0/token", + true); + } private void assertAcquireTokenCommon(String clientId, IClientCredential credential) throws Exception { ConfidentialClientApplication cca = ConfidentialClientApplication.builder( @@ -113,4 +140,20 @@ private void assertAcquireTokenCommon(String clientId, IClientCredential credent Assert.assertNotNull(result); Assert.assertNotNull(result.accessToken()); } + + private void assertAcquireTokenCommon_withParameters(String clientId, IClientCredential credential, IClientCredential credentialParam) throws Exception { + + ConfidentialClientApplication cca = ConfidentialClientApplication.builder( + clientId, credential). + authority(TestConstants.MICROSOFT_AUTHORITY). + build(); + + IAuthenticationResult result = cca.acquireToken(ClientCredentialParameters + .builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE)).clientCredential(credentialParam) + .build()) + .get(); + + Assert.assertNotNull(result); + Assert.assertNotNull(result.accessToken()); + } } diff --git a/src/main/java/com/microsoft/aad/msal4j/ClientCredentialFactory.java b/src/main/java/com/microsoft/aad/msal4j/ClientCredentialFactory.java index ab7eafef..1d93319a 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ClientCredentialFactory.java +++ b/src/main/java/com/microsoft/aad/msal4j/ClientCredentialFactory.java @@ -9,6 +9,11 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import static com.microsoft.aad.msal4j.ParameterValidationUtils.validateNotNull; @@ -29,7 +34,7 @@ public static IClientSecret createFromSecret(String secret) { } /** - * Static method to create a {@link ClientCertificate} instance from a certificate + * Static method to create a {@link ClientCertificate} instance from a password-protected certificate. * * @param pkcs12Certificate InputStream containing PCKS12 formatted certificate * @param password certificate password @@ -48,7 +53,7 @@ public static IClientCertificate createFromCertificate(final InputStream pkcs12C } /** - * Static method to create a {@link ClientCertificate} instance. + * Static method to create a {@link ClientCertificate} instance from a private key/public certificate pair. * * @param key RSA private key to sign the assertion. * @param publicKeyCertificate x509 public certificate used for thumbprint @@ -61,7 +66,7 @@ public static IClientCertificate createFromCertificate(final PrivateKey key, fin } /** - * Static method to create a {@link ClientCertificate} instance. + * Static method to create a {@link ClientCertificate} instance from a certificate chain. * * @param key RSA private key to sign the assertion. * @param publicKeyCertificateChain ordered with the user's certificate first followed by zero or more certificate authorities @@ -75,12 +80,26 @@ public static IClientCertificate createFromCertificateChain(PrivateKey key, List } /** - * Static method to create a {@link ClientAssertion} instance. + * Static method to create a {@link ClientAssertion} instance from a JWT token encoded as a base64 URL encoded string. * - * @param clientAssertion Jwt token encoded as a base64 URL encoded string + * @param clientAssertion JWT token encoded as a base64 URL encoded string * @return {@link ClientAssertion} */ public static IClientAssertion createFromClientAssertion(String clientAssertion) { return new ClientAssertion(clientAssertion); } + + /** + * Static method to create a {@link ClientAssertion} instance from a provided Callable. + * + * @param callable Callable that produces a JWT token encoded as a base64 URL encoded string + * @return {@link ClientAssertion} + */ + public static IClientAssertion createFromCallback(Callable callable) throws ExecutionException, InterruptedException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + + Future future = executor.submit(callable); + + return new ClientAssertion(future.get()); + } } diff --git a/src/main/java/com/microsoft/aad/msal4j/ClientCredentialParameters.java b/src/main/java/com/microsoft/aad/msal4j/ClientCredentialParameters.java index 1190441f..367516c0 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ClientCredentialParameters.java +++ b/src/main/java/com/microsoft/aad/msal4j/ClientCredentialParameters.java @@ -49,6 +49,11 @@ public class ClientCredentialParameters implements IAcquireTokenParameters { */ private String tenant; + /** + * Overrides the client credentials for this request + */ + private IClientCredential clientCredential; + private static ClientCredentialParametersBuilder builder() { return new ClientCredentialParametersBuilder(); diff --git a/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java b/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java index 8d711dfb..9c96ca51 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java +++ b/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java @@ -125,7 +125,7 @@ private ClientAuthentication buildValidClientCertificateAuthority() { return createClientAuthFromClientAssertion(clientAssertion); } - private ClientAuthentication createClientAuthFromClientAssertion( + protected ClientAuthentication createClientAuthFromClientAssertion( final ClientAssertion clientAssertion) { final Map> map = new HashMap<>(); try { diff --git a/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java b/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java index 3230fc00..0c33c8c3 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java +++ b/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java @@ -73,7 +73,15 @@ OAuthHttpRequest createOauthHttpRequest() throws SerializeException, MalformedUR oauthHttpRequest.setQuery(URLUtils.serializeParameters(params)); if (msalRequest.application().clientAuthentication() != null) { - msalRequest.application().clientAuthentication().applyTo(oauthHttpRequest); + // If the client application has a client assertion to apply to the request, check if a new client assertion + // was supplied as a request parameter. If so, use the request's assertion instead of the application's + if (msalRequest instanceof ClientCredentialRequest && ((ClientCredentialRequest) msalRequest).parameters.clientCredential() != null) { + ((ConfidentialClientApplication) msalRequest.application()) + .createClientAuthFromClientAssertion((ClientAssertion) ((ClientCredentialRequest) msalRequest).parameters.clientCredential()) + .applyTo(oauthHttpRequest); + } else { + msalRequest.application().clientAuthentication().applyTo(oauthHttpRequest); + } } return oauthHttpRequest; } diff --git a/src/samples/msal-b2c-web-sample/pom.xml b/src/samples/msal-b2c-web-sample/pom.xml index 225e5c73..3fcf10ef 100644 --- a/src/samples/msal-b2c-web-sample/pom.xml +++ b/src/samples/msal-b2c-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.11.2 + 1.11.3 com.nimbusds diff --git a/src/samples/msal-obo-sample/pom.xml b/src/samples/msal-obo-sample/pom.xml index fe8b5be9..de59bb84 100644 --- a/src/samples/msal-obo-sample/pom.xml +++ b/src/samples/msal-obo-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.11.2 + 1.11.3 com.nimbusds diff --git a/src/samples/msal-web-sample/pom.xml b/src/samples/msal-web-sample/pom.xml index 0d54dfa1..25686f81 100644 --- a/src/samples/msal-web-sample/pom.xml +++ b/src/samples/msal-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.11.2 + 1.11.3 com.nimbusds