Skip to content

Commit a1dcbca

Browse files
committed
# Conflicts: # msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/InstanceDiscoveryTest.java # msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/InvalidAuthorityIT.java # msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/CIAMAuthority.java # src/main/java/com/microsoft/aad/msal4j/B2CAuthority.java
2 parents 8815e05 + a08b9be commit a1dcbca

File tree

332 files changed

+216
-363
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

332 files changed

+216
-363
lines changed

.github/workflows/codeql.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ jobs:
2121

2222
# Initializes the CodeQL tools for scanning.
2323
- name: Initialize CodeQL
24-
uses: github/codeql-action/init@v1
24+
uses: github/codeql-action/init@v2
2525
# Override language selection by uncommenting this and choosing your languages
2626
# with:
2727
# languages: go, javascript, csharp, python, cpp, java
2828

2929
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
3030
# If this step fails, then you should remove it and run the build manually (see below).
3131
- name: Autobuild
32-
uses: github/codeql-action/autobuild@v1
32+
uses: github/codeql-action/autobuild@v2
3333

3434
# ℹ️ Command-line programs to run using the OS shell.
3535
# 📚 https://git.io/JvXDl
@@ -43,4 +43,4 @@ jobs:
4343
# make release
4444

4545
- name: Perform CodeQL Analysis
46-
uses: github/codeql-action/analyze@v1
46+
uses: github/codeql-action/analyze@v2

.gitignore renamed to msal4j-sdk/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*.rar
2121

2222
# Intellij
23-
.idea/
23+
../.idea/
2424

2525
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
2626
hs_err_pid*
File renamed without changes.

src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java renamed to msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,12 @@ private void assertAcquireTokenInstanceAware(User user) {
206206
Assert.assertEquals(result.account().environment(), cachedResult.environment());
207207
}
208208

209-
@Test
209+
//@Test
210210
public void acquireTokensInHomeAndGuestClouds_ArlingtonAccount() throws MalformedURLException, ExecutionException, InterruptedException {
211211
acquireTokensInHomeAndGuestClouds(AzureEnvironment.AZURE_US_GOVERNMENT);
212212
}
213213

214-
@Test
214+
//@Test
215215
public void acquireTokensInHomeAndGuestClouds_MooncakeAccount() throws MalformedURLException, ExecutionException, InterruptedException {
216216
acquireTokensInHomeAndGuestClouds(AzureEnvironment.AZURE_CHINA);
217217
}

src/main/java/com/microsoft/aad/msal4j/AcquireTokenByAppProviderSupplier.java renamed to msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AcquireTokenByAppProviderSupplier.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import java.util.concurrent.CompletableFuture;
77
import java.util.concurrent.ExecutionException;
88

