Skip to content

Commit b93bf63

Browse files
authored
Merge pull request #117 from AzureAD/sagonzal/addClientAssertion
Sagonzal/add client assertion
2 parents 70fc909 + e7e1c16 commit b93bf63

File tree

19 files changed

+257
-81
lines changed

19 files changed

+257
-81
lines changed

src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package com.microsoft.aad.msal4j;
55

6+
import labapi.AppIdentityProvider;
67
import labapi.FederationProvider;
78
import labapi.LabResponse;
89
import labapi.LabUserProvider;
@@ -13,6 +14,10 @@
1314

1415
import java.util.Collections;
1516
import java.util.Set;
17+
import java.util.concurrent.ExecutionException;
18+
19+
import static com.microsoft.aad.msal4j.TestConstants.GRAPH_DEFAULT_SCOPE;
20+
import static com.microsoft.aad.msal4j.TestConstants.KEYVAULT_DEFAULT_SCOPE;
1621

1722
public class AcquireTokenSilentIT {
1823
private LabUserProvider labUserProvider;
@@ -151,43 +156,78 @@ public void acquireTokenSilent_MultipleAccountsInCache_UseCorrectAccount() throw
151156
Assert.assertEquals(result.account().username(), labResponse.getUser().getUpn());
152157
}
153158

154-
private IPublicClientApplication getPublicClientApplicationWithTokensInCache()
155-
throws Exception {
159+
@Test
160+
public void acquireTokenSilent_usingCommonAuthority_returnCachedAt() throws Exception {
161+
acquireTokenSilent_returnCachedTokens(TestConstants.ORGANIZATIONS_AUTHORITY);
162+
}
163+
164+
@Test
165+
public void acquireTokenSilent_usingTenantSpecificAuthority_returnCachedAt() throws Exception {
156166
LabResponse labResponse = labUserProvider.getDefaultUser(
157167
NationalCloud.AZURE_CLOUD,
158168
false);
159-
String password = labUserProvider.getUserPassword(labResponse.getUser());
169+
String tenantSpecificAuthority = TestConstants.MICROSOFT_AUTHORITY_HOST + labResponse.getUser().getTenantId();
170+
acquireTokenSilent_returnCachedTokens(tenantSpecificAuthority);
171+
}
160172

161-
PublicClientApplication pca = PublicClientApplication.builder(
162-
labResponse.getAppId()).
163-
authority(TestConstants.ORGANIZATIONS_AUTHORITY).
164-
build();
173+
@Test
174+
public void acquireTokenSilent_ConfidentialClient_acquireTokenSilent() throws Exception{
165175

166-
pca.acquireToken(UserNamePasswordParameters.
167-
builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE),
168-
labResponse.getUser().getUpn(),
169-
password.toCharArray())
176+
IConfidentialClientApplication cca = getConfidentialClientApplications();
177+
178+
IAuthenticationResult result = cca.acquireToken(ClientCredentialParameters
179+
.builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE))
170180
.build())
171181
.get();
172-
return pca;
182+
183+
Assert.assertNotNull(result);
184+
Assert.assertNotNull(result.accessToken());
185+
186+
String cachedAt = result.accessToken();
187+
188+
result = cca.acquireTokenSilently(SilentParameters
189+
.builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE))
190+
.build())
191+
.get();
192+
193+
Assert.assertNotNull(result);
194+
Assert.assertEquals(result.accessToken(), cachedAt);
173195
}
174196

175-
@Test
176-
private void acquireTokenSilent_usingCommonAuthority_returnCachedAt() throws Exception {
177-
acquireTokenSilent_returnCachedTokens(TestConstants.ORGANIZATIONS_AUTHORITY);
197+
@Test(expectedExceptions = ExecutionException.class)
198+
public void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrowsException()
199+
throws Exception {
200+
201+
IConfidentialClientApplication cca = getConfidentialClientApplications();
202+
203+
IAuthenticationResult result = cca.acquireToken(ClientCredentialParameters
204+
.builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE))
205+
.build())
206+
.get();
207+
208+
Assert.assertNotNull(result);
209+
Assert.assertNotNull(result.accessToken());
210+
211+
//Acquiring token for different scope, expect exception to be thrown
212+
cca.acquireTokenSilently(SilentParameters
213+
.builder(Collections.singleton(GRAPH_DEFAULT_SCOPE))
214+
.build())
215+
.get();
178216
}
179217

180-
@Test
181-
private void acquireTokenSilent_usingTenantSpecificAuthority_returnCachedAt() throws Exception {
182-
LabResponse labResponse = labUserProvider.getDefaultUser(
183-
NationalCloud.AZURE_CLOUD,
184-
false);
185-
String tenantSpecificAuthority = TestConstants.MICROSOFT_AUTHORITY_HOST + labResponse.getUser().getTenantId();
218+
private IConfidentialClientApplication getConfidentialClientApplications() throws Exception{
219+
AppIdentityProvider appProvider = new AppIdentityProvider();
220+
final String clientId = appProvider.getDefaultLabId();
221+
final String password = appProvider.getDefaultLabPassword();
222+
IClientCredential credential = ClientCredentialFactory.createFromSecret(password);
186223

187-
acquireTokenSilent_returnCachedTokens(tenantSpecificAuthority);
224+
return ConfidentialClientApplication.builder(
225+
clientId, credential).
226+
authority(TestConstants.MICROSOFT_AUTHORITY).
227+
build();
188228
}
189229

190-
void acquireTokenSilent_returnCachedTokens(String authority) throws Exception {
230+
private void acquireTokenSilent_returnCachedTokens(String authority) throws Exception {
191231

192232
LabResponse labResponse = labUserProvider.getDefaultUser(
193233
NationalCloud.AZURE_CLOUD,
@@ -217,4 +257,25 @@ void acquireTokenSilent_returnCachedTokens(String authority) throws Exception {
217257
Assert.assertNotNull(silentAuthResult);
218258
Assert.assertEquals(interactiveAuthResult.accessToken(), silentAuthResult.accessToken());
219259
}
260+
261+
private IPublicClientApplication getPublicClientApplicationWithTokensInCache()
262+
throws Exception {
263+
LabResponse labResponse = labUserProvider.getDefaultUser(
264+
NationalCloud.AZURE_CLOUD,
265+
false);
266+
String password = labUserProvider.getUserPassword(labResponse.getUser());
267+
268+
PublicClientApplication pca = PublicClientApplication.builder(
269+
labResponse.getAppId()).
270+
authority(TestConstants.ORGANIZATIONS_AUTHORITY).
271+
build();
272+
273+
pca.acquireToken(
274+
UserNamePasswordParameters.builder(
275+
Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE),
276+
labResponse.getUser().getUpn(),
277+
password.toCharArray())
278+
.build()).get();
279+
return pca;
280+
}
220281
}

src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ private IAuthenticationResult acquireTokenInteractiveB2C(LabResponse labResponse
247247
String authCode) {
248248
IAuthenticationResult result;
249249
try{
250-
IClientCredential credential = ClientCredentialFactory.create("");
250+
IClientCredential credential = ClientCredentialFactory.createFromSecret("");
251251
ConfidentialClientApplication cca = ConfidentialClientApplication.builder(
252252
labResponse.getAppId(),
253253
credential)

src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
@Test
2525
public class ClientCredentialsIT {
26+
2627
@Test
2728
public void acquireTokenClientCredentials_AsymmetricKeyCredential() throws Exception{
2829
String clientId = "55e7e5af-ca53-482d-9aa3-5cb1cc8eecb5";
@@ -35,7 +36,24 @@ public void acquireTokenClientCredentials_ClientSecret() throws Exception{
3536
AppIdentityProvider appProvider = new AppIdentityProvider();
3637
final String clientId = appProvider.getDefaultLabId();
3738
final String password = appProvider.getDefaultLabPassword();
38-
IClientCredential credential = ClientCredentialFactory.create(password);
39+
IClientCredential credential = ClientCredentialFactory.createFromSecret(password);
40+
41+
assertAcquireTokenCommon(clientId, credential);
42+
}
43+
44+
@Test
45+
public void acquireTokenClientCredentials_ClientAssertion() throws Exception{
46+
String clientId = "55e7e5af-ca53-482d-9aa3-5cb1cc8eecb5";
47+
IClientCredential certificateFromKeyStore = getCertificateFromKeyStore();
48+
49+
ClientAssertion clientAssertion = JwtHelper.buildJwt(
50+
clientId,
51+
(AsymmetricKeyCredential) certificateFromKeyStore,
52+
"https://login.microsoftonline.com/common/oauth2/v2.0/token");
53+
54+
55+
IClientCredential credential = ClientCredentialFactory.createFromClientAssertion(
56+
clientAssertion.assertion());
3957

4058
assertAcquireTokenCommon(clientId, credential);
4159
}
@@ -53,23 +71,6 @@ private void assertAcquireTokenCommon(String clientId, IClientCredential credent
5371

5472
Assert.assertNotNull(result);
5573
Assert.assertNotNull(result.accessToken());
56-
57-
String cachedAt = result.accessToken();
58-
59-
result = cca.acquireTokenSilently(SilentParameters
60-
.builder(Collections.singleton(GRAPH_DEFAULT_SCOPE))
61-
.build())
62-
.get();
63-
64-
Assert.assertNull(result);
65-
66-
result = cca.acquireTokenSilently(SilentParameters
67-
.builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE))
68-
.build())
69-
.get();
70-
71-
Assert.assertNotNull(result);
72-
Assert.assertEquals(result.accessToken(), cachedAt);
7374
}
7475

7576

@@ -84,6 +85,6 @@ private IClientCredential getCertificateFromKeyStore() throws
8485
X509Certificate publicCertificate = (X509Certificate)keystore.getCertificate(
8586
certificateAlias);
8687

87-
return ClientCredentialFactory.create(key, publicCertificate);
88+
return ClientCredentialFactory.createFromCertificate(key, publicCertificate);
8889
}
8990
}

src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void acquireTokenWithOBO_Managed() throws Exception {
5050
final String password = appProvider.getOboPassword();
5151

5252
ConfidentialClientApplication cca =
53-
ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.create(password)).
53+
ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)).
5454
authority(msidlab4Authority).
5555
build();
5656