9+
/** Disclaimer - This class is meant to be used by the Azure SDK team only.
10+
* Any other teams are discouraged from using this class to prevent any side effects.
11+
*/
912
class AcquireTokenByAppProviderSupplier extends AuthenticationResultSupplier {
1013

1114
private static final int TWO_HOURS = 2*3600;
@@ -65,9 +68,14 @@ AuthenticationResult execute() throws Exception {
6568

6669
public AuthenticationResult fetchTokenUsingAppTokenProvider(AppTokenProviderParameters appTokenProviderParameters) throws ExecutionException, InterruptedException {
6770

68-
CompletableFuture<TokenProviderResult> completableFuture = this.clientCredentialRequest.appTokenProvider.apply(appTokenProviderParameters);
71+
TokenProviderResult tokenProviderResult;
72+
try{
73+
CompletableFuture<TokenProviderResult> completableFuture = this.clientCredentialRequest.appTokenProvider.apply(appTokenProviderParameters);
74+
tokenProviderResult = completableFuture.get();
6975

70-
TokenProviderResult tokenProviderResult = completableFuture.get();
76+
} catch (Exception ex){
77+
throw new MsalAzureSDKException(ex);
78+
}
7179

7280
validateAndUpdateTokenProviderResult(tokenProviderResult);
7381

@@ -78,6 +86,5 @@ public AuthenticationResult fetchTokenUsingAppTokenProvider(AppTokenProviderPara
7886
.expiresOn(tokenProviderResult.getExpiresInSeconds())
7987
.refreshOn(tokenProviderResult.getRefreshInSeconds())
8088
.build();
81-
8289
}
8390
}

src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java renamed to msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,9 @@ public class AuthenticationErrorCode {
115115
* A JWT parsing failure, indicating the JWT provided to MSAL is of invalid format.
116116
*/
117117
public final static String INVALID_JWT = "invalid_jwt";
118+
/**
119+
* Indicates that a Broker implementation is missing from the device, such as when an app developer
120+
* does not include one of our broker packages as a dependency in their project, or otherwise cannot
121+
* be accessed by MSAL Java*/
122+
public final static String MISSING_BROKER = "missing_broker";
118123
}

src/main/java/com/microsoft/aad/msal4j/AuthenticationResultSupplier.java renamed to msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationResultSupplier.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@
44
package com.microsoft.aad.msal4j;
55

66

7-
import java.nio.charset.StandardCharsets;
87
import java.net.MalformedURLException;
9-
import java.util.Base64;
10-
11-
import java.io.UnsupportedEncodingException;
128
import java.net.URI;
139
import java.net.URISyntaxException;
1410
import java.net.URL;
11+
import java.nio.charset.StandardCharsets;
1512
import java.security.MessageDigest;
1613
import java.security.NoSuchAlgorithmException;
14+
import java.util.Base64;
1715
import java.util.concurrent.CompletionException;
1816
import java.util.function.Supplier;
1917

@@ -114,8 +112,8 @@ private void logResult(AuthenticationResult result, HttpHeaders headers) {
114112
.refreshToken());
115113
if (clientApplication.logPii()) {
116114
clientApplication.log.debug(LogHelper.createMessage(String.format(
117-
"Access Token with hash '%s' and Refresh Token with hash '%s' returned",
118-
accessTokenHash, refreshTokenHash),
115+
"Access Token with hash '%s' and Refresh Token with hash '%s' returned",
116+
accessTokenHash, refreshTokenHash),
119117
headers.getHeaderCorrelationIdValue()));
120118
} else {
121119
clientApplication.log.debug(
@@ -126,7 +124,7 @@ private void logResult(AuthenticationResult result, HttpHeaders headers) {
126124
} else {
127125
if (clientApplication.logPii()) {
128126
clientApplication.log.debug(LogHelper.createMessage(String.format(
129-
"Access Token with hash '%s' returned", accessTokenHash),
127+
"Access Token with hash '%s' returned", accessTokenHash),
130128
headers.getHeaderCorrelationIdValue()));
131129
} else {
132130
clientApplication.log.debug(LogHelper.createMessage(
@@ -149,6 +147,9 @@ private void logException(Exception ex) {
149147
clientApplication.log.debug(logMessage, ex);
150148
return;
151149
}
150+
} else if (ex instanceof MsalAzureSDKException) {
151+
clientApplication.log.debug(ex.getMessage(), ex);
152+
return;
152153
}
153154

154155
clientApplication.log.error(logMessage, ex);
@@ -163,11 +164,7 @@ private ApiEvent initializeApiEvent(MsalRequest msalRequest) {
163164
apiEvent.setRequestId(msalRequest.requestContext().telemetryRequestId());
164165
apiEvent.setWasSuccessful(false);
165166

166-
if (clientApplication instanceof ConfidentialClientApplication) {
167-
apiEvent.setIsConfidentialClient(true);
168-
} else {
169-
apiEvent.setIsConfidentialClient(false);
170-
}
167+
apiEvent.setIsConfidentialClient(clientApplication instanceof ConfidentialClientApplication);
171168

172169
try {
173170
Authority authenticationAuthority = clientApplication.authenticationAuthority;

src/main/java/com/microsoft/aad/msal4j/ClientCertificate.java renamed to msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ClientCertificate.java

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
package com.microsoft.aad.msal4j;
55

6+
import lombok.Getter;
7+
import lombok.experimental.Accessors;
8+
69
import java.io.IOException;
710
import java.io.InputStream;
811
import java.lang.reflect.InvocationTargetException;
@@ -19,10 +22,11 @@
1922
import java.security.cert.CertificateException;
2023
import java.security.cert.X509Certificate;
2124
import java.security.interfaces.RSAPrivateKey;
22-
import java.util.*;
23-
24-
import lombok.Getter;
25-
import lombok.experimental.Accessors;
25+
import java.util.ArrayList;
26+
import java.util.Arrays;
27+
import java.util.Base64;
28+
import java.util.Enumeration;
29+
import java.util.List;
2630

2731
final class ClientCertificate implements IClientCertificate {
2832

@@ -97,14 +101,7 @@ static ClientCertificate create(InputStream pkcs12Certificate, String password)
97101
final KeyStore keystore = KeyStore.getInstance("PKCS12");
98102
keystore.load(pkcs12Certificate, password.toCharArray());
99103

100-
final Enumeration<String> aliases = keystore.aliases();
101-
if (!aliases.hasMoreElements()) {
102-
throw new IllegalArgumentException("certificate not loaded from input stream");
103-
}
104-
String alias = aliases.nextElement();
105-
if (aliases.hasMoreElements()) {
106-
throw new IllegalArgumentException("more than one certificate alias found in input stream");
107-
}
104+
String alias = getPrivateKeyAlias(keystore);
108105

109106
ArrayList<X509Certificate> publicKeyCertificateChain = new ArrayList<>();
110107
PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, password.toCharArray());
@@ -123,6 +120,26 @@ static ClientCertificate create(InputStream pkcs12Certificate, String password)
123120
return new ClientCertificate(privateKey, publicKeyCertificateChain);
124121
}
125122

123+
static String getPrivateKeyAlias(KeyStore keystore) throws KeyStoreException {
124+
String alias = null;
125+
final Enumeration<String> aliases = keystore.aliases();
126+
while (aliases.hasMoreElements()) {
127+
String currentAlias = aliases.nextElement();
128+
if (keystore.entryInstanceOf(currentAlias, KeyStore.PrivateKeyEntry.class)) {
129+
if (alias != null) {
130+
throw new IllegalArgumentException("more than one certificate alias found in input stream");
131+
}
132+
alias = currentAlias;
133+
}
134+
}
135+
136+
if (alias == null) {
137+
throw new IllegalArgumentException("certificate not loaded from input stream");
138+
}
139+
140+
return alias;
141+
}
142+
126143
static ClientCertificate create(final PrivateKey key, final X509Certificate publicKeyCertificate) {
127144
return new ClientCertificate(key, Arrays.asList(publicKeyCertificate));
128145
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.aad.msal4j;
5+
6+
import java.util.Set;
7+
import java.util.concurrent.CompletableFuture;
8+
9+
/**
10+
* Used to define the basic set of methods that all Brokers must implement
11+
*
12+
* All methods are so they can be referenced by MSAL Java without an implementation, and by default simply throw an
13+
* exception saying that a broker implementation is missing
14+
*/
15+
public interface IBroker {
16+
17+
/**
18+
* checks if a IBroker implementation exists
19+
*/
20+
21+
default boolean isAvailable(){
22+
return false;
23+
}
24+
/**
25+
* Acquire a token silently, i.e. without direct user interaction
26+
*
27+
* This may be accomplished by returning tokens from a token cache, using cached refresh tokens to get new tokens,
28+
* or via any authentication flow where a user is not prompted to enter credentials
29+
*
30+
* @param requestParameters MsalRequest object which contains everything needed for the broker implementation to make a request
31+
* @return IBroker implementations will return an AuthenticationResult object
32+
*/
33+
default IAuthenticationResult acquireToken(PublicClientApplication application, SilentParameters requestParameters) {
34+
throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER);
35+
}
36+
37+
/**
38+
* Acquire a token interactively, by prompting users to enter their credentials in some way
39+
*
40+
* @param requestParameters MsalRequest object which contains everything needed for the broker implementation to make a request
41+
* @return IBroker implementations will return an AuthenticationResult object
42+
*/
43+
default IAuthenticationResult acquireToken(PublicClientApplication application, InteractiveRequestParameters requestParameters) {
44+
throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER);
45+
}
46+
47+
/**
48+
* Acquire a token silently, i.e. without direct user interaction, using username/password authentication
49+
*
50+
* @param requestParameters MsalRequest object which contains everything needed for the broker implementation to make a request
51+
* @return IBroker implementations will return an AuthenticationResult object
52+
*/
53+
default IAuthenticationResult acquireToken(PublicClientApplication application, UserNamePasswordParameters requestParameters) {
54+
throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER);
55+
}
56+
57+
default CompletableFuture removeAccount(IAccount account) {
58+
throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER);
59+
}
60+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.microsoft.aad.msal4j;
2+
3+
/**
4+
* Exception type thrown when Azure SDK returns an error response.
5+
*/
6+
public class MsalAzureSDKException extends MsalException{
7+
public MsalAzureSDKException(Throwable throwable) {
8+
super(throwable);
9+
}
10+
11+
public MsalAzureSDKException(String message, String errorCode) {
12+
super(message, errorCode);
13+
}
14+
}

src/test/java/com/microsoft/aad/msal4j/CacheFormatTests.java renamed to msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/CacheFormatTests.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
package com.microsoft.aad.msal4j;
55

6-
import com.fasterxml.jackson.databind.DeserializationFeature;
7-
import com.fasterxml.jackson.databind.ObjectMapper;
86
import com.nimbusds.oauth2.sdk.ParseException;
97
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
108
import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
@@ -26,9 +24,6 @@
2624
import java.nio.charset.StandardCharsets;
2725
import java.nio.file.Files;
2826
import java.nio.file.Paths;
29-
import java.sql.Time;
30-
import java.time.Duration;
31-
import java.time.Instant;
3227
import java.util.*;
3328

3429
import static com.microsoft.aad.msal4j.Constants.POINT_DELIMITER;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.aad.msal4j;
5+
6+
import org.easymock.EasyMock;
7+
import org.testng.annotations.BeforeMethod;
8+
import org.testng.annotations.Test;
9+
10+
import java.security.KeyStore;
11+
import java.security.KeyStoreSpi;
12+
import java.util.Arrays;
13+
import java.util.Collections;
14+
15+
import static org.testng.AssertJUnit.assertEquals;
16+
17+
@Test
18+
public class ClientCertificatePkcs12Test extends AbstractMsalTests {
19+
20+
private KeyStoreSpi keyStoreSpi;
21+
private KeyStore keystore;
22+
23+
@BeforeMethod
24+
public void setUp() throws Exception {
25+
keyStoreSpi = EasyMock.createMock(KeyStoreSpi.class);
26+
keystore = new KeyStore(keyStoreSpi, null, "PKCS12") {};
27+
keystore.load(null);
28+
}
29+
30+
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "certificate not loaded from input stream")
31+
public void testNoEntries() throws Exception {
32+
EasyMock.expect(keyStoreSpi.engineAliases())
33+
.andReturn(Collections.enumeration(Collections.emptyList())).times(1);
34+
EasyMock.replay(keyStoreSpi);
35+
36+
ClientCertificate.getPrivateKeyAlias(keystore);
37+
}
38+
39+
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "certificate not loaded from input stream")
40+
public void testNoPrivateKey() throws Exception {
41+
EasyMock.expect(keyStoreSpi.engineAliases())
42+
.andReturn(Collections.enumeration(Arrays.asList("CA_cert1", "CA_cert2"))).times(1);
43+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("CA_cert1", KeyStore.PrivateKeyEntry.class)).andReturn(false).times(1);
44+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("CA_cert2", KeyStore.PrivateKeyEntry.class)).andReturn(false).times(1);
45+
EasyMock.replay(keyStoreSpi);
46+
47+
ClientCertificate.getPrivateKeyAlias(keystore);
48+
}
49+
50+
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "more than one certificate alias found in input stream")
51+
public void testMultiplePrivateKeyAliases() throws Exception {
52+
EasyMock.expect(keyStoreSpi.engineAliases())
53+
.andReturn(Collections.enumeration(Arrays.asList("private_key1", "private_key2", "CA_cert"))).times(1);
54+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("private_key1", KeyStore.PrivateKeyEntry.class)).andReturn(true).times(1);
55+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("private_key2", KeyStore.PrivateKeyEntry.class)).andReturn(true).times(1);
56+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("CA_cert", KeyStore.PrivateKeyEntry.class)).andReturn(false).times(1);
57+
EasyMock.replay(keyStoreSpi);
58+
59+
ClientCertificate.getPrivateKeyAlias(keystore);
60+
}
61+
62+
@Test
63+
public void testMultipleEntriesButOnlyOnePrivateKey() throws Exception {
64+
EasyMock.expect(keyStoreSpi.engineAliases())
65+
.andReturn(Collections.enumeration(Arrays.asList("CA_cert1", "private_key", "CA_cert2"))).times(1);
66+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("CA_cert1", KeyStore.PrivateKeyEntry.class)).andReturn(false).times(1);
67+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("private_key", KeyStore.PrivateKeyEntry.class)).andReturn(true).times(1);
68+
EasyMock.expect(keyStoreSpi.engineEntryInstanceOf("CA_cert2", KeyStore.PrivateKeyEntry.class)).andReturn(false).times(1);
69+
EasyMock.replay(keyStoreSpi);
70+
71+
String privateKeyAlias = ClientCertificate.getPrivateKeyAlias(keystore);
72+
assertEquals("private_key", privateKeyAlias);
73+
}
74+
75+
}

0 commit comments

Comments
 (0)