src/integrationtest/java/labapi/KeyVaultSecretsProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,6 @@ private IClientCredential getClientCredentialFromKeyStore() {
7878
} catch (Exception e){
7979
throw new RuntimeException("Error getting certificate from keystore: " + e.getMessage());
8080
}
81-
return ClientCredentialFactory.create(key, publicCertificate);
81+
return ClientCredentialFactory.createFromCertificate(key, publicCertificate);
8282
}
8383
}

src/main/java/com/microsoft/aad/msal4j/AsymmetricKeyCredential.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
*/
3030
public final class AsymmetricKeyCredential implements IClientCredential{
3131

32-
public final static int MIN_KEY_SIZE_IN_BITS = 2048;
32+
private final static int MIN_KEY_SIZE_IN_BITS = 2048;
3333

3434
/**
3535
* Returns private key of the credential.
@@ -93,23 +93,22 @@ else if("sun.security.mscapi.RSAPrivateKey".equals(key.getClass().getName())){
9393
* @throws CertificateEncodingException if an encoding error occurs
9494
* @throws NoSuchAlgorithmException if requested algorithm is not available in the environment
9595
*/
96-
public String getPublicCertificateHash()
96+
public String publicCertificateHash()
9797
throws CertificateEncodingException, NoSuchAlgorithmException {
9898
return Base64.encodeBase64String(AsymmetricKeyCredential
9999
.getHash(this.publicCertificate.getEncoded()));
100100
}
101101

102102
/**
103103
* Base64 encoded public certificate.
104-
*
104+
*
105105
* @return base64 encoded string
106106
* @throws CertificateEncodingException if an encoding error occurs
107107
*/
108108
public String publicCertificate() throws CertificateEncodingException {
109109
return Base64.encodeBase64String(this.publicCertificate.getEncoded());
110110
}
111111

112-
113112
/**
114113
* Static method to create KeyCredential instance.
115114
*
@@ -126,7 +125,7 @@ public String publicCertificate() throws CertificateEncodingException {
126125
* @throws IOException {@link IOException}
127126
* @throws UnrecoverableKeyException {@link UnrecoverableKeyException}
128127
*/
129-
public static AsymmetricKeyCredential create(final InputStream pkcs12Certificate, final String password)
128+
static AsymmetricKeyCredential create(final InputStream pkcs12Certificate, final String password)
130129
throws KeyStoreException, NoSuchProviderException,
131130
NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {
132131
final KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE");
@@ -149,7 +148,7 @@ public static AsymmetricKeyCredential create(final InputStream pkcs12Certificate
149148
* Public certificate used for thumb print.
150149
* @return KeyCredential instance
151150
*/
152-
public static AsymmetricKeyCredential create(final PrivateKey key, final X509Certificate publicCertificate) {
151+
static AsymmetricKeyCredential create(final PrivateKey key, final X509Certificate publicCertificate) {
153152
return new AsymmetricKeyCredential(key, publicCertificate);
154153
}
155154

src/main/java/com/microsoft/aad/msal4j/ClientAssertion.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
@Accessors(fluent = true)
1616
@Getter
1717
@EqualsAndHashCode
18-
public final class ClientAssertion {
18+
public final class ClientAssertion implements IClientCredential{
1919

2020
public static final String assertionType = JWTAuthentication.CLIENT_ASSERTION_TYPE;
2121
private final String assertion;
@@ -26,7 +26,7 @@ public final class ClientAssertion {
2626
*
2727
* @param assertion The jwt used as credential.
2828
*/
29-
public ClientAssertion(final String assertion) {
29+
ClientAssertion(final String assertion) {
3030
if (StringHelper.isBlank(assertion)) {
3131
throw new NullPointerException("assertion");
3232
}

src/main/java/com/microsoft/aad/msal4j/ClientCredentialFactory.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,48 @@
1515
public class ClientCredentialFactory {
1616

1717
/**
18-
*
18+
* Static method to create a {@link ClientSecret} instance from a client secret
1919
* @param secret secret of application requesting a token
2020
* @return {@link ClientSecret}
2121
*/
22-
public static IClientCredential create(String secret){
22+
public static IClientCredential createFromSecret(String secret){
2323
return new ClientSecret(secret);
2424
}
2525

2626
/**
27-
*
27+
* Static method to create a {@link AsymmetricKeyCredential} instance from a certificate
2828
* @param pkcs12Certificate InputStream containing PCKS12 formatted certificate
2929
* @param password certificate password
30-
* @return {@link IClientCredential}
30+
* @return {@link AsymmetricKeyCredential}
3131
* @throws CertificateException
3232
* @throws UnrecoverableKeyException
3333
* @throws NoSuchAlgorithmException
3434
* @throws KeyStoreException
3535
* @throws NoSuchProviderException
3636
* @throws IOException
3737
*/
38-
public static IClientCredential create
39-
(final InputStream pkcs12Certificate, final String password)
38+
public static IClientCredential createFromCertificate(final InputStream pkcs12Certificate, final String password)
4039
throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException,
4140
KeyStoreException, NoSuchProviderException, IOException {
4241
return AsymmetricKeyCredential.create(pkcs12Certificate, password);
4342
}
4443

4544
/**
46-
*
45+
* Static method to create a {@link AsymmetricKeyCredential} instance.
4746
* @param key RSA private key to sign the assertion.
4847
* @param publicCertificate x509 public certificate used for thumbprint
49-
* @return {@link IClientCredential}
48+
* @return {@link AsymmetricKeyCredential}
5049
*/
51-
public static IClientCredential create
52-
(final PrivateKey key, final X509Certificate publicCertificate) {
50+
public static IClientCredential createFromCertificate(final PrivateKey key, final X509Certificate publicCertificate) {
5351
return AsymmetricKeyCredential.create(key, publicCertificate);
5452
}
53+
54+
/**
55+
* Static method to create a {@link ClientAssertion} instance.
56+
* @param clientAssertion Jwt token encoded as a base64 URL encoded string
57+
* @return {@link ClientAssertion}
58+
*/
59+
public static IClientCredential createFromClientAssertion(String clientAssertion){
60+
return new ClientAssertion(clientAssertion);
61+
}
5562
}

src/main/java/com/microsoft/aad/msal4j/ClientSecret.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public final class ClientSecret implements IClientCredential {
2525
* @param clientSecret
2626
* Secret of the client requesting the token.
2727
*/
28-
public ClientSecret(final String clientSecret) {
28+
ClientSecret(final String clientSecret) {
2929
if (StringHelper.isBlank(clientSecret)) {
3030
throw new IllegalArgumentException("clientSecret is null or empty");
3131
}

src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ private void initClientAuthentication(IClientCredential clientCredential) {
7474
this.authenticationAuthority.selfSignedJwtAudience());
7575

7676
clientAuthentication = createClientAuthFromClientAssertion(clientAssertion);
77+
} else if (clientCredential instanceof ClientAssertion){
78+
clientAuthentication = createClientAuthFromClientAssertion((ClientAssertion) clientCredential);
7779
} else {
7880
throw new IllegalArgumentException("Unsupported client credential");
7981
}

0 commit comments

Comments
 (0